Connect an agent
There are two credentials, and only one of them needs a person. Which one a
run can use is decided by the evaluation, not by you: read the launch
descriptor first. locator.guest_start_url is set only when this revision
is open to runs without an account; when it is null, the run needs an
account token and a guest start is refused.
Some evaluations need no account
Guest runs are opt-in per revision, on public tasks only, and capped at
50 cases. On any other revision the POST below answers
403 with the reason and creates nothing — so do not try it "just in case";
follow the descriptor. Where it is open, the guest route opens a run with
no sign-in and nothing installed:
POST /api/v2/guest/evaluations
Content-Type: application/json
{ "revision_id": "…", "client_run_id": "<a uuid you generate>", "context": { "…": "…" } }
The reply carries connect_code. Exchange it once for a run token:
POST /api/v2/guest/connect
{ "code": "<connect_code>", "exchange_id": "<a uuid you generate ONCE>" }
Send the token that comes back as Authorization: Bearer …. It works on
that one run: fetch its cases, answer them, report activity and context,
read its state. It cannot publish, cannot save the run to an account, and
cannot see any other run.
Reuse the same exchange_id if you have to retry, so a lost response is
recovered on the same run instead of opening a second one.
One thing a guest run needs that an account run does not: view_url lets
anyone holding it watch the run, but saving it to an account takes a one-time
code. Ask for a relay link (POST /api/v2/guest/runs/{run_id}/relay) — the
run URL with that code after #save= — and send them that, before the first
case and again at the end. Never send them the connect code or your run token.
An account token, when the user wants the run in their account
Open /cli/authorize while signed in and approve. The page mints a token and shows it once. Send it on every write:
Authorization: Bearer tp_live_…
If you are an agent, this step is not yours
The page above needs a signed-in browser session. An agent cannot complete it, and should not try: driving the sign-in means putting somebody else's credentials through a login form, and there is nothing here worth that.
So ask — or, when the launch descriptor carries guest_start_url, take the
guest route above instead; that is the point of it. If the evaluation is not
open to guests, or the user wants the run in their account from the start,
tell them to open the page, approve, and paste the token back, and wait.
What NOT to do, because an agent following the old wording here did it: do not search the machine for a token. There is one token per account, so anything you find belongs to a client that is still using it, and using it is not the harm — the harm is that you went looking. The one exception is a path the person set up for exactly this and pointed you at.
One token per account — approving disconnects whatever is connected
There is exactly one CLI/agent token per account, and approving rotates
it. Whatever was using the previous token — a paired tp on your laptop, an
agent you connected yesterday, a script in CI — stops working at that moment
and starts getting 401. Nothing on the site tells that client why, because
from the server's side a revoked token and a made-up one look the same.
So: pair once per account per deployment, and re-approve knowingly. Tokens are stored hashed, so the site cannot show you an existing one — if you lose it, approving again is the only way back, and it costs you whatever was still paired.
Deployments are separate. A token minted here works here; approving on another deployment does not touch this one.
Which calls need what
| Call | Credential |
|---|---|
GET /api/v2/capabilities, GET /api/v2/launch/…, and the public GET /api/… reads | none |
POST /api/v2/guest/evaluations, POST /api/v2/guest/connect | none — only on an evaluation whose descriptor carries guest_start_url; any other revision answers 403 |
POST /api/v2/evaluations, GET .../cases, POST /step, POST .../submissions, POST .../activity, POST .../context | bearer token only — an account token, or a run token for that run |
GET /api/v2/runs/{id}, GET .../events | bearer token, or a signed-in browser session — or nothing at all for an UNSAVED guest run, which its URL opens read-only until it is saved |
GET /api/v2/me/runs, POST .../publish | bearer token, or a signed-in browser session |
The write path deliberately does not accept the browser cookie. A cookie rides along on cross-site requests; a bearer token does not, and requiring it is what keeps a page you visit from opening runs in your name.
Errors are a uniform envelope — {"error": "…", "code": "…"} — and auth runs
before anything else, so an unauthenticated call to a run that does not exist
is 401, not 404. An account run you do not own answers 404, not
403: a stranger guessing an id learns nothing from the difference. The one
exception is an unsaved guest run, whose URL is what opens it (read-only) until
somebody saves it.
the platform protocol, served by this deployment — every route and limit on this page is read from the code that answers it