Skip to content

POST /v1/completions

Updated
Reading time
5 min
Level
beginner

The native endpoint. Everything Revoye can do is expressible here — pick a provider or let the router choose, wait for the answer or queue it, set your own timeouts, continue an existing conversation, and attach metadata that comes back untouched.

curl https://revoyeapi.degird.com/v1/completions \
  -H "Authorization: Bearer $REVOYE_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 4f1c9d2e-8b3a-4c1d-9e2f-7a6b5c4d3e2f" \
  -d '{
    "prompt": "Summarise the CAP theorem in three sentences.",
    "provider": "chatgpt",
    "wait": true
  }'

Request

FieldTypeDefaultNotes
promptstring, 1–100 000 charsrequiredThe text typed into the provider
providerchatgpt | claude | gemini | deepseek | qwen | perplexity | nullnullnull means any enabled provider, chosen by your rotation strategy
agent_idstringnullPin to one agent. NO_AGENT_AVAILABLE if it is not eligible
waitbooleantrueHold the HTTP request open until the job settles
timeout_ms5 000–600 000180 000Per attempt
deadline_ms10 000–3 600 000900 000For the whole job, across all attempts
priority−10…100Higher dispatches first within your own queue
modenew | continuenewcontinue requires conversation_ref
conversation_refstringnullOpaque; returned by a previous completion
callback_urlhttps URLnullWebhook on completion
metadataobject ≤ 4 KiB{}Echoed back verbatim. Revoye never reads it

Headers. Idempotency-Key is optional and strongly recommended — see retrying safely.

Choosing a provider, or not

Omitting provider is usually right: your rotation strategy spreads work across everything you have enabled, which is the point of running several accounts. Name a provider when the answer's quality depends on which model produces it.

Naming one is a constraint, not a preference. Revoye will not silently answer a chatgpt request from DeepSeek — receiving a different model than you asked for would be a correctness bug, not a helpful fallback.

metadata is yours

Whatever you put in comes back out, byte for byte, on the completion and on the webhook. Use it to correlate a job with your own records — a tenant id, a trace id, a row id — so that a webhook arriving an hour later can be routed without a database round trip.

Response — wait: true, success (200)

{
  "id": "job_01JAY7Q2K8XYZ",
  "status": "succeeded",
  "response": "The CAP theorem states that…",
  "provider": "chatgpt",
  "agent_id": "agt_01JAY7…",
  "agent_name": "Agent 2",
  "conversation_ref": "https://chatgpt.com/c/6f0a…",
  "attempts": 1,
  "queue_ms": 240,
  "run_ms": 18432,
  "created_at": "2026-08-18T09:14:02Z",
  "finished_at": "2026-08-18T09:14:21Z",
  "metadata": { "trace": "abc" }
}
FieldMeans
attemptsHow many agents this job was dispatched to. > 1 means something timed out or failed
queue_msHow long it waited before its first dispatch. Set once, so a retried job still reports its original wait
run_msTime spent actually executing
conversation_refAn opaque handle to the thread in the provider's own UI. Pass it back with mode: "continue"

Response — wait: false (202)

{ "id": "job_01JAY7Q2K8XYZ", "status": "queued", "created_at": "2026-08-18T09:14:02Z" }

Blocking semantics

wait: true is a long poll, not synchronous execution. The job is durable the moment it is accepted, so:

  • If your connection drops, the job keeps running.
  • The result is at GET /v1/completions/{id}, or delivered to your callback_url.
  • Nothing is lost by a client timeout.

The connection is held for at most min(deadline_ms, 600000). On expiry you get 504 JOB_TIMEOUT with the job id, and the job continues.

Set your HTTP client's timeout above 300 seconds, or do not use wait: true. The single commonest integration bug is a 30-second default abandoning healthy work — and it looks exactly like Revoye failing, which it is not.

GET /v1/completions/{id}

Returns the same object. Statuses: queued, dispatched, succeeded, failed, cancelled, expired. A non-terminal job returns 200 with its real status and, when queued, a queue_position.

Add ?wait=true to long-poll a job you created asynchronously.

curl "https://revoyeapi.degird.com/v1/completions/job_01JAY7Q2K8XYZ" \
  -H "Authorization: Bearer $REVOYE_KEY"

Requires completions:read.

DELETE /v1/completions/{id}

Cancels a job. Returns 200 with the job object.

  • A queued job is cancelled immediately.
  • A dispatched job is marked cancelled and a cancel is relayed to the agent best-effort — the browser may already be mid-generation, and the extension cannot always stop it.
  • Cancelling a job that has already finished is a no-op, not an error.

Limits

Prompt100 000 characters
Request body1 MiB
Queued jobs per account1 000
metadata4 KiB

Errors

CodeHTTPRetry?Cause
INVALID_REQUEST400Nodetails.field names the offender
UNAUTHORIZED401NoBad or revoked key
FORBIDDEN403NoKey lacks completions:write
NOT_FOUND404NoNo such job, or not yours
CONFLICT409NoIdempotency key reused with a different body
PAYLOAD_TOO_LARGE413NoPrompt or body over the limit
RATE_LIMITED429After Retry-AfterRevoye's ingress limit
NO_DEVICE_ONLINE503YesNothing paired is connected (only when wait: false)
NO_AGENT_AVAILABLE503YesConnected, but no agent is eligible right now
PROVIDER_RATE_LIMITED503YesYour own hourly cap
JOB_TIMEOUT504YesHeld as long as you asked; the job continues
JOB_FAILED502MaybeAttempts exhausted. details.attempts explains
JOB_CANCELLED499NoCancelled by you or from the dashboard

Full handling guidance: Errors.