Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Remote API

The remote API lets an agent that cannot join your network talk to a chat running on one of your machines. The motivating case is a cloud agent session: it can reach the public hub over HTTPS, but not your VPN, so it cannot reach an aya-helper directly. Your helpers already connect out to the hub, so the hub is the meeting point. The agent sends a message to a chat, and reads the reply back, through /api/remote/v1.

This is a separate surface from the browser UI, the worker protocol and the service integrations. It has its own audience, its own two scopes, and its own limits. The authoritative wire contract is docs/api/remote-api.md and docs/api/openapi.json (tag remote); this page is the integrator’s walkthrough.

Examples use hub.example.test for the hub and issuer.example.test for its identity provider.

What a caller can do

  • List the machines (a machine is the group of roots one helper serves), their roots and whether each is online.
  • List projects, sessions and messages, and search.
  • Send a message to an existing session, or start a new chat in a root.
  • Read the reply by polling a turn’s events.

What it cannot do, by design:

  • Ask for more than allow-edits. The default is read-only; full-access, bypassPermissions, auto, plan and provider-native modes are refused.
  • Choose a model tier, or see a local file path.
  • Read a turn someone else queued.

Everything it sends is stamped, so the chat’s agent can tell it is talking to an external agent and not to you.

Get a token

HUB=https://hub.example.test
AUTH=$(curl -s "$HUB/api/remote/v1/auth-info")
ISSUER=$(echo "$AUTH" | jq -r .issuer)
RESOURCE=$(echo "$AUTH" | jq -r .resource)
TOKEN_ENDPOINT=$(curl -s "$ISSUER/.well-known/openid-configuration" | jq -r .token_endpoint)

TOKEN=$(curl -s -X POST "$TOKEN_ENDPOINT" \
  -d grant_type=client_credentials \
  -d client_id="$CLIENT_ID" \
  -d client_secret="$CLIENT_SECRET" \
  -d resource="$RESOURCE" \
  -d scope="chat:read chat:drive" | jq -r .access_token)

Your operator registers the client in your identity provider and allows it to request this resource and these scopes. See Authentication for the scope table and the error bodies.

Send a message and read the reply

# Find a session (or machines/roots to start a new chat in).
curl -s -H "Authorization: Bearer $TOKEN" "$HUB/api/remote/v1/sessions?limit=5"

# Send. Reuse the same request_id to retry safely.
TURN=$(curl -s -X POST "$HUB/api/remote/v1/sessions/7/messages" \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"request_id":"send-001","text":"Summarise the failing test."}' | jq -r .turn_id)

# Poll until done; each call holds up to 20 s waiting for something new.
SINCE=-1
while :; do
  R=$(curl -s -H "Authorization: Bearer $TOKEN" \
    "$HUB/api/remote/v1/turns/$TURN/events?since=$SINCE&wait_ms=20000")
  echo "$R" | jq -c '.events[]'
  SINCE=$(echo "$R" | jq -r .next_since)
  [ "$(echo "$R" | jq -r .done)" = true ] && break
done

The repository ships a wrapper that does the token dance and the loop for you: scripts/aya-remote.sh (machines, sessions, read, send, new, wait), configured by AYA_HUB_URL, AYA_HUB_CLIENT_ID and AYA_HUB_CLIENT_SECRET.

Things to know

  • Retries are safe. request_id is required on both writes. Repeating it with the same input returns the same turn (replayed: true); different input is a 409. It is scoped to your client, so choose any unique string.
  • Turns can wait. If no helper has polled the chat’s root recently the response carries a warning. The turn is still queued, but a queued turn expires after ten minutes unclaimed, so check /machines for online.
  • Limits. Text is at most 64 KiB, a request body at most 256 KiB, and you may have REMOTE_MAX_ACTIVE_TURNS (default 8) turns waiting or running; past that you get 429 with Retry-After.
  • Re-claims. If a turn’s claim_generation changes between polls, a helper took it over; discard what you showed and read again from since=-1.
  • No ownership. The hub does not track who owns a session; a caller acts as the operator across everything the hub indexes. Treat a token for this resource as a credential to the operator’s chats, and grant it accordingly.

Run your own

The operator turns the surface on with REMOTE_API_RESOURCE and tunes it with REMOTE_ALLOWED_SUBJECTS (required), REMOTE_MACHINE_ALIASES_JSON and REMOTE_MAX_ACTIVE_TURNS; submits and reads are rate limited per subject (429 with Retry-After). See Run your own.