Mandates
Generated HTTP reference for the 5 operations the published OpenAPI document describes under mandates.
Mandates
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.
These five operations read and transition a consumer mandate: the allowance a consumer signed for one agent, with a purpose, a payee allowlist, per-currency caps and an expiry. None of them creates one. A mandate is born in the consent flow (POST /v1/consents/init, then the consumer signs at the hosted page) and is signed server-side, so there is no signing step on your side. The concept page, Mandates, receipts and approvals, has the matrix of what exists per channel (REST, SDK, CLI, dashboard, meta-tool), the lifecycle, and what is still not in this document.
The same four transitions are also registered under /v1/consumers/mandates/{id}/..., the older spelling. They are not in this document; the canonical paths below are the ones to call.
Every operation, from the spec
Generated from the published OpenAPI document, so it never drifts from what the API actually serves. The section above is written by hand and carries what a schema cannot: the object model, field rules, and the order to call things in.
GET /v1/mandates
https://api.codespar.dev/v1/mandatesList consumer allowances
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
consumer_id | string | no | — |
limit | integer | no | — |
status | "active" | "paused" | "revoked" | "expired" | no | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | The query did not match the schema: an unknown status, a limit outside 1..200, or a limit that is not a number. details.issues carries the Zod issues. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
mandates | array of object | yes | — |
curl -X GET https://api.codespar.dev/v1/mandates \
-H "Authorization: Bearer $CODESPAR_API_KEY"const res = await fetch("https://api.codespar.dev/v1/mandates", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/mandates");{
"mandates": [
{
"id": "obj_0000000000000000",
"consumer_id": "csm_0000000000000000",
"agent_id": "agt_0000000000000000",
"purpose": "string",
"merchant_allowlist": [
"string"
],
"merchant_pin_kind": "pix-key",
"cap_minor": "1000",
"per_tx_cap_minor": "1000",
"currency": "BRL",
"status": "active",
"expires_at": "2026-01-15T12:00:00.000Z",
"created_at": "2026-01-15T12:00:00.000Z"
}
]
}GET /v1/mandates/{id}
https://api.codespar.dev/v1/mandates/{id}Read one consumer allowance
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | yes | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
404 | object | No such allowance for this credential's organization. An allowance belonging to another organization answers exactly the same way: the org predicate sits in the SELECT, so the handler cannot tell absent from someone else's, and must not. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
agent_id | string | yes | The agent this allowance authorizes to spend. |
cap_minor | string | yes | Total spend authorized over the life of the allowance, in minor units. A STRING: the column is bigint and the driver hands it back as text, so parse it as an integer and never as a float. |
consumer_id | string | yes | — |
created_at | string (date-time) | yes | — |
currency | string | yes | Left open here rather than closed. The column admits BRL, USD, MXN, COP, ARS, USDC and BRLA today, and that list is a CHECK constraint on our side rather than a promise on the wire. |
display_name | string,null | yes | — |
expires_at | string (date-time) | yes | Never null: the column is NOT NULL. |
id | string | yes | — |
intent_note | string,null | yes | — |
merchant_allowlist | array of string | yes | The payees this allowance may pay, read as merchant_pin_kind says to read them. Never empty: consumer_mandates_allowlist_nonempty refuses a row whose allowlist is not an array of at least one entry, so an allowance that authorizes nothing cannot exist. |
merchant_pin_kind | "pix-key" | "merchant-id" | "mcc" | yes | How each allowlist entry pins a payee. The v1 rail pins by Pix key. |
per_tx_cap_minor | string | yes | Per-transaction ceiling, in minor units. Same string shape as cap_minor. |
purpose | string | yes | — |
status | "active" | "paused" | "revoked" | "expired" | yes | — |
curl -X GET https://api.codespar.dev/v1/mandates/{id} \
-H "Authorization: Bearer $CODESPAR_API_KEY"const res = await fetch("https://api.codespar.dev/v1/mandates/{id}", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/mandates/{id}", {
path: {
id: "mandate_0000000000000000"
}
});{
"id": "obj_0000000000000000",
"consumer_id": "csm_0000000000000000",
"agent_id": "agt_0000000000000000",
"purpose": "string",
"merchant_allowlist": [
"string"
],
"merchant_pin_kind": "pix-key",
"cap_minor": "1000",
"per_tx_cap_minor": "1000",
"currency": "BRL",
"status": "active",
"expires_at": "2026-01-15T12:00:00.000Z",
"created_at": "2026-01-15T12:00:00.000Z"
}POST /v1/mandates/{id}/pause
https://api.codespar.dev/v1/mandates/{id}/pausePause an allowance
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | yes | — |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
reason | string | no | Recorded in the evidence row's metadata, alongside the from/to pair. Not echoed in the response. |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | A body was sent and did not match the schema. details.issues carries the Zod issues. |
404 | object | No such allowance for this credential's organization. An allowance belonging to another organization answers exactly the same way: the org predicate sits in the SELECT, so the handler cannot tell absent from someone else's, and must not. |
409 | object | invalid_transition when the allowance's current status is not a legal source for this verb. transition_conflict when another request moved the allowance between this one's read and its write: the UPDATE is guarded on the status that was read, so a concurrent transition loses rather than double-applying, and the evidence row is written in the same transaction as the status change or not at all. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
changed | boolean | yes | False when the allowance was already in this state. Nothing was written and no evidence row was appended. |
mandate | object | yes | — |
curl -X POST https://api.codespar.dev/v1/mandates/{id}/pause \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"reason": "string"
}'const res = await fetch("https://api.codespar.dev/v1/mandates/{id}/pause", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"reason": "string"
}),
});
const data = await res.json();const result = await cs.api.post("/v1/mandates/{id}/pause", {
path: {
id: "mandate_0000000000000000"
},
body: {
reason: "string"
}
});{
"mandate": {
"id": "obj_0000000000000000",
"status": "paused"
},
"changed": true
}POST /v1/mandates/{id}/resume
https://api.codespar.dev/v1/mandates/{id}/resumeResume a paused allowance
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | yes | — |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
reason | string | no | Recorded in the evidence row's metadata, alongside the from/to pair. Not echoed in the response. |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | A body was sent and did not match the schema. details.issues carries the Zod issues. |
404 | object | No such allowance for this credential's organization. An allowance belonging to another organization answers exactly the same way: the org predicate sits in the SELECT, so the handler cannot tell absent from someone else's, and must not. |
409 | object | invalid_transition when the allowance's current status is not a legal source for this verb. transition_conflict when another request moved the allowance between this one's read and its write: the UPDATE is guarded on the status that was read, so a concurrent transition loses rather than double-applying, and the evidence row is written in the same transaction as the status change or not at all. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
changed | boolean | yes | False when the allowance was already in this state. Nothing was written and no evidence row was appended. |
mandate | object | yes | — |
curl -X POST https://api.codespar.dev/v1/mandates/{id}/resume \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"reason": "string"
}'const res = await fetch("https://api.codespar.dev/v1/mandates/{id}/resume", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"reason": "string"
}),
});
const data = await res.json();const result = await cs.api.post("/v1/mandates/{id}/resume", {
path: {
id: "mandate_0000000000000000"
},
body: {
reason: "string"
}
});{
"mandate": {
"id": "obj_0000000000000000",
"status": "active"
},
"changed": true
}POST /v1/mandates/{id}/revoke
https://api.codespar.dev/v1/mandates/{id}/revokeRevoke an allowance
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | yes | — |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
reason | string | no | Recorded in the evidence row's metadata, alongside the from/to pair. Not echoed in the response. |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | A body was sent and did not match the schema. details.issues carries the Zod issues. |
404 | object | No such allowance for this credential's organization. An allowance belonging to another organization answers exactly the same way: the org predicate sits in the SELECT, so the handler cannot tell absent from someone else's, and must not. |
409 | object | invalid_transition when the allowance's current status is not a legal source for this verb. transition_conflict when another request moved the allowance between this one's read and its write: the UPDATE is guarded on the status that was read, so a concurrent transition loses rather than double-applying, and the evidence row is written in the same transaction as the status change or not at all. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
changed | boolean | yes | False when the allowance was already in this state. Nothing was written and no evidence row was appended. |
mandate | object | yes | — |
curl -X POST https://api.codespar.dev/v1/mandates/{id}/revoke \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"reason": "string"
}'const res = await fetch("https://api.codespar.dev/v1/mandates/{id}/revoke", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"reason": "string"
}),
});
const data = await res.json();const result = await cs.api.post("/v1/mandates/{id}/revoke", {
path: {
id: "mandate_0000000000000000"
},
body: {
reason: "string"
}
});{
"mandate": {
"id": "obj_0000000000000000",
"status": "revoked"
},
"changed": true
}