API / watch-pending-pool-requests-via-api

API

Watch pending pool requests via the Cloud Agents API

Stream pending-request lifecycle events over Server-Sent Events with GET https://api.cursor.com/v0/private-workers/pending-requests/stream. Official reference: Cloud Agents API → Watch Pending Pool Requests (Workers and Pools).

The Cloud Agents API v1 is in public beta. Paths keep the older private-workers name; they refer to the same Self-Hosted Machines workers. Authenticate with a service account API key via Basic auth (-u "$CURSOR_API_KEY:") or Authorization: Bearer. Other API key types are rejected. Availability: Beta (all plans) per the API overview.

Controllers list-then-watch: call List Pending Pool Requests (GET /v0/private-workers/pending-requests) first, keep the response's streamCursor, then open this watch from that exact position. Use the same repository and pool filters for the list and the watch; cursors are bound to the filters that issued them.

Request

Query parameters:

Param Default Meaning
cursor Required. The streamCursor from a list response, or the SSE id: of the last event you processed. On reconnect, a native EventSource resends that id as the Last-Event-ID header, which takes precedence over the query parameter.
repository Same semantics as List Pending Pool Requests. Required for repo-scoped service account API keys. Pagination parameters are not accepted on the stream.
pool Watch only events for this pool. Exact, case-sensitive match against the request's pool label. Must match the filter used by the list that issued the cursor. Omit to watch every pool on the team.
curl --request GET --no-buffer \
  --url "https://api.cursor.com/v0/private-workers/pending-requests/stream?cursor=$STREAM_CURSOR" \
  --header 'Accept: text/event-stream' \
  -u "$CURSOR_API_KEY:"

Events

The watch replays the retained transitions after the cursor, then follows live. Every event's SSE id: is the cursor to resume from if the connection drops.

Event Payload Meaning
created Full request object (same shape as List Pending Pool Requests) A request entered the queue, including a claimed-but-offline request whose reconnect window lapsed and whose claim expired.
claimed { id } A worker claimed the request, or an offline worker reconnected and resumed its claimed request.
claimed_offline Full request object, including claimedWorkerId and wakeTimeoutMs A follow-up arrived for a request whose claimed worker is offline. Revive the machine before the window lapses, or the claim expires and the request is re-advertised with a fresh created event.
expired { id } The request left the queue without being claimed.
heartbeat {} Cursor checkpoint with no state change, sent about every 20 seconds on a quiet stream. Heartbeats advance an idle watch's resume position but do not extend the cursor's lifetime.

Example stream:

: connected

event: heartbeat
id: djQuY3Vyc29yLWNoZWNrcG9pbnQ
data: {}

event: created
id: djQuY3Vyc29yLWFmdGVyLWNyZWF0ZWQ
data: {"id":"bc-00000000-0000-0000-0000-000000000002","userId":321,"userEmail":"owner@acme.example","repoOwner":"acme","repoName":"payments-service","repoUrl":"https://github.com/acme/payments-service","labels":[{"key":"pool","value":"gpu"}],"createdAtMs":1737306880000}

event: claimed
id: djQuY3Vyc29yLWFmdGVyLWNsYWltZWQ
data: {"id":"bc-00000000-0000-0000-0000-000000000002"}

Cursor lifetime

Every cursor in a watch chain expires five minutes after the list that issued it. Heartbeats and reconnects do not extend it. When the cursor expires, or the retained event window no longer covers it, the endpoint returns HTTP 410 Gone with {"code": "cursor_expired"}: re-list and watch from the fresh streamCursor. Re-list proactively on a five-minute timer with jitter instead of riding the 410, so a fleet of controllers does not synchronize its list calls.

Delivery guarantees

Delivery is best-effort; the list is the source of truth. Events are published after each transition commits, with retries, but a rare failure can drop one, and a dropped event is never redelivered. Between re-lists, treat events as low-latency hints: apply them idempotently (upsert created and claimed_offline requests, remove claimed and expired requests by id) and let the next list correct any drift. A claimed event for a request you never saw is a no-op. Claims stay atomic server-side regardless of your local view.

Do not persist cursors. A service account can hold at most four concurrent streams; use one stream per controller and fan out locally.

Controller loop

  1. List pending requests to completion and replace your local view with the result. Keep the response's streamCursor.
  2. Open the watch with ?cursor=<streamCursor> and apply events to your local view. Track the latest event id: you processed.
  3. On disconnect, reconnect with the latest event id as ?cursor=, or rely on a native EventSource, which resends it as Last-Event-ID automatically.
  4. On HTTP 410 Gone, go back to step 1 and re-list.

Pair this stream with Claim A Pending Request (POST /v0/private-workers/claim) when your controller assigns work to a worker, and with Release A Claim (POST /v0/private-workers/claims/{id}/release) when you drop a routing binding.