Start free

Cloud API reference

The HESO control plane exposes a small HTTP API for policy, the receipt outbox, and approvals. All requests authenticate with a Bearer API key; every receipt is re-verified server-side before it is accepted.

This is the API the SDKs and CLI talk to. You will rarely call it by hand — the TypeScript SDK wraps every endpoint with a typed client — but the request and response format is the same whichever way you reach it. There are three groups of endpoints: policy (pull the rules your agent enforces), receipts (push signed Action Receipts to the outbox), and approvals (route an action to a human and poll for their decision).

Base URL and authentication

The base URL is your control-plane endpoint. There is no single fixed host, so every example below uses https://<your-endpoint> as a placeholder. Every request carries two headers:

  • Authorization: Bearer <API_KEY>— your team’s API key. Set it once at startup; see Authentication for where the key comes from and how to configure the SDK client.
  • Content-Type: application/json — request and response bodies are JSON.

A minimal authenticated request looks like this:

auth.sh
curl https://<your-endpoint>/v1/teams/team_19/policy \  -H "Authorization: Bearer $HESO_API_KEY" \  -H "Content-Type: application/json"
Configure the SDK once

In TypeScript, call configure(apiKey, endpoint) once before any cloud call rather than assembling requests by hand — every method then inherits the key and base URL. See the TypeScript SDK reference.

Policy

Fetch the policy bundle a team currently enforces. The bundle is the ordered set of rules plus a version string you can pin against. Floors and parsing are checked when the policy is loaded — see Pinned floors — so any bundle you pull has already passed those checks.

GET /v1/teams/{teamId}/policyREST

Return the active PolicyBundle for a team.

bash
GET /v1/teams/{teamId}/policy

Parameters

teamIdstringrequired
Path parameter. The team whose policy you want.

ReturnsPolicyBundle

A PolicyBundle with the fields below.

Example

get-policy.sh
curl https://<your-endpoint>/v1/teams/team_19/policy \  -H "Authorization: Bearer $HESO_API_KEY"

The response shape:

FieldTypeDescription
versionstringThe bundle version. Pin against it to detect a policy change.
rulesPolicyRule[]The ordered rule list, first-match-wins. See Policy files for the rule shape.
fetchedAtstringISO timestamp of when the bundle was fetched.

Receipts

Push signed receipts to the outbox. The server re-verifies every receipt before accepting it. It recomputes the BLAKE3 action_hash, checks the Ed25519 signatures, and re-derives the trust level — the same verify orderthe browser and SDKs run. A receipt that fails is rejected with a reason; acceptance never depends on the client’s word.

POST /v1/receiptsREST

Submit a single Action Receipt to the outbox.

bash
POST /v1/receipts

Parameters

bodyActionReceiptrequired
The full signed receipt. See the receipt schema for every field.

ReturnsOutboxPushResult

An OutboxPushResult with the fields below.

Example

post-receipt.sh
curl https://<your-endpoint>/v1/receipts \  -H "Authorization: Bearer $HESO_API_KEY" \  -H "Content-Type: application/json" \  -d @receipt.json

The response shape:

FieldTypeDescription
receiptIdstringThe server-assigned id for the stored receipt.
acceptedbooleanWhether the receipt passed server-side re-verification and was stored.
rejectionReasonstring?Present only when accepted is false — why the receipt was refused.

POST /v1/receipts/batchREST

Submit many receipts in one request.

bash
POST /v1/receipts/batch

Parameters

receiptsActionReceipt[]required
An array of signed receipts, wrapped in a { receipts } object.

ReturnsOutboxPushResult[]

An array of OutboxPushResult, one per submitted receipt, in the same order.

Example

post-receipts-batch.sh
curl https://<your-endpoint>/v1/receipts/batch \  -H "Authorization: Bearer $HESO_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "receipts": [ /* ActionReceipt, ActionReceipt, … */ ] }'

Each receipt is re-verified independently, so a batch can come back with some entries accepted and others rejected. Match each result to its receipt by position.

What acceptance means

An accepted receipt means the server re-ran the math and the signatures hold — it confirms the operator (and, at L1, an approver) authorized the action under a known policy. The server holds no signing key; it only re-checks what is already on the receipt. It records what was authorized, not whether the action succeeded downstream.

Approvals

When policy routes an action to a human, open an approval, poll it until the approver decides, then carry their decision back into the receipt. The approver signs with their own device-held key — the cloud holds no signing key. See Human approval for the full flow.

POST /v1/approvalsREST

Open an approval request for a receipt and route it to a human.

bash
POST /v1/approvals

Parameters

receiptActionReceiptrequired
The receipt whose action needs a human decision.
routingHintstring
Optional hint for who should be asked (e.g. a queue or on-call label).

ReturnsOpenApprovalResult

An OpenApprovalResult with the fields below.

Example

open-approval.sh
curl https://<your-endpoint>/v1/approvals \  -H "Authorization: Bearer $HESO_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "receipt": { /* ActionReceipt */ }, "routingHint": "finance-oncall" }'

The response shape:

FieldTypeDescription
approvalIdstringThe id you poll to learn the outcome.
tokenstring?An optional one-time token for the approver to act on.
expiresAtstringISO timestamp after which the request can no longer be answered.

GET /v1/approvals/{approvalId}REST

Poll the current state of an approval.

bash
GET /v1/approvals/{approvalId}

Parameters

approvalIdstringrequired
Path parameter. The id returned when the approval was opened.

ReturnsPollApprovalResult

A PollApprovalResult with the fields below.

Example

poll-approval.sh
curl https://<your-endpoint>/v1/approvals/apr_7c2 \  -H "Authorization: Bearer $HESO_API_KEY"

The response shape:

FieldTypeDescription
outcomeApprovalOutcomeThe current state — pending, or a settled decision.
approvalApproval?The settled approval record, present once the approver has decided.

The TypeScript SDK’s waitForApproval wraps this poll loop for you, with a configurable interval and timeout — reach for it instead of writing your own loop.

POST /v1/approvals/{approvalId}/tokenREST

Submit the approver's signed token to settle an approval.

bash
POST /v1/approvals/{approvalId}/token

Parameters

approvalIdstringrequired
Path parameter. The id of the open approval.
tokenstringrequired
The approver's token, carried in the request body.

ReturnsApproval

The settled Approval record.

Example

submit-token.sh
curl https://<your-endpoint>/v1/approvals/apr_7c2/token \  -H "Authorization: Bearer $HESO_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "token": "3a9f…04af" }'

Errors and limits

A non-2xx response carries a JSON error body. The TypeScript client surfaces this as a thrown Error whose message includes the HTTP status, the statusText, and the response body, so you do not have to parse it by hand.

error.json
{  "error": "policy_not_found",  "message": "No policy bundle for team team_19"}

The status codes you will see:

StatusMeaning
400Malformed request — bad JSON, or a body that fails schema validation.
401Missing or invalid API key.
404No such team, receipt, or approval.
422A receipt was well-formed but failed server-side re-verification.
429Rate limited — the request rate for your plan was exceeded. Wait for the Retry-After header, then retry (see Rate limits).
503Server briefly at capacity — retry after the short Retry-After delay.
Rate limits and quotas

Each plan has a sustained request rate (with a short burst allowance) and a monthly receipt quota. The exact numbers depend on your plan and can change, so rather than list them here we surface them where they stay accurate: your dashboard shows the current rate limit and usage for your team. Over the rate, the API returns 429 with a Retry-After header — see Rate limits for the headers and how to back off.

Next steps