Skip to main content
API reference

Facilitator

Generated HTTP reference for the 2 operations the published OpenAPI document describes under facilitator.

6 min read
View MarkdownEdit on GitHub

Facilitator

This page is generated from the published OpenAPI document. It is complete with respect to that document and says nothing about surfaces the document does not describe yet. See what is generated here for what that means.

Base URL: https://api.codespar.dev

Every operation below requires a Bearer token. See Authentication.

POST /v1/facilitator/x402/executions

Ingest and execute a foreign x402 authorization

Request body

FieldTypeRequiredDescription
authorizationobjectyes
domainobjectyes
intentobjectyes
signaturestringyes

Responses

StatusBodyDescription
200objectOK
400objectBad Request. missing_required_field is the body failing validation, with the Zod issues in error.details.issues. malformed_authorization is the verifier or the payee binding rejecting a field the schema let through — the schema's address regex accepts any 40 hex digits, including a mixed-case address whose EIP-55 checksum is wrong, and the on-chain address parser does not. It covers authorization.from, authorization.to, intent.payee and domain.verifyingContract, and an out-of-range domain.chainId. invalid_payload is the cap gate refusing the translated mandate's shape.
401objectUnauthorized — the SIGNATURE, not the credential. bad_signature is a signature that does not recover; signer_mismatch is one that recovers to an address other than authorization.from. A rejected API key or access token never reaches this handler.
409objectConflict — this (signer, nonce) has already been executed, so it settles once. error.details.foreign_authorization_id names the row that ALREADY executed; no new row was created. The same code answers the race, where a concurrent execution won and the unique index on executed nonces refused the loser — in that case the id is the loser's own row.
422objectUnprocessable — the request is well-formed and the signature is valid, but a bound refuses it. authorization_not_yet_valid and authorization_expired are the authorization's own window. payee_authorization_mismatch is intent.payee differing from the recipient the signature authorizes on-chain, compared after both are checksummed. per_tx_cap_exceeded and total_cap_exceeded are the cap gate; see the note above on what omitting total_cap_minor does. expired is the translated mandate outliving its own window between the two checks. x402_mainnet_cap_exceeded is the operator's per-payment mainnet ceiling, a second gate on top of the mandate's own. The pipeline also guards against a non-positive amount with amount_below_minimum, which is NOT in this list: authorization.value is validated as strictly greater than zero before the handler runs, so that guard is defence in depth for other callers of the pipeline and is not a result an HTTP caller can produce.
500objectInternal error from the cap gate, surfaced rather than swallowed. Reached when the gate fails for a reason that should be impossible on this path — the mandate secret vanishing between the moment it was resolved and the moment the gate looked it up, say. error.code is left open here because it echoes whatever the gate reported.
501objectNot Implemented — a live credential asked for mainnet settlement and the operator has not enabled it. Refused before any signature work, any database write or any wallet call, so nothing is left dangling. A test credential cannot reach this: it always settles on testnet.
502objectBad Gateway — on-chain settlement failed. The evidence row is marked rejected with reject_code = 'settlement_failed' and its id is in error.details.foreign_authorization_id. A broadcast is not a settlement: a transaction that reverts or is not confirmed in time lands here too, so a 502 does NOT mean the money is in flight.

Response 200

FieldTypeRequiredDescription
execution_idstringyes
foreign_authorization_idstringyesfauth_-prefixed. The id GET /v1/facilitator/x402/executions/\{id\} takes.
mandate_idstringyes
receiptobjectyes
settlementobjectyes
signerstringyesThe address recovered from the signature.
status"executed"yes

Example response

{
  "status": "executed",
  "foreign_authorization_id": "foreignauthorization_0000000000000000",
  "mandate_id": "mandate_0000000000000000",
  "execution_id": "execution_0000000000000000",
  "signer": "string",
  "settlement": {
    "tx_hash": "string",
    "network": "string",
    "money_moved": true,
    "adapter": "string"
  },
  "receipt": {
    "id": "obj_0000000000000000",
    "state": "paid",
    "chain": "string"
  }
}

