ClocknextHelp
DocumentationAPI ReferenceMCP Tools
Dashboard

Get started

OverviewQuickstart

Good to know

Response formatHandling errorsPaginating resultsSending data reliablyYour own customer idsWebhooks

API reference

Record credit POSTRecord outcome POSTComplete outcome POSTRecord wallet POSTRecord unit POSTComplete composite POST
List customers GETCreate a customer POSTGet a customer GETUpdate a customer PATCHDelete a customer DELETEGet a customer's balances GETGet a customer's wallet ledger GETSet a customer's low-balance alert PUTGet a customer's current plan GETChange a recurring grant PUTList Customer Usage Log GETLook Up Usage Logs by ID GET
Manage a customer's credit balance POSTManage a customer's outcome balance POSTManage a customer's unit balance POSTManage a customer's composite balance POSTManage a customer's wallet balance POST
List a customer's members GETAdd a member POSTUpdate a member PATCHRemove a member DELETE
PlansCreditsOutcomesUnitsComposites
List taxes GETCreate a tax POSTGet a tax GETUpdate a tax PATCH
List purchases GETCreate a purchase POSTGet a purchase GETUpdate a purchase's auto-payment setting PATCHCancel a purchase POST
List invoices GETGet an invoice GETMint a hosted pay link GET
List payments GETGet a payment GET
wallet.balance.update POSTwallet.balance.drop POST
Mint a customer-portal access token POST
Signals

Record outcome

Records one step of a multi-call workflow. agentKey names the OutcomeStep. runId is required and groups every signal of one run — a step may repeat freely.

This only records the step; it never bills. Close and bill the run with a separate call to POST /signal/outcome/complete.


If the model has both cache-read and cache-write rates, cacheTokens carries the cache-**read** tokens only (billed at the cache-read rate); send cache-write tokens separately as cacheWriteTokens.

POST
/signal/outcome

Authorization

apiKey
AuthorizationBearer <token>

Organisation-scoped key from Settings → API Keys.

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

The step shape ONLY — POST /signal/outcome accepts nothing else. A body carrying complete is rejected outright; declaring the workflow finished is POST /signal/outcome/complete (see OutcomeCompletionInput), a wholly separate request.

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/signal/outcome" \  -H "Content-Type: application/json" \  -d '{    "customerId": "cus_abc123",    "agentKey": "resolved_ticket.triage",    "runId": "ticket_9021",    "usage": {      "model": "openai/gpt-4o",      "inputTokens": 900,      "outputTokens": 120    }  }'
{  "statusCode": 200,  "statusDetail": {    "status": "SUCCESS",    "message": "OK"  },  "result": {    "usageLog": {      "id": "log_8m3n5p",      "customerId": "cus_abc123",      "modelName": "gpt-4o",      "creditName": "Deep Research",      "member": "dana@acme.com",      "inputTokens": 1200,      "outputTokens": 350,      "cacheTokens": 200,      "cacheWriteTokens": 0,      "credit": 2,      "providedCost": 0.0087,      "customerCost": 0.012,      "status": "SUCCESS",      "appliedRules": [],      "outcome": null,      "balance": {        "dimension": "credit",        "remaining": 18,        "name": "Deep Research"      },      "createdAt": "2026-07-21T14:03:22.000Z"    },    "rawId": "raw_4k8w2r"  }}
{  "statusCode": 202,  "statusDetail": {    "status": "SUCCESS",    "message": "Signal accepted for processing."  },  "result": {    "queued": true,    "messageId": "msg_2f4a1b8c",    "rawId": "raw_4k8w2r"  }}
{  "statusCode": 400,  "statusDetail": {    "status": "ERROR",    "message": "Request body must be valid JSON."  },  "result": {}}
{  "statusCode": 401,  "statusDetail": {    "status": "ERROR",    "message": "Invalid API key."  },  "result": {}}
{  "statusCode": 404,  "statusDetail": {    "status": "ERROR",    "message": "No customer with id \"cus_abc123\"."  },  "result": {}}
{  "statusCode": 409,  "statusDetail": {    "status": "ERROR",    "message": "Customer \"cus_abc123\" is archived."  },  "result": {}}
{  "statusCode": 422,  "statusDetail": {    "status": "ERROR",    "message": "No credit with agent key \"summarize\" exists in this workspace."  },  "result": {}}
{  "statusCode": 500,  "statusDetail": {    "status": "ERROR",    "message": "Could not complete the request."  },  "result": {}}

Record credit POST

Records usage against a named Credit balance. `agentKey` must be sold on the customer's active plan, or covered by a composite that is. Priced automatically from the model's rates and token counts — you can't post a price yourself. If the model has both cache-read and cache-write rates, `cacheTokens` carries the cache-**read** tokens only (billed at the cache-read rate); send cache-write tokens separately as `cacheWriteTokens`.

Complete outcome POST

Closes and bills ONE outcome run. This is the only way to close a run; `POST /signal/outcome` never accepts `complete`. Closes the run only. If a step carried a `composite`, that occurrence still needs its own [`POST /signal/composite/complete`](/docs/api-reference/signals/completeCompositeSignal).

customerId?string

Customer id. Required unless externalId is sent.

Length1 <= length
externalId?string

Your own id for the customer, instead of customerId. Signals are processed asynchronously, so one matching no customer is accepted and then left Pending with a 404 reason — create the customer and retry it.

Match^[A-Za-z0-9-]{1,32}$
Length1 <= length <= 32
member?string

Member email this signal is attributed to; must belong to the customer. Matched case-insensitively.

idempotencyKey?string

Dedupes retries — a repeat request carrying the same key returns the original signal's result instead of recording a duplicate. Scoped per organisation AND per meter, so one key reused across /signal/credit and /signal/wallet is two independent keys, not a collision. See Async, flushing & reliability.

Length1 <= length <= 255
agentKey*string

The OutcomeStep's agent key — a step identifies its outcome too, so one key resolves both.

Length1 <= length
runId*string

Groups every signal of one workflow attempt. A step may repeat freely within a run. A run id belongs to exactly one (outcome, customer) pair, and one that has already completed is rejected — start a new run to bill another outcome.

Length1 <= length <= 255
usage*

What one LLM call did. Grouped under usage rather than scattered across the top level, because it is all one idea. Required on credit, outcome and wallet signals — all three are priced from the model's rates against these counts.

composite?

Composite tag. The property NAME is the composite's refId and the value is your own correlation id — { "voice_ai": "call_5f3a" } groups this signal into the voice_ai occurrence for call_5f3a.

Because the tag is nested under composite, a composite's refId can be any name — even one like model or status — without clashing with a real field of the signal.

A tag naming no live composite is ignored and never fails the signal — the signal carries money, the tag only decides which row of a report it lands in.

custom?

Free-form metadata.