How the API executes a prompt
- Updated
- Reading time
- 4 min
- Level
- beginner
On this page
- The path
- 1. Accepted means durable
- 2. The device check comes before the dispatch
- 3. Agent selection is a filter, then a strategy
- 4. The prompt is typed, and generation is waited for
- 5. Timeouts and retries protect the job, not the connection
- 6. The result comes back with its history
- What this means for your integration
Your request is written to a durable queue, dispatched to an idle browser agent on one of your own paired machines, typed into a provider's web interface, and read back when the answer finishes generating. Every design decision below follows from one fact: the execution environment is a real browser on a computer you own, which is slower and less reliable than a data centre, and is sometimes asleep.
Understanding this is what stops you designing an integration that fights the system.
The path
your application
→ POST /v1/completions
→ your queue durable; survives restarts and offline devices
→ is one of your devices online?
→ Revoye Desk on your machine authenticated socket, signed with your device key
→ the Revoye extension via a local browser bridge
→ an idle agent one browser tab, one provider
→ the provider's web UI the prompt is typed in
→ the agent reads the finished answer
→ back the same way to your waiting request, or to your callback_url1. Accepted means durable
Revoye writes the job down before it does anything else. From that moment:
- A dropped connection does not lose it. Fetch it at
GET /v1/completions/{id}. - A Revoye deploy does not lose it.
- Your machine being offline does not lose it.
This is the single most useful property to design around. Your integration's job is to record the id, not to hold a connection.
2. The device check comes before the dispatch
Nothing is dispatched until the router knows one of your devices is connected. If none is:
| Your request said | What happens |
|---|---|
wait: true | The request waits. The job queues, and runs when a device returns |
wait: false with a callback_url | Accepted and queued. The webhook fires when it runs |
wait: false, no callback_url | 503 NO_DEVICE_ONLINE immediately — there would be nobody to tell |
For anything unattended, the second row is the one you want.
3. Agent selection is a filter, then a strategy
A candidate agent must clear all of:
- enabled in your dashboard, and enabled in your extension
- currently idle
- its provider is enabled
- its device is connected
- it has not already failed this job
- under its own hourly limit, its provider's hourly limit, and your global hourly limit
- permitted by your time-window policy, if you use one
- matching the
provideryou asked for, if you asked for one
"Idle" includes work you started yourself. The same agents serve the extension's multi-agent chat, so an agent mid-way through a chat prompt is not idle and the router skips it rather than interrupting it. If your API traffic and your own chatting compete, the fix is more agents — or switching individual ones off for one use or the other.
Whatever survives, your rotation strategy chooses from: round robin, least recently used, priority, or time window. You set the strategy in the dashboard; rate limits and capacity covers what happens when the filter empties.
4. The prompt is typed, and generation is waited for
The agent opens the provider's page, enters the prompt, and waits for generation to actually finish — not for a fixed timer. Then it reads the answer back out of the page.
This is why run_ms in the response is usually tens of seconds, and why it varies with prompt
length, provider load and the model the provider decided to use. It is also why there is no
streaming: there is no partial output to forward, only a page that is still changing.
5. Timeouts and retries protect the job, not the connection
Two separate clocks:
| Field | Default | Range | Covers | |
|---|---|---|---|---|
| Per attempt | timeout_ms | 180 000 | 5 000–600 000 | One dispatch to one agent |
| Whole job | deadline_ms | 900 000 | 10 000–3 600 000 | Every attempt together |
When an attempt times out, the job is taken off that agent and offered to the next eligible one — never back to one that already failed it. When the deadline passes, the job stops.
wait: true holds your HTTP connection for at most min(deadline_ms, 600000). If that expires you
get 504 JOB_TIMEOUT with the job id, and the job keeps running. The timeout is your patience,
not the job's.
6. The result comes back with its history
Every completed job tells you what happened, not just what it produced: provider, agent_id,
attempts, queue_ms (how long it waited before the first dispatch, even after retries) and
run_ms. When something is slow, those four fields say whether it was your queue, your fleet, or the
provider.
What this means for your integration
- Store the job id. It is the only thing you cannot recover.
- Set a long client timeout, or don't wait at all. A 30-second default will abandon healthy work.
- Use
Idempotency-Key. Retrying safely. - Treat
NO_DEVICE_ONLINEandNO_AGENT_AVAILABLEas capacity, not failure. Back off and retry. - Run more agents for more throughput. One agent is one prompt at a time.