Example request

curl -X POST https://api.codespar.dev/v1/facilitator/x402/executions \
  -H "Authorization: Bearer $CODESPAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
       "authorization": {
         "from": "string",
         "to": "string",
         "value": "string",
         "validAfter": "string",
         "validBefore": "string",
         "nonce": "string"
       },
       "domain": {
         "name": "Example",
         "version": "string",
         "chainId": 0,
         "verifyingContract": "string"
       },
       "signature": "string",
       "intent": {
         "payee": "string",
         "resource": "string",
         "seller": "string",
         "purpose": "string",
         "max_amount_minor": 1000,
         "total_cap_minor": 1,
         "resource_url": "https://example.com/hook"
       }
     }'

GET /v1/facilitator/x402/executions/{id}

Read one ingested foreign authorization

Path parameters

NameTypeRequiredDescription
idstringyesfauth_-prefixed, as returned by the POST.

Responses

StatusBodyDescription
200objectOK
404objectNot Found. No foreign authorization with this id in the caller's organization — including the case where the authorization verified but was refused before any row was written.

Response 200

FieldTypeRequiredDescription
amount_minorintegeryesA COARSE cents figure, rounded UP from value_atomic, kept so older minor-denominated reads still work. It is NOT what the cap gate ran on and NOT what moved: the gate and the settlement both use atomic. Read value_atomic when the number has to be exact.
assetstring,nullyesThe token contract — the EIP-712 verifyingContract, checksummed.
chain_idinteger,nullyes
created_atstring (date-time)yes
execution_idstring,nullyesx402exec_-prefixed. Set on success.
idstringyesfauth_-prefixed.
kind"x402" | "ap2" | "acp"yesClosed by the kind CHECK in packages/api/src/migrations/0099_foreign_authorizations.sql, which is also where this table's status set is closed. Only x402 is written today; ap2 and acp are reserved for the ingestion paths that reuse the same machinery.
mandate_idstring,nullyesThe internal mandate the foreign authorization was translated onto. Set on success.
networkstring,nullyesbase-sepolia for a test credential, base for a live one. Stamped at insert time, so a rejected row carries it too.
noncestringyesThe EIP-3009 nonce, 32 bytes hex. Unique per authorization.
payeestringyesThe on-chain recipient, checksummed.
receipt_idstring,nullyesThe sealed Control Record receipt. Set on success.
reject_codestring,nullyesThe refusal ground on a rejected row: the cap-gate or mainnet-cap code, or the literal settlement_failed. It is the same string the POST answered with under error.code. Null otherwise, and an open string here because its source set is the cap gate's, not this table's.
settlement_txstring,nullyesThe on-chain settlement transaction hash. Set on success.
signerstringyesThe address RECOVERED from the signature, not the from the caller sent.
status"verified" | "executed" | "rejected"yesClosed by CHECK (status IN ('verified','executed','rejected')) in packages/api/src/migrations/0099_foreign_authorizations.sql. The evidence row is inserted verified only AFTER the payee binding, the replay guard and the amount guard have all passed — not when the signature verifies. A refusal at any of those three leaves NO row, so an authorization that verified cryptographically may have no record here at all. From that point every refusal marks the row rejected with its code in reject_code, and success marks it executed in the same transaction that seals the receipt, so the two commit together.
value_atomicstringyesThe exact authorized amount in atomic units (USDC, 6dp). This is what settles.

Example response

{
  "id": "obj_0000000000000000",
  "kind": "x402",
  "signer": "string",
  "payee": "string",
  "nonce": "string",
  "value_atomic": "string",
  "amount_minor": 1000,
  "status": "verified",
  "created_at": "2026-01-15T12:00:00.000Z"
}

Example request

curl -X GET https://api.codespar.dev/v1/facilitator/x402/executions/{id} \
  -H "Authorization: Bearer $CODESPAR_API_KEY"
Facilitator | CodeSpar