Consent Records
2 operations under /v1/consent-records (GET): parameters, status codes, refusal bodies, and the same request in curl, HTTP, Python and TypeScript.
Base URL: https://api.codespar.dev
Every operation below requires a Bearer token. See Authentication.
GET /v1/consent-records
https://api.codespar.dev/v1/consent-recordsList consent evidence
The organization's consent evidence, most recent first. It is append-only: nothing here is edited after it is signed.
The response is { consents: [...] }.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
consumer_id | string | no | — |
limit | integer | no | 1 to 200; the default is 50. |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | The query did not match the schema. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
consents | array of object | yes | — |
curl -X GET https://api.codespar.dev/v1/consent-records \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/consent-records HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/consent-records",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/consent-records", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/consent-records");{
"consents": [
{
"id": "obj_0000000000000000",
"consumer_id": "csm_0000000000000000",
"funding_source_id": "fundingsource_0000000000000000",
"agent_id": "agt_0000000000000000",
"kind": "grant",
"hmac_secret_version": 0,
"cap_minor": "1000",
"per_tx_cap_minor": "1000",
"currency": "BRL",
"purpose": "string",
"ip_address": "string",
"signed_at": "2026-01-15T12:00:00.000Z",
"expires_at": "2026-01-15T12:00:00.000Z",
"metadata": {}
}
]
}GET /v1/consent-records/{id}
https://api.codespar.dev/v1/consent-records/{id}Read one consent record
One row of the consent log: the moment a consumer authorized a specific agent to debit a specific funding source up to a specific cap. Requires the consents:read scope.
APPEND-ONLY EVIDENCE. consumer_consents is the LGPD audit log — rows are never rewritten and never deleted, and a revocation is a new row with kind = 'revoke'. That is what lets a revocation be replayed from the log alone, and it is why this read returns exactly what the listing returns: there is no derived state for a point read to add.
RESOLUTION IS SCOPED TO THE ORGANIZATION ONLY — the query predicates on org_id and the id, with no project term. A consent record created under a sibling project in the same organization IS readable with this credential. The funding-source read in this same group is narrower, so do not carry an assumption across the two.
NO SIGNATURE MATERIAL LEAVES. The row carries payload_hash and hmac_signature; both are dropped before the body is built. What crosses the wire is hmac_secret_version — the stamp that says which secret signed it — and its note explains why that stamp is what makes secret rotation safe rather than destructive.
PERSONAL DATA, TRUNCATED. ip_address is the network of the consumer's address (/24 for IPv4, /64 for IPv6); the full address stays on the row, recorded because a consent event has to be replayable with its origin.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | yes | The consent record id. |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
404 | object | Not Found. No consent record with this id in the caller's organization. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
agent_id | string,null | yes | The agent the consumer authorized, when the row names one. |
cap_minor | string,null | yes | A STRING, not a number. The column is bigint and the driver hands bigints back as strings; the handler passes the value straight through. Parse it as an integer in the row's currency, and do not assume it survives a float. Null on revoke / amend rows. |
consumer_id | string | yes | — |
currency | "BRL" | "USD" | "MXN" | "COP" | "ARS" | "USDC" | "BRLA" | yes | Closed by consumer_consents_currency_check (name in exactly one migration file), which admits NULL as well as the seven values. Non-null on grant rows, which the completeness constraint requires to declare one. |
expires_at | string,null (date-time) | yes | — |
funding_source_id | string,null | yes | The consumer_funding_sources row this consent authorizes debits against. Nullable on the column, and required by consumer_consents_grant_completeness on kind = 'grant' rows: a grant must declare what it authorizes, while revoke and amend reference the original grant. |
hmac_secret_version | integer | yes | Which version of the consumer's HMAC secret signed this row. This is the field that makes rotation safe, and the half that is usually left out when the rule is quoted. Rotating a consumer's secret invalidates FUTURE mandates only, and the reason is here rather than in the rotation: the verifier resolves the secret by (consumer, version) and the mandate itself carries the version it was signed under, so a mandate signed under the prior version still resolves the prior secret and still verifies. Rotation marks the old row and inserts version+1; it deletes nothing. Without the version stamp the sentence would mean the opposite — that rotating breaks the audit trail. |
id | string | yes | ccsnt_-prefixed on rows the mandate-lifecycle writer creates. |
ip_address | string,null | yes | The network of the consumer's IP at signing time: an IPv4 address truncated to its /24 (203.0.113.0), an IPv6 address to its /64 (2001:db8:85a3:8d3::), null when none was recorded or the stored value does not parse. The inet column keeps the full address, because a consent event has to be replayable with timestamp and origin; only the response is truncated. user_agent is recorded alongside it and is NOT returned. |
kind | "grant" | "revoke" | "amend" | yes | Closed by consumer_consents_kind_check, whose name appears in exactly one migration file. The log is append-only: a revocation is a NEW row with kind = 'revoke', never a rewrite of the grant, which is what makes a revocation replayable from the log alone. |
metadata | object | yes | Defaults to {}. Rows written by the mandate-lifecycle handler carry the transition instead: action, from, to, and reason when the caller gave one. |
per_tx_cap_minor | string,null | yes | Same bigint-as-string treatment as cap_minor. |
purpose | string,null | yes | The purpose the consumer signed. Non-null on grant rows. |
signed_at | string (date-time) | yes | When the consent event happened. The listing orders by this. |
curl -X GET https://api.codespar.dev/v1/consent-records/{id} \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/consent-records/{id} HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/consent-records/{id}",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/consent-records/{id}", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/consent-records/{id}", {
path: {
id: "consentrecord_0000000000000000"
}
});{
"id": "obj_0000000000000000",
"consumer_id": "csm_0000000000000000",
"funding_source_id": "fundingsource_0000000000000000",
"agent_id": "agt_0000000000000000",
"kind": "grant",
"hmac_secret_version": 0,
"cap_minor": "1000",
"per_tx_cap_minor": "1000",
"currency": "BRL",
"purpose": "string",
"ip_address": "string",
"signed_at": "2026-01-15T12:00:00.000Z",
"expires_at": "2026-01-15T12:00:00.000Z",
"metadata": {}
}Audit Events
10 operations under /v1/audit-events (GET POST PATCH): parameters, status codes, refusal bodies, and the same request in curl, HTTP, Python and TypeScript.
Commerce Memory
12 operations under /v1/commerce-memory (GET POST): parameters, status codes, refusal bodies, and the same request in curl, HTTP, Python and TypeScript.