Workspace API guide
The HTTP endpoints that connect a hook, a gateway or your own script to your Scopebond workspace. Most teams never call them, because the hook and gateway commands do it for you. Use this page to build an integration or to see exactly what crosses the network.
Examples use https://cloud.scopebond.com. All bodies are JSON.
Who can call what
| Caller | Signs in with | Can |
|---|---|---|
| A person | A browser session in the workspace | Whatever their role allows |
| A connected hook or gateway | A machine credential: Authorization: Bearer <credential> | Send receipts and report that it is alive. Nothing else |
A machine credential belongs to one hook or gateway in one environment. The hook keeps it in .scopebond/cloud.json; never commit that file. Disconnecting the agent in the workspace revokes it.
Endpoints
| Endpoint | Needs | Purpose |
|---|---|---|
POST /v1/device/code | Nothing | Start a login. Returns a code to show the person and a secret device_code |
POST /v1/device/token | The device_code | Poll until someone approves the code at /app/device. Returns the enrollment once |
POST /v1/enroll | A single-use enrollment | Exchange it for a machine credential, proving the machine holds its signing keys. The answer also names ingest_url, the address that machine sends its records to |
POST /v1/ingest | Machine credential | Send receipts |
POST /v1/heartbeat | Machine credential | Report that a gateway is alive |
POST /verify | Nothing | Check a receipt's signatures. Changes nothing |
Log in without pasting
hook login posts client_name and harness (such as "claude") to /v1/device/code, shows a code such as BCDF-GHJK, then polls /v1/device/token at the returned interval. Until approval the poll answers authorization_pending, slow_down, access_denied or expired_token. Codes expire after 10 minutes. The hook then completes /v1/enroll.
An enrollment copied from the workspace is single use, expires after about 15 minutes, and is useless without the machine's keys.
Send receipts
{ "receipts": [ { "payload": { "...": "..." }, "signature": { "...": "..." } } ] }
Up to 100 receipts and 1 MiB per request, each signed by the enrolled keys. A repeat is counted once. A success returns ingested, duplicates and this month's usage. A record that fails validation is refused on its own and listed in rejected with its index, action id and code; the rest of the batch is stored.
| Status | Meaning | Action |
|---|---|---|
| 200 | Stored; a record refused on its own is listed in rejected (invalid_receipt, id_conflict) and the rest is stored | None for the stored records; a rejected record stays on the computer |
| 400 | Malformed body, or a record with a timestamp ahead of the workspace clock | Fix the request; a timestamp ahead is accepted once the time passes |
| 401 / 403 | Missing, revoked or wrong credential | Reconnect the agent |
| 409 | The records are signed by a key this connection did not enroll, or it is briefly unavailable (attester_unavailable) | Sign the computer in again; its records stay queued |
| 413 | Over 100 receipts or 1 MiB | Send smaller batches |
| 429 | Monthly limit or rate limit reached | None; receipts wait locally and retry |
| 503 | Stored; activity view catching up | None |
Every refusal carries error, a machine-readable code (credential_refused, machine_credential_required, bad_request, invalid_receipt, batch_too_large, attester_unavailable, id_conflict, quota, projection_pending) and a plain remediation.
A failed upload never loses a record and never blocks your agent.
Verify a receipt
{ "receipt": { "payload": { "...": "..." }, "signature": { "...": "..." } }, "public_key_pem": "-----BEGIN PUBLIC KEY-----..." }
public_key_pem is the public key of the hook or gateway that signed the receipt. Leave it out if that gateway is enrolled in a workspace, and the key is looked up for you. The response says whether each signature is valid and where the key came from. See Receipts and verification.
Limits: requests are rate-limited per address, and a 429 carries Retry-After. If a credential leaks, disconnect the agent in the workspace and connect it again. No endpoint here accepts prices, plan changes or personal data.