Uploading guides
A guide is a captured walkthrough: one screenshot and some metadata per step, optionally narrated. Like video uploads, the file never passes through us. You ask for presigned storage URLs, PUT each screenshot directly, then send the step metadata.
Uploading needs a token granted the content:write scope, which is an OAuth
path today.
Two outcomes, decided for you
Section titled “Two outcomes, decided for you”A guide either publishes straight away or goes to a reviewer, depending on whether the person behind the token may publish guides to everyone in the organization. Someone who can publishes. Anyone else who may contribute, such as a team admin, goes to a reviewer.
You do not choose. The server decides from the caller’s permission and tells
you which happened, in the mode field on both responses. This is deliberate: a
client cannot reliably know what its user is allowed to do, and any answer it
hardcodes goes stale the first time an organization adds a role.
mode |
What it means |
|---|---|
published |
The guide was created directly, without going to review. On its own that leaves a draft, for someone to review and publish in the editor. Send publish: true when you finalize and it is in the library once processing finishes. |
review |
The guide is waiting on a reviewer, and is not visible to learners yet. |
Show the difference to whoever submitted it. “Your guide is live” and “your guide is with a reviewer” are not the same message.
Start the upload
Section titled “Start the upload”POST /v1/guides/upload-urls
Creates the guide and returns one presigned URL per screenshot.
{ "title": "Filing an expense", "screenshotCount": 3, "includeAudio": false}{ "id": "…", "mode": "review", "screenshotUrls": [ { "sequence": 0, "uploadUrl": "https://…", "key": "…" } ], "audio": { "uploadUrl": "https://…", "key": "…" }}Treat id as opaque and hand it straight back when finalizing. It is a guide id
when publishing and a submission id when going to review, and that difference is
not yours to interpret.
Updating a guide you made before
Section titled “Updating a guide you made before”Add a sourceKey to upload-urls: any string you choose that names what the
guide was generated from, up to 200 characters. Send the same one again and the
upload replaces that guide instead of creating a second. Send a different one
and you get a new guide. We do not interpret it, so my-repo#invoice-tour works
as well as anything.
{ "title": "Filing an invoice", "screenshotCount": 3, "includeAudio": true, "sourceKey": "my-repo#invoice-tour"}Send the same sourceKey to finalize as well. The response says what
happened:
updatesExisting: truemeans an existing guide is being replaced, so you can say “updated” and not “created”.idis that guide’s id, and the screenshot and audio URLs point at fresh storage names, so the version people are watching is never overwritten while you upload.- The guide keeps its title. Someone may have renamed it, and the title you send is used only when the guide is created.
Replacing a guide replaces all of it, including step text that someone edited in the app since.
It is unique per organization, so two uploads racing to create the same
sourceKey produce one guide. The second finds the first and carries on with it.
When you cannot publish, the upload is a proposal for an administrator, and the rules differ:
- Your first upload under a key files a proposal, and the key belongs to it.
- Uploading again while it is still pending replaces that proposal with a fresh
one under the same key. The response says
replacedPending: true. The earlier proposal is only withdrawn once the new one exists, and if an administrator decides it in the meantime, their decision stands and the upload is answered as it would be after that decision. Send"stack": trueinstead to file another beside it, which carries no key. - If an administrator rejects a first proposal, the key is released, so the source can be uploaded again as something new.
- If a guide already exists under the key, uploading over it proposes an update
to it. The response says
updatesExisting: truewithmode: "review", and the published guide stays exactly as it is until an administrator approves the update, which then replaces it. Uploading again while your update is pending replaces your proposal, as for a first upload.
A 409 on a sourceKey means the state of that guide stands in the way, and
the code tells you which kind of wait it is:
| Code | Meaning |
|---|---|
SOURCE_KEY_BUSY |
The guide is still being processed, or another upload for it is in progress. Try again in a few minutes. An upload that was started and abandoned holds the key for up to four hours. |
SOURCE_KEY_AWAITING_REVIEW |
A proposal for this source, or a proposed update to it, is waiting in the review queue. It has to be approved or rejected first. |
SOURCE_KEY_UPDATE_NEEDS_ADMIN |
The guide exists and you cannot see it, so you cannot propose an update to it. Someone who can see it can upload it. |
Finish the upload
Section titled “Finish the upload”POST /v1/guides/finalize
Call this once every screenshot has been PUT successfully.
{ "id": "…", "steps": [ { "sequence": 0, "actionType": "click", "targetInfo": { "selector": "#submit" }, "pageUrl": "https://example.com/expenses", "screenshotKey": "…", "defaultText": "Click Submit" } ], "audioKey": "…"}Each step’s screenshotKey is the key returned for the matching sequence,
and must be one this guide’s upload-urls issued: a key belonging to another
guide is refused with 400. targetInfo takes flat string values only.
submitterNote is also accepted here. It is attached to the review request, so
it reaches a reviewer only when mode comes back as review, and is ignored
otherwise because a published guide has nobody to read it. It belongs on this
call rather than on upload-urls because the review request does not exist
until now.
{ "id": "…", "title": "Filing an expense", "mode": "review" }Publishing straight away
Section titled “Publishing straight away”Add "publish": true to the finalize body and a guide that was created directly
publishes as soon as it is finalized, instead of waiting as a draft for someone
to publish it in the editor. It is for a client that already has the exact text
of every step, such as a tool that generated the narration itself, so:
- Audio is not transcribed. The transcript would replace the
defaultTextyou sent, and it would arrive after the guide had already been published. - Publishing an update asks nobody who already learned the guide to retrain,
unless you say otherwise with
retrainingIntent(below), so it is safe to send again when the walkthrough changes.
publish is ignored when the guide goes to review, because a guide waiting on a
reviewer is not yours to publish. Leave it out and you get the draft, which is
what a tool that opens the editor afterwards wants.
Narration you generated
Section titled “Narration you generated”If your audio was synthesized from the steps’ own text, add
"scriptedNarration": true to the finalize body. The audio is then not
transcribed, and the defaultText you sent stands. Without it, audio is
transcribed and the transcript replaces each step’s text, which is right for a
recording of someone speaking and wrong for narration made from the text.
Unlike publish, it is honored when the guide goes to review, since it
describes the upload rather than asking to publish it. publish: true implies
it.
Retraining after an update
Section titled “Retraining after an update”When the upload updates a guide that already exists, add retrainingIntent to
the finalize body to say whether people who already learned it should be
retrained once the update is live:
| Value | What happens |
|---|---|
none |
Nobody is asked to retrain. The default. |
changed_material |
They get a short lesson on what changed. |
full_retake |
They take the original lesson again. |
An update that publishes now applies it at once. One that goes to review is offered to the reviewer as the starting choice, and the reviewer decides. It is ignored for a guide uploaded for the first time, since nobody has learned it yet.
The link
Section titled “The link”When the guide was created directly, the response carries a url to it in the
web app:
{ "id": "…", "title": "Filing an expense", "mode": "published", "url": "https://mereon.ai/guides/…/view" }With publish the page is useful once processing finishes, which takes a few
minutes, so say so if you show the link to someone straight away. Without it the
guide is a draft and the link is for people who may edit it. A guide going to
review has no url: it is not visible to learners yet.
There is no mode in the request. It is worked out again from your permission,
so the answer cannot be influenced by the client.
That means the answer can differ from the one upload-urls gave you, if the
caller’s permission changed in between. The guide is still finalized either way;
what changes is whether it also becomes a review submission. Read mode on this
response rather than the earlier one when telling someone where their guide
went.
What can go wrong
Section titled “What can go wrong”| Status | Meaning |
|---|---|
400 |
The body is invalid, or a step references a screenshotKey that belongs to another guide. |
403 |
The token lacks content:write. Note that lacking permission to publish is not an error: it routes to review instead. |
404 |
The id does not exist, or belongs to somewhere the caller cannot see. The two are deliberately indistinguishable. |