Consumers
Generated HTTP reference for the 22 operations the published OpenAPI document describes under consumers.
Consumers
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.
GET /v1/consumers
List titulars
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit | integer | no | — |
verified | "true" | "false" | no | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | Bad Request — the body or query did not match the schema. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
consumers | array of object | yes | — |
Example response
{
"consumers": [
{
"id": "obj_0000000000000000",
"metadata": {},
"status": "unverified",
"created_at": "string"
}
]
}Example request
curl -X GET https://api.codespar.dev/v1/consumers \
-H "Authorization: Bearer $CODESPAR_API_KEY"POST /v1/consumers
Create the titular, without a document
Request body
| Field | Type | Required | Description |
|---|---|---|---|
display_name | string | no | — |
metadata | object | no | — |
Responses
| Status | Body | Description |
|---|---|---|
201 | object | OK |
400 | object | Bad Request — the body or query did not match the schema. |
Response 201
| Field | Type | Required | Description |
|---|---|---|---|
created_at | string | yes | — |
display_name | string,null | yes | — |
document | string,null | yes | — |
document_source | string,null | yes | — |
document_verified_at | string,null | yes | — |
id | string | yes | — |
metadata | object | yes | — |
person_type | string,null | yes | — |
status | "unverified" | "verified" | yes | — |
tax_type | string,null | yes | — |
Example response
{
"id": "obj_0000000000000000",
"metadata": {},
"status": "unverified",
"created_at": "string"
}Example request
curl -X POST https://api.codespar.dev/v1/consumers \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"display_name": "Example",
"metadata": {}
}'GET /v1/consumers/mandates/{id}/card
The card bound to a mandate, and its recent authorizations
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | yes | The mandate id. |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit | integer | no | Authorizations to return. Default 20, clamped to the range 1 to 100. |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
404 | object | No such mandate for this organization. A mandate another organization holds answers the same way. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
authorizations | array of object | yes | Newest first. Empty when no card is bound. |
card | object,null | yes | Null when the mandate exists and has no active card binding. |
mandate_id | string | yes | — |
Example response
{
"mandate_id": "mandate_0000000000000000",
"card": {
"card_id": "card_0000000000000000",
"consumer_id": "csm_0000000000000000",
"status": "string",
"bound_at": "2026-01-15T12:00:00.000Z"
},
"authorizations": [
{
"transaction_id": "transaction_0000000000000000",
"amount_minor": "1000",
"currency": "BRL",
"decision": "string",
"at": "2026-01-15T12:00:00.000Z"
}
]
}Example request
curl -X GET https://api.codespar.dev/v1/consumers/mandates/{id}/card \
-H "Authorization: Bearer $CODESPAR_API_KEY"DELETE /v1/consumers/mandates/{id}/card
Unbind the card from a mandate
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | yes | The mandate id. |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
404 | object | mandate_not_found: no such mandate for this organization, which is also the answer for another organization's mandate. no_active_card: the mandate exists and has no active card binding to revoke. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
card_id | string | yes | — |
mandate_id | string | yes | — |
revoked | true | yes | — |
Example response
{
"mandate_id": "mandate_0000000000000000",
"card_id": "card_0000000000000000",
"revoked": true
}Example request
curl -X DELETE https://api.codespar.dev/v1/consumers/mandates/{id}/card \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/consumers/receipts/{id}
Read one agentic receipt
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | yes | The receipt id. |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
404 | object | No receipt with this id in this project. A receipt in another project of the same organization answers the same way. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
chain | string | yes | — |
delivery | object,null | yes | Null until a delivery proof has been sealed onto the receipt. |
exceptions | array of object | yes | — |
mandate | object | yes | — |
payment | object | yes | — |
quote | object,null | yes | Null when the receipt carries neither a seller nor a resource. |
receipt_id | string | yes | — |
receipt_sig | string | yes | — |
state | "paid" | "delivered" | "exception" | "voided" | yes | — |
Example response
{
"receipt_id": "receipt_0000000000000000",
"state": "paid",
"mandate": {
"id": "obj_0000000000000000",
"nonce": "string",
"scope": "string",
"currency": "BRL",
"sig": "string"
},
"quote": {},
"payment": {
"rail": "string",
"amount_minor": 1000,
"amount_authorized": "1000",
"amount_charged": "1000",
"amount_refunded": "1000",
"metering": {
"basis": "string",
"units": "string",
"unit_price": "string"
},
"sandbox": true,
"attempt_id": "attempt_0000000000000000",
"money_moved": true,
"at": "string"
},
"delivery": {
"result": "confirmed"
},
"chain": "string",
"receipt_sig": "string",
"exceptions": [
{
"code": "string",
"detail": "string",
"at": "string"
}
]
}Example request
curl -X GET https://api.codespar.dev/v1/consumers/receipts/{id} \
-H "Authorization: Bearer $CODESPAR_API_KEY"POST /v1/consumers/{consumerId}/contact-verifications
Send a one-time code to a consumer's e-mail address or phone number
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
consumerId | string | yes | — |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
channel | "email" | "phone" | yes | — |
locale | string | no | — |
target | string | yes | — |
Responses
| Status | Body | Description |
|---|---|---|
201 | object | OK |
400 | object | invalid_body: the body did not match the schema, and details.issues carries the validation errors. invalid_target: the contact does not canonicalize for this channel. |
422 | object | no_celcoin_account: the consumer has no active pix-celcoin funding source in this organization — or is not this organization's consumer at all. otp_sender_refused: the sender refused to deliver to this number and repeating will not change that; details.provider names which sender said so. Nothing was sent, and no attempt was consumed, in either case. |
429 | object | too_many_requests: the per-contact hourly ceiling was reached. details.window_seconds and details.max_sends give the window and the ceiling. Nothing was sent. |
500 | object | object | Two different bodies, because two different failures land here. contact_verification_error in the usual error envelope: an unclassified failure while starting. Nothing was sent that could be verified. THE OTHER SHAPE IS NOT THAT ENVELOPE. Resolving the consumer's account runs before the handler's own error handling, so a failure there is answered by the framework's default: \{ statusCode, error, message \}, plus code when the underlying failure carried one. A client that parses error.code will find error is a STRING here. Nothing was sent in this case either. |
503 | object | otp_sender_unavailable, and the three conditions behind it DO NOT agree on whether a retry is free. 1. No sender is configured for this channel on the deployment. Nothing was written and nothing was sent; retrying changes nothing until the deployment does. 2. The send itself did not complete. The verification row is removed, so the attempt does not count against the hourly ceiling and a retry is free. Removal is best effort: if it fails the row stands, unverifiable, and does consume one of the hour's sends. 3. THE CODE REACHED THE CONTACT and the row could not be completed. The row STANDS and DOES count against the hourly ceiling, and the consumer may receive a code that can never be verified. details.verification_id is present, and it is present ONLY in this case — that is how a caller tells the free retry from the expensive one. |
Response 201
| Field | Type | Required | Description |
|---|---|---|---|
channel | "email" | "phone" | yes | — |
expires_at | string (date-time) | yes | MAY ALREADY BE IN THE PAST. A second start for the same contact supersedes the earlier one by closing its validity, and if that lands while this send is in flight the row keeps the closed expiry and this field reports it. A superseded code answers 410 on verify. |
id | string (uuid) | yes | Send this back in the /verify step. |
provider | "dev" | "prelude" | "resend" | yes | Which sender delivered, and it decides the /verify step: prelude checks the code against the contact itself, so target is REQUIRED there. The other two are checked locally and do not need it. |
status | "sent" | yes | — |
target_masked | string | yes | The masked contact. The full value never comes back: the row keeps a keyed hash. |
Example response
{
"id": "obj_0000000000000000",
"channel": "email",
"target_masked": "string",
"provider": "dev",
"expires_at": "2026-01-15T12:00:00.000Z",
"status": "sent"
}Example request
curl -X POST https://api.codespar.dev/v1/consumers/{consumerId}/contact-verifications \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"channel": "email",
"target": "string",
"locale": "string"
}'POST /v1/consumers/{consumerId}/contact-verifications/{id}/verify
Check the code the consumer received
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
consumerId | string | yes | — |
id | string (uuid) | yes | — |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
code | string | yes | — |
target | string | no | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | invalid_body: the body did not match the schema (details.issues). invalid_code: the code is not 4-8 digits, or it does not match; details.attempts_left says what is left, and only the second of those spent an attempt. invalid_target: this verification is checked against the contact and target did not come with the code. |
404 | object | not_found: there is no verification with this id for this consumer. It covers a malformed id, a row of another consumer, a row of another organization, a row whose send never completed, and a target that does not hash to the row's — that last one having spent an attempt. |
410 | object | verification_expired: the code expired, or a later send superseded it, or the sender itself reports it expired. Start a new verification. |
429 | object | too_many_attempts: this verification has spent every attempt. There is no reset; start a new one. |
500 | object | contact_verification_error: an unclassified failure while checking. This operation reads nothing outside its own error handling, so this is its only 500 body. |
503 | object | otp_sender_unavailable covers two conditions that cost different things. THE PROVIDER DID NOT ANSWER the check. The attempt was reserved before the call, so it IS spent and is not given back — returning it would reopen the race the reservation closes. details.attempts_left says how many remain. NO SENDER FOR THIS ROW'S PROVIDER exists on this deployment. The verify goes back to whichever sender delivered, whatever the deployment is configured for today, and this check runs BEFORE the reservation: NO attempt was spent and the response carries no details at all. Nothing the caller does resolves it — start a new verification, which will be sent through a sender that exists. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
id | string (uuid) | yes | — |
status | "verified" | yes | — |
verified_at | string (date-time) | yes | — |
Example response
{
"id": "obj_0000000000000000",
"status": "verified",
"verified_at": "2026-01-15T12:00:00.000Z"
}Example request
curl -X POST https://api.codespar.dev/v1/consumers/{consumerId}/contact-verifications/{id}/verify \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"code": "string",
"target": "string"
}'GET /v1/consumers/{consumerId}/dda/boletos
The boletos DDA has mirrored for this consumer
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
consumerId | string | yes | — |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
from_date | string | yes | — |
to_date | string | yes | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | from_date or to_date is missing or is not YYYY-MM-DD. details.issues carries the validation issues. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
boletos | array of object | yes | — |
count | integer | yes | The length of boletos. No pagination, so this is the total for the window. |
from_date | string | yes | Echoes the validated from_date query parameter, so exactly YYYY-MM-DD. |
to_date | string | yes | Echoes the validated to_date query parameter, so exactly YYYY-MM-DD. |
Example response
{
"from_date": "string",
"to_date": "string",
"count": 1,
"boletos": [
{
"linha_digitavel": "string",
"status": "string"
}
]
}Example request
curl -X GET https://api.codespar.dev/v1/consumers/{consumerId}/dda/boletos \
-H "Authorization: Bearer $CODESPAR_API_KEY"POST /v1/consumers/{consumerId}/dda/subscriptions
Register the consumer's CPF/CNPJ with DDA
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
consumerId | string | yes | — |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
client_name | string | no | — |
document | string | yes | — |
idempotency_key | string | yes | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | Nothing was registered by this call: an attempt under this same key already holds the registration, or one is in flight. replayed is true and status is the row's current state. |
201 | object | This call registered the document. replayed is false and status is pending. |
400 | object | invalid_body, from three places: the body did not match the schema (and only then is details.issues present); document carries no CPF/CNPJ digits; idempotency_key is blank. Nothing was registered. |
403 | object | dda_document_not_owned: the document is not the one verified for this consumer, or the consumer entity and the funding source disagree about who this consumer is. dda_document_unauthorized: no active mandate names this document in its signed allowlist. Nothing was registered, and neither carries details. |
422 | object | no_celcoin_account: the consumer has no active pix-celcoin funding source in this organization — or is not this organization's consumer at all. dda_document_not_owned ALSO answers 422, and only for one cause: the account has no verified document on file at all, so no document can be shown to be theirs. Sending a different document will not help; finish onboarding and retry. |
500 | object | object | NOT the usual error envelope. Three reads — the consumer's account, the verified document, and the authorizing mandate — run before the handler's own error handling, so a failure in any of them is answered by the framework's default: \{ statusCode, error, message \}, plus code when the underlying failure carried one. All three sit before the provider is called, so nothing was registered. |
502 | object | dda_provider_unavailable, from two conditions the response does not separate: the provider answered and the answer was not a success (the local row is marked error), or the call was in flight and whether it arrived is genuinely not known here. Because the caller cannot tell them apart, the guidance is the same for both — retry with the SAME idempotency_key, which makes the retry the same registration rather than a second one. |
503 | object | dda_claim_unavailable: the local claim failed and NOTHING was sent to the provider. This is stated apart from the 502 on purpose — here it is PROVEN that nothing left. Retry with the SAME idempotency_key. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
client_request_id | string | yes | The provider-side anchor for this registration. Derived from (organization, consumer, document, idempotency_key), so the SAME key against a DIFFERENT document is a DIFFERENT registration. |
document | string | yes | Digits only. This is the document the consumer SIGNED in the mandate's allowlist, not the spelling that arrived in the body. |
mandate_id | string | yes | The active mandate whose signed allowlist authorized this. |
replayed | boolean | yes | true when this call registered nothing. Either an earlier attempt under this key already holds the registration, or one is in flight right now. |
status | "pending" | "active" | "error" | "deleted" | yes | A registration this call made is always pending, and becomes active only when the provider's confirmation webhook lands. A 200 (nothing registered by this call) reports the row's current state, which can be any of the four. |
Example response
{
"document": "string",
"status": "pending",
"client_request_id": "clientrequest_0000000000000000",
"mandate_id": "mandate_0000000000000000",
"replayed": true
}Example request
curl -X POST https://api.codespar.dev/v1/consumers/{consumerId}/dda/subscriptions \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"document": "string",
"client_name": "Example",
"idempotency_key": "string"
}'GET /v1/consumers/{consumerId}/dda/subscriptions/{document}
Read a DDA registration
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
consumerId | string | yes | — |
document | string | yes | CPF or CNPJ. Punctuation is accepted and stripped. |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | The path's document carries no CPF/CNPJ digits. |
404 | object | No DDA registration for this consumer and document. Also the answer for a consumer another organization holds. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
celcoin_subscription_id | string,null | yes | The provider's own id, null until the confirmation webhook supplies it. |
client_request_id | string | yes | The idempotency correlation carried to the provider on the registration. |
document | string | yes | Digits only. The stored form, which is what the provider leg was sent. |
status | "pending" | "active" | "error" | "deleted" | yes | pending until the provider's confirmation webhook lands. Nothing polls the provider for this: the webhook is the only source of a status change, so a registration can sit pending indefinitely if a delivery was lost. |
Example response
{
"document": "string",
"status": "pending",
"client_request_id": "clientrequest_0000000000000000"
}Example request
curl -X GET https://api.codespar.dev/v1/consumers/{consumerId}/dda/subscriptions/{document} \
-H "Authorization: Bearer $CODESPAR_API_KEY"DELETE /v1/consumers/{consumerId}/dda/subscriptions/{document}
End a DDA registration
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
consumerId | string | yes | — |
document | string | yes | CPF or CNPJ. Punctuation is accepted and stripped. |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | The path's document carries no CPF/CNPJ digits. |
404 | object | No DDA registration for this consumer and document. Also returned when the row disappeared between the ownership read and the provider call, because in that case nothing was sent upstream. |
502 | object | The registration could not be ended upstream. Nothing about the local row changed; retry later. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
document | string | yes | — |
status | "deleted" | yes | — |
Example response
{
"document": "string",
"status": "deleted"
}Example request
curl -X DELETE https://api.codespar.dev/v1/consumers/{consumerId}/dda/subscriptions/{document} \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/consumers/{consumerId}/fund/{txId}
Poll an onramp, and credit the wallet when it settles
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
consumerId | string | yes | — |
txId | string | yes | The ramp provider's transaction id, from the fund call. |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
404 | object | One code, and THREE distinct failures collapse into it. The route answers 404 exactly when the provider lookup yields nothing, and that lookup gives up in three separate places: 1. NOT CONNECTED. The organization has no ramp-provider account in connected state. A caller that never completed the provider connection sees this on every poll, for every transaction id. 2. CREDENTIAL GONE. The connection row exists and still reads as connected, but its stored credential no longer dereferences: revoked, rotated, or vaulted under a scope that no longer matches the row. Nothing else about the connection looks broken, which is what makes this one hard to recognize from the outside. 3. THE PROVIDER DID NOT RETURN IT. Any non-2xx from the provider lands here. That covers both this credential has no such transaction and the provider is down or refused, and the two are NOT distinguished: an outage is indistinguishable from an unknown id at this boundary. Only the third is worth retrying, and the response does not say which one happened. So read a 404 as this API cannot currently see the transaction, never as the transaction does not exist, and in particular never as grounds for starting a second onramp for the same deposit. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
amount_minor | integer | yes | USDC the wallet receives, in minor units. Only meaningful once the tx settles. |
credited | boolean | yes | True whenever this poll found the transaction in a terminal success state with a positive delivered amount, and therefore posted the ledger credit. It is true on EVERY such poll, not only the first: the credit deduplicates on a deterministic external reference derived from the transaction id, so the repeat post is a no-op. Read it as this transaction has settled and the credit exists, never as this call created the credit. |
currency | string | yes | — |
status | string | yes | The ramp provider's status, lower-cased and passed through (awaiting_deposit, processing, completed, and others it may add). Not a closed set. |
transaction_id | string | yes | Echoes the path parameter. |
Example response
{
"transaction_id": "transaction_0000000000000000",
"status": "string",
"amount_minor": 1000,
"currency": "BRL",
"credited": true
}Example request
curl -X GET https://api.codespar.dev/v1/consumers/{consumerId}/fund/{txId} \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/consumers/{consumerId}/pix-keys
List the Pix keys on the consumer's account
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
consumerId | string | yes | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
422 | object | no_celcoin_account: the consumer has no active Pix funding source, so there is no account to list. pix_key_provider_refused: the provider adjudicated and said no; details.provider_code names its business code, and retrying the same input cannot succeed. |
502 | object | The listing could not be completed upstream. The outcome is unknown and the condition is transient; retry. details.provider_code still carries whatever code the body named, so a generic provider error is visible without being called terminal. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
account_masked | string | yes | Four mask characters followed by the account's last two digits. |
count | integer | yes | — |
keys | array of object | yes | — |
Example response
{
"account_masked": "string",
"keys": [
{
"key": "string",
"key_type": "string"
}
],
"count": 1
}Example request
curl -X GET https://api.codespar.dev/v1/consumers/{consumerId}/pix-keys \
-H "Authorization: Bearer $CODESPAR_API_KEY"POST /v1/consumers/{consumerId}/pix-keys
Register a Pix key on the consumer's account
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
consumerId | string | yes | — |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
key | string | no | — |
key_type | "EVP" | "CPF" | "CNPJ" | "EMAIL" | "PHONE" | yes | — |
Responses
| Status | Body | Description |
|---|---|---|
201 | object | OK |
400 | object | invalid_body: the body did not match the schema (details.issues). pix_key_invalid: the value is not a valid key for the key_type asked for, or key came with an EVP request, or key was missing for every other type. Neither reached the provider. |
403 | object | pix_key_document_mismatch: a CPF/CNPJ key whose value is not the account holder's own document. Refused locally, without calling the provider. |
409 | object | pix_key_ownership_unverified: this e-mail or phone has no recent verification for this consumer. details carries the channel, the endpoint to call and the window a verification stays good for; start a contact verification, confirm the code, retry. Nothing reached the provider. pix_key_registration_uncertain: the registration was accepted and came back with no readable key, and the account's key list could not settle whether it landed. details carries retry_safe: false, inventory_known and inventory_count. LIST the keys instead of repeating the registration. |
422 | object | no_celcoin_account: the consumer has no active pix-celcoin funding source in this organization — or is not this organization's consumer at all; nothing reached the provider. pix_key_provider_refused: the provider adjudicated and refused, with details.provider_status and details.provider_code (null when the body named no code); repeating the same input will not pass. |
500 | object | object | NOT the usual error envelope. Resolving the consumer's account runs before the handler's own error handling, so a failure there is answered by the framework's default: \{ statusCode, error, message \}, plus code when the underlying failure carried one. It happens before the provider is called, so nothing was registered. |
502 | object | pix_key_provider_unavailable, and THREE different failures produce it with THREE different details. Read the field, not the code. 1. A transient upstream failure — a 5xx, transport, credentials, a generic internal code, a codeless 429/408/404. details.provider_status and details.provider_code (null when the body named no code). 2. A registration that PROVABLY did not land: the account's key list answered and does not carry the key. details.verified_not_registered is true and details.inventory_count is the number of keys the list reported. There is NO provider_code here — no provider code was involved in reaching this conclusion. 3. Anything the handler did not classify. No details at all. All three are retriable; only the second one is retriable on evidence rather than on the absence of it. |
Response 201
| Field | Type | Required | Description |
|---|---|---|---|
account_masked | string | yes | The account, masked to its last two digits. |
cache_filled | boolean | yes | false when the key WAS registered but the local cache could not be filled. Not a registration failure. |
key | string | yes | The registered key. For EVP this is the random key the provider minted, and this response is the only place it appears in this call. |
key_type | string | yes | The type as the provider returned it. Deliberately an open string rather than the request's enum: the value comes back from upstream and is carried through untyped. |
reconciled | boolean | yes | true when the key returned here came from re-reading the account's key list, because the registration response was unreadable. The key belongs to the account; THIS call is not proven to be what created it. |
Example response
{
"key": "string",
"key_type": "string",
"account_masked": "string",
"cache_filled": true,
"reconciled": true
}Example request
curl -X POST https://api.codespar.dev/v1/consumers/{consumerId}/pix-keys \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"key_type": "EVP",
"key": "string"
}'DELETE /v1/consumers/{consumerId}/pix-keys/{key}
Delete a Pix key from the consumer's account
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
consumerId | string | yes | — |
key | string | yes | The key value. Trimmed; an empty value is 400. |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | The key in the path is empty after trimming. |
422 | object | no_celcoin_account: the consumer has no active Pix funding source. pix_key_provider_refused: the provider adjudicated and refused; retrying the same input cannot succeed. details.provider_code carries its business code. |
502 | object | The deletion could not be completed upstream. Whether the key is gone is unknown; retry, then list the keys to confirm. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
account_masked | string | yes | — |
cache_cleared | boolean | yes | Whether the deleted key was the one this API had cached as the receiving key. |
cache_refilled_with | string,null | yes | When the cache was cleared, the account's remaining keys are re-read and one is cached back, preferring a non-EVP key. Null when nothing was cleared, nothing remains, or the inventory read did not answer. |
deleted | true | yes | — |
key | string | yes | The canonical form actually sent upstream. A punctuated CPF or CNPJ is normalized before the delete, so this can differ from the value in the path. |
Example response
{
"key": "string",
"account_masked": "string",
"deleted": true,
"cache_cleared": true
}Example request
curl -X DELETE https://api.codespar.dev/v1/consumers/{consumerId}/pix-keys/{key} \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/consumers/{consumerId}/pix/charges/{reference}
A minted Pix charge's state
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
consumerId | string | yes | — |
reference | string | yes | The charge id, the mint's transaction id, or its client request id. Up to 128 characters of letters, digits, _ and -. |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | reference does not look like any of the three identifiers this route resolves. |
404 | object | No Pix charge with this reference. Deliberately the same answer for a charge another organization holds. |
409 | object | The reference matches more than one charge, because one value can sit in one row's transaction id and another row's client request id. Query by the charge id instead. |
500 | object | The charge status could not be read. |
502 | object | The provider consult did not answer, or it answered for a DIFFERENT cob than this charge and was refused rather than reported as this charge's state. Nothing about the charge was changed; retry later. details.reason names which of the two it was. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
amount_minor | integer | yes | — |
charge_id | string | yes | — |
client_request_id | string,null | yes | Null for a static cob. |
created_at | string (date-time) | yes | — |
currency | string | yes | — |
provider | object,null | yes | Null when the provider was not consulted: the charge is already terminal on our books, or it is a static cob carrying no identifier the consult accepts. |
settled_at | string,null (date-time) | yes | — |
status | string | yes | Our books: pending, settled or expired. Left open rather than enumerated because the value is carried as a plain string end to end. |
status_message | string | yes | Prose written for the caller, explaining what the combination above does and does not prove. |
transaction_id | string,null | yes | The provider transaction id recorded at mint. |
wallet_id | string | yes | — |
Example response
{
"charge_id": "charge_0000000000000000",
"status": "string",
"amount_minor": 1000,
"currency": "BRL",
"wallet_id": "wlt_0000000000000000",
"created_at": "2026-01-15T12:00:00.000Z",
"provider": {
"result": "ok",
"identity_verified": true
},
"status_message": "string"
}Example request
curl -X GET https://api.codespar.dev/v1/consumers/{consumerId}/pix/charges/{reference} \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/consumers/{consumerId}/pix/devolutions/{devolutionId}
A Pix devolution's state
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
consumerId | string | yes | — |
devolutionId | string | yes | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
404 | object | Not a known devolution for this consumer. The same opaque answer for a truly unknown id and for another tenant's. |
500 | object | The devolution status could not be read. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
amount_minor | integer | yes | — |
charge_id | string,null | yes | — |
client_code | string | yes | The server-minted idempotency correlation sent upstream. |
created_at | string (date-time) | yes | — |
currency | "BRL" | yes | — |
devolution_id | string | yes | — |
end_to_end_id | string | yes | — |
failure_reason | string,null | yes | — |
idempotent_replay | boolean | yes | Always false on this read. The field is meaningful on the create, not here. |
original_payment_id | string,null | yes | — |
payment_direction | "inbound" | "outbound" | yes | Which side of the rail the ORIGINAL payment was on. inbound: the consumer received it and the money goes back to its payer. outbound: the consumer sent it and the money returns to the consumer. |
provider_truth | "statement" | "unavailable" | "not_checked" | yes | Where the state above came from. not_checked: the row is already terminal. statement: the consumer's own account statement was read and settled the row if it showed the reversal. unavailable: the statement could not be fetched, so the recorded state is returned unchanged. A statement failure degrades this field; it never turns the read into an error. |
reason | string | yes | The Pix return code. |
return_identification | string,null | yes | — |
settled_at | string,null (date-time) | yes | — |
status | "pending" | "accepted" | "settled" | "failed" | "uncertain" | yes | The five values the table's CHECK constraint admits. |
status_message | string | yes | — |
wallet_id | string,null | yes | — |
Example response
{
"devolution_id": "devolution_0000000000000000",
"status": "pending",
"payment_direction": "inbound",
"end_to_end_id": "endtoend_0000000000000000",
"amount_minor": 1000,
"currency": "BRL",
"reason": "string",
"client_code": "string",
"created_at": "2026-01-15T12:00:00.000Z",
"idempotent_replay": true,
"status_message": "string",
"provider_truth": "statement"
}Example request
curl -X GET https://api.codespar.dev/v1/consumers/{consumerId}/pix/devolutions/{devolutionId} \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/consumers/{consumerId}/pix/receivements/{endToEndId}
Did this specific Pix land on this consumer's account
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
consumerId | string | yes | — |
endToEndId | string | yes | The payment's end-to-end id, the reference the payer's receipt carries. 16 to 64 alphanumeric characters. |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | endToEndId does not have the shape of an end-to-end id. |
404 | object | No receivement with this end-to-end id on this consumer's account. Also the answer when the movement exists but belongs to another account, which is what keeps a bearer identifier from probing across tenants. |
409 | object | The consumer has no active Pix funding source, so there is no account to bind the answer to. Checked BEFORE the provider is queried, so an unbindable consumer never causes a lookup on a bearer identifier. |
500 | object | The receivement status could not be read. |
502 | object | The provider consult did not answer, or answered in a shape this backend cannot tenant-bind or cannot classify. Fail-closed in all three cases; details.reason names which. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
amount_minor | integer,null | yes | Minor-unit truth from OUR charge row. Null when uncorrelated. |
charge_id | string,null | yes | — |
charge_status | string,null | yes | — |
correlation | "charge_matched" | "no_correlator_returned" | "no_charge_matched" | "charge_ambiguous" | "not_attempted" | yes | How this receivement was tied to a charge on our books. None of the empty outcomes proves a credit is not coming: the reconciler matches the account STATEMENT on a field this consult never returns. |
devolutions | array of object | yes | Our devolution rows keyed on this end-to-end id, whatever the movement type is. |
end_to_end_id | string | yes | — |
initiation_type | string,null | yes | — |
provider_amount | — | no | The provider's own representation of the amount, passed through unconverted. |
provider_transaction_id | string,null | yes | — |
provider_transaction_type | string | yes | The provider's discriminator, verbatim. |
received | boolean | yes | True only when the provider classifies the movement as the homologated inbound type. A movement of another type on the same account (a reversal of a Pix the consumer SENT, for one) answers false with the type named below. |
status_message | string | yes | — |
transaction_id_brcode | string,null | yes | — |
transaction_identification | string,null | yes | — |
wallet_credited | boolean | yes | — |
wallet_id | string,null | yes | — |
Example response
{
"end_to_end_id": "endtoend_0000000000000000",
"received": true,
"provider_transaction_type": "string",
"correlation": "charge_matched",
"wallet_credited": true,
"devolutions": [
{
"devolution_id": "devolution_0000000000000000",
"status": "string",
"amount_minor": 1000
}
],
"status_message": "string"
}Example request
curl -X GET https://api.codespar.dev/v1/consumers/{consumerId}/pix/receivements/{endToEndId} \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/consumers/{consumerId}/receipts
A consumer's agentic receipts
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
consumerId | string | yes | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
receipts | array of object | yes | — |
Example response
{
"receipts": [
{
"receipt_id": "receipt_0000000000000000",
"state": "paid",
"mandate": {
"id": "obj_0000000000000000",
"nonce": "string",
"scope": "string",
"currency": "BRL",
"sig": "string"
},
"quote": {},
"payment": {
"rail": "string",
"amount_minor": 1000,
"amount_authorized": "1000",
"amount_charged": "1000",
"amount_refunded": "1000",
"metering": {
"basis": "string",
"units": "string",
"unit_price": "string"
},
"sandbox": true,
"attempt_id": "attempt_0000000000000000",
"money_moved": true,
"at": "string"
},
"delivery": {
"result": "confirmed"
},
"chain": "string",
"receipt_sig": "string",
"exceptions": [
{
"code": "string",
"detail": "string",
"at": "string"
}
]
}
]
}Example request
curl -X GET https://api.codespar.dev/v1/consumers/{consumerId}/receipts \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/consumers/{id}
Read one titular, with the ids of what hangs off it
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
404 | object | Not Found |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
created_at | string | yes | — |
display_name | string,null | yes | — |
document | string,null | yes | — |
document_source | string,null | yes | — |
document_verified_at | string,null | yes | — |
id | string | yes | — |
metadata | object | yes | — |
person_type | string,null | yes | — |
status | "unverified" | "verified" | yes | — |
tax_type | string,null | yes | — |
Example response
{
"id": "obj_0000000000000000",
"metadata": {},
"status": "unverified",
"created_at": "string"
}Example request
curl -X GET https://api.codespar.dev/v1/consumers/{id} \
-H "Authorization: Bearer $CODESPAR_API_KEY"PATCH /v1/consumers/{id}
Update the mutable fields of a titular
Request body
| Field | Type | Required | Description |
|---|---|---|---|
display_name | string,null | no | — |
metadata | object | no | — |
tax_type | "PJ" | "MEI" | "ME" | "PF" | no | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | Bad Request — the body or query did not match the schema. |
404 | object | Not Found |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
created_at | string | yes | — |
display_name | string,null | yes | — |
document | string,null | yes | — |
document_source | string,null | yes | — |
document_verified_at | string,null | yes | — |
id | string | yes | — |
metadata | object | yes | — |
person_type | string,null | yes | — |
status | "unverified" | "verified" | yes | — |
tax_type | string,null | yes | — |
Example response
{
"id": "obj_0000000000000000",
"metadata": {},
"status": "unverified",
"created_at": "string"
}Example request
curl -X PATCH https://api.codespar.dev/v1/consumers/{id} \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"metadata": {},
"tax_type": "PJ"
}'GET /v1/consumers/{id}/wallet
The consumer's unified wallet, rolled up per currency
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | yes | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
consumer_id | string | yes | — |
currencies | array of object | yes | — |
Example response
{
"consumer_id": "csm_0000000000000000",
"currencies": [
{
"currency": "BRL",
"authorized_minor": 1,
"spent_minor": 1,
"available_minor": 1,
"funding_source_ids": [
"string"
],
"mandate_ids": [
"string"
]
}
]
}Example request
curl -X GET https://api.codespar.dev/v1/consumers/{id}/wallet \
-H "Authorization: Bearer $CODESPAR_API_KEY"