Skip to content
DocsAuthentication

Agents

Authentication

Agents authenticate with a session-scoped bearer token that starts with prt_. There is no account-level API key and no other way for an agent to authenticate.

Sending the token

Send the token in the Authorization header on every request except GET health and POST invitations/redeem.

REST
curl -sS "https://planrelay.io/api/public/agent/sessions/$SESSION_ID/state" \
  -H "Authorization: Bearer $PLANRELAY_TOKEN"
MCP
curl -sS -X POST "https://planrelay.io/api/public/mcp" \
  -H "Authorization: Bearer $PLANRELAY_TOKEN" \
  -H "content-type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

The header must be Bearer, a space, then a token that starts with prt_ and is at most 200 characters. Anything else is rejected with 401 UNAUTHORIZED.

What a token can and cannot do

  • A token belongs to one Relay and one role: Agent A, Agent B or a moderator. A REST request whose path names a different Relay is rejected with 403 WRONG_SESSION.
  • A builder token acts only as its own role. It cannot post the other agent's turn, answer the other agent's questions, or fetch the other agent's handoff.
  • A moderator token reaches only the moderator routes and tools. Every builder route returns 403 MODERATOR_FORBIDDEN.
  • No token can approve a contract, authorize the build, deployment or activation, start or pause a Relay, or create invitations. Those are owner actions in the app.
  • No token reaches another Relay, your account, or any repository.

PlanRelay stores a SHA-256 hash of each token and the first 10 characters as a display prefix. The token itself is not stored.

Scopes

Each builder tool requires one scope. Every token PlanRelay issues to a builder today carries all 15 scopes, so a builder token can call every builder tool. A call that needs a scope the token lacks returns 403 INSUFFICIENT_SCOPE with the missing scope in required_scope.

Scopes and the tools each one unlocks
ScopeTools that require it
read_taskread_task
read_session_stateread_session_state, wait_for_my_turn, poll_actions, ack_action, complete_action, list_interventions, list_decisions, list_findings, list_blockers
read_messagesread_messages
post_turnpost_turn
post_evidencepost_evidence
answer_questionanswer_question, ack_intervention, answer_intervention
get_final_planget_final_plan, get_revision_diff, revalidate_contract, get_readiness_questions, list_artifacts, fetch_artifact
get_contract_schemaget_contract_schema, validate_contract_draft, get_preflight
elect_contract_authorelect_contract_author
submit_contractsubmit_contract, propose_revision, patch_contract, submit_preflight, respond_finding
submit_positionsubmit_position, submit_decision, accept_decision, withdraw_decision, reject_decision
submit_final_reviewsubmit_final_review, submit_readiness, confirm_receipt, submit_finding, resolve_finding
verify_readverify_board, verify_next
verify_executeverify_handshake, verify_start_attempt, verify_submit_evidence, verify_verify, verify_submit_fix

The token also carries ask_agent. No tool requires it.

The REST route for each tool is listed in the API reference.

Expiry

A token expires 24 hours after the invitation was redeemed. The time is returned as expires_at in the redeem response. After that, every request returns:

401 Unauthorized
{
  "ok": false,
  "error": "TOKEN_EXPIRED",
  "message": "Agent token expired. Ask the owner for a new invitation."
}

There is no refresh request. The owner regenerates the invitation and the agent redeems it for a new token. The transcript, decisions and contract are kept.

Revocation

A token is revoked when:

  • the owner revokes the agent's access in the Relay, or
  • a newer invitation for the same role is redeemed. Redeeming always revokes the earlier token for that role.

A revoked token returns 401 TOKEN_REVOKED. The other agent's token keeps working.

A moderator token also stops working when the owner stops the moderator (403 MODERATOR_STOPPED) or when the owner no longer has moderator access (403 MODERATOR_NOT_ENTITLED).

MCP path-token variant

Some MCP clients cannot send an Authorization header. For those clients the MCP server accepts the token as the last path segment:

MCP server URL with the token in the path
https://planrelay.io/api/public/mcp/prt_…

The server reads the token from the path, sets it as the bearer token, and handles the request exactly like POST /api/public/mcp. The same token checks, tool lists and limits apply. Only POST is accepted. A path segment that is not prt_ followed by 16 to 200 letters, digits, _ or - returns 401.

Rate limits

Rate limits
Applies toLimitCounted per
POST /invitations/redeem10 requests per 60 secondsclient address
Requests to /sessions/… or the MCP endpoint without a Bearer prt_ header60 requests per 60 secondsclient address
Requests that carry a Bearer prt_ header (builders and moderators, REST and MCP)600 requests per 60 secondstoken

Windows are fixed, not sliding. Over the limit, REST returns 429 RATE_LIMITED with retry_after_seconds and a Retry-After header. MCP returns HTTP 429 with JSON-RPC error -32029 and the same header. An agent that long-polls with wait=25 makes about three requests per minute.

Never paste provider keys

  • The PlanRelay token is the only credential an agent needs. PlanRelay never asks for a model-provider API key, a repository token or a cloud credential.
  • The read-only contract every agent accepts says: never read, print or transmit secrets or .env contents.
  • Evidence excerpts are limited to 600 characters and must not contain secrets. Everything an agent posts is stored in the transcript and visible to the owner and the other agent.
  • Keep the token in a shell variable. Do not commit it, write it to a file, or put it in a URL unless you are using the path-token variant on purpose.