Consents
4 operations under /v1/consents (POST GET): parameters, status codes, refusal bodies, and the same request in curl, HTTP, Python and TypeScript.
Base URL: https://api.codespar.dev
Operations below require a Bearer token unless their bar says No credential. See Authentication.
POST /v1/consents
https://api.codespar.dev/v1/consentsStart a hosted consent
Starts a hosted consent for a consumer mandate. The partner's backend calls this with its API key and gets a one-shot token carrying the intent the consumer is about to authorize (purpose, total and per-transaction caps, currency, mandate TTL and the merchant, withdrawal and DDA allowlists that will be signed into the mandate). The partner composes the URL of the hosted consent page from the token and hands it to the consumer.
The mandate is created server-side when the consent is submitted: the consumer's secret is provisioned, the mandate is signed with it, the funding source and the consent record are written and the token is consumed, in one transaction. The API key never signs — nothing a partner can call WITH A KEY produces a signed mandate.
surface decides who runs that ceremony, and it is fixed here, at creation. hosted (the default): hand consent_url to the consumer and our page does the rest. partner: you render your own screen and call GET /v1/consents/{token} and POST /v1/consents/{token}/submit yourself, both described in this document, and the submit must carry an attestation saying who witnessed the human and when. That block is signed into the mandate, which is what keeps the two surfaces distinguishable in an audit instead of collapsing into an IP address that would be your server's either way.
intent.merchant_allowlist defaults to ["*"], an explicit wildcard bounded by the caps, purpose and expiry; pass concrete Pix keys to narrow it. intent.withdrawal_allowlist and intent.dda_allowlist are deliberately NOT defaulted: absent means the mandate authorizes no cash-out and no DDA registration, which is a different thing from a wildcard, and "*" is refused in both. callback_url, when given, is returned to the hosted page at submit time so the partner's backend can receive the signed mandate.
The token is stamped with the credential's project, and that stamp is what scopes the funding source the consumer's submission later creates.
Requires the consents:write scope.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
agent_id | string | yes | — |
callback_url | string (uri) | no | — |
consumer_email_hint | string (email) | no | — |
intent | object | yes | — |
surface | "hosted" | "partner" | no | — |
Responses
| Status | Body | Description |
|---|---|---|
201 | object | Created. The consent is pending until the consumer signs on the hosted page or the token expires. |
400 | object | The body did not match the schema. details.issues carries the Zod issues; the refusals with their own message are a "*" entry in intent.withdrawal_allowlist and a intent.dda_allowlist entry that is not a CPF (11 digits) or a CNPJ (14 digits). |
Response 201
| Field | Type | Required | Description |
|---|---|---|---|
consent_url | string | yes | The hosted page for this token, composed from the server's configured base. Hand it to the consumer on the hosted surface. It is returned on partner too, where it is a usable fallback: the same token also answers on the page. |
expires_at | string | yes | ISO 8601. The token expires 24 hours after this call; a consumer opening the link after that sees an expired consent and nothing is created. This is the TOKEN's TTL, not the mandate's: the mandate's own TTL is intent.mandate_ttl_seconds, counted from the moment the consumer signs. |
surface | "hosted" | "partner" | yes | Echo of the requested surface, which is fixed for this token's life. hosted: the consumer signs on the page at consent_url and the submit must NOT carry an attestation. partner: you render the screen and call GET /v1/consents/{token} and POST /v1/consents/{token}/submit yourself, and the submit MUST carry attestation. |
token | string | yes | One-shot, ctk_-prefixed, unguessable. Put it in the URL of the hosted consent page and hand that URL to the consumer. It is consumed by the consumer's submission and cannot be replayed. |
curl -X POST https://api.codespar.dev/v1/consents \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "agt_0000000000000000",
"consumer_email_hint": "person@example.com",
"intent": {
"purpose": "string",
"cap_minor": 1,
"per_tx_cap_minor": 1,
"currency": "BRL",
"mandate_ttl_seconds": 0,
"merchant_allowlist": [
"*"
],
"merchant_pin_kind": "pix-key",
"withdrawal_allowlist": [
"string"
],
"dda_allowlist": [
"string"
],
"periodic_cap": {
"window": "day",
"cap_minor": 1
},
"slots": [
{
"currency": "BRL",
"rail": "string",
"cap_minor": 1,
"per_tx_cap_minor": 1
}
],
"display_name": "Example",
"intent_note": "string",
"shipping": {}
},
"callback_url": "https://example.com/hook",
"surface": "hosted"
}'POST /v1/consents HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEY
Content-Type: application/json
{
"agent_id": "agt_0000000000000000",
"consumer_email_hint": "person@example.com",
"intent": {
"purpose": "string",
"cap_minor": 1,
"per_tx_cap_minor": 1,
"currency": "BRL",
"mandate_ttl_seconds": 0,
"merchant_allowlist": [
"*"
],
"merchant_pin_kind": "pix-key",
"withdrawal_allowlist": [
"string"
],
"dda_allowlist": [
"string"
],
"periodic_cap": {
"window": "day",
"cap_minor": 1
},
"slots": [
{
"currency": "BRL",
"rail": "string",
"cap_minor": 1,
"per_tx_cap_minor": 1
}
],
"display_name": "Example",
"intent_note": "string",
"shipping": {}
},
"callback_url": "https://example.com/hook",
"surface": "hosted"
}import os
import requests
res = requests.post(
"https://api.codespar.dev/v1/consents",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
json={
"agent_id": "agt_0000000000000000",
"consumer_email_hint": "person@example.com",
"intent": {
"purpose": "string",
"cap_minor": 1,
"per_tx_cap_minor": 1,
"currency": "BRL",
"mandate_ttl_seconds": 0,
"merchant_allowlist": [
"*"
],
"merchant_pin_kind": "pix-key",
"withdrawal_allowlist": [
"string"
],
"dda_allowlist": [
"string"
],
"periodic_cap": {
"window": "day",
"cap_minor": 1
},
"slots": [
{
"currency": "BRL",
"rail": "string",
"cap_minor": 1,
"per_tx_cap_minor": 1
}
],
"display_name": "Example",
"intent_note": "string",
"shipping": {}
},
"callback_url": "https://example.com/hook",
"surface": "hosted"
},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/consents", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"agent_id": "agt_0000000000000000",
"consumer_email_hint": "person@example.com",
"intent": {
"purpose": "string",
"cap_minor": 1,
"per_tx_cap_minor": 1,
"currency": "BRL",
"mandate_ttl_seconds": 0,
"merchant_allowlist": [
"*"
],
"merchant_pin_kind": "pix-key",
"withdrawal_allowlist": [
"string"
],
"dda_allowlist": [
"string"
],
"periodic_cap": {
"window": "day",
"cap_minor": 1
},
"slots": [
{
"currency": "BRL",
"rail": "string",
"cap_minor": 1,
"per_tx_cap_minor": 1
}
],
"display_name": "Example",
"intent_note": "string",
"shipping": {}
},
"callback_url": "https://example.com/hook",
"surface": "hosted"
}),
});
const data = await res.json();const result = await cs.api.post("/v1/consents", {
body: {
agent_id: "agt_0000000000000000",
consumer_email_hint: "person@example.com",
intent: {
purpose: "string",
cap_minor: 1,
per_tx_cap_minor: 1,
currency: "BRL",
mandate_ttl_seconds: 0,
merchant_allowlist: [
"*"
],
merchant_pin_kind: "pix-key",
withdrawal_allowlist: [
"string"
],
dda_allowlist: [
"string"
],
periodic_cap: {
window: "day",
cap_minor: 1
},
slots: [
{
currency: "BRL",
rail: "string",
cap_minor: 1,
per_tx_cap_minor: 1
}
],
display_name: "Example",
intent_note: "string",
shipping: {}
},
callback_url: "https://example.com/hook",
surface: "hosted"
}
});{
"token": "string",
"expires_at": "string",
"surface": "hosted",
"consent_url": "https://example.com/hook"
}POST /v1/consents/init
https://api.codespar.dev/v1/consents/initStart a hosted consent
DEPRECATED alias of POST /v1/consents (ent#979), kept for two releases. Same handler, same required scope, same request and same responses; switch the path and nothing else changes. The canonical path is described in this document too.
Starts a hosted consent for a consumer mandate. The partner's backend calls this with its API key and gets a one-shot token carrying the intent the consumer is about to authorize (purpose, total and per-transaction caps, currency, mandate TTL and the merchant, withdrawal and DDA allowlists that will be signed into the mandate). The partner composes the URL of the hosted consent page from the token and hands it to the consumer.
The mandate is created server-side when the consent is submitted: the consumer's secret is provisioned, the mandate is signed with it, the funding source and the consent record are written and the token is consumed, in one transaction. The API key never signs — nothing a partner can call WITH A KEY produces a signed mandate.
surface decides who runs that ceremony, and it is fixed here, at creation. hosted (the default): hand consent_url to the consumer and our page does the rest. partner: you render your own screen and call GET /v1/consents/{token} and POST /v1/consents/{token}/submit yourself, both described in this document, and the submit must carry an attestation saying who witnessed the human and when. That block is signed into the mandate, which is what keeps the two surfaces distinguishable in an audit instead of collapsing into an IP address that would be your server's either way.
intent.merchant_allowlist defaults to ["*"], an explicit wildcard bounded by the caps, purpose and expiry; pass concrete Pix keys to narrow it. intent.withdrawal_allowlist and intent.dda_allowlist are deliberately NOT defaulted: absent means the mandate authorizes no cash-out and no DDA registration, which is a different thing from a wildcard, and "*" is refused in both. callback_url, when given, is returned to the hosted page at submit time so the partner's backend can receive the signed mandate.
The token is stamped with the credential's project, and that stamp is what scopes the funding source the consumer's submission later creates.
Requires the consents:write scope.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
agent_id | string | yes | — |
callback_url | string (uri) | no | — |
consumer_email_hint | string (email) | no | — |
intent | object | yes | — |
surface | "hosted" | "partner" | no | — |
Responses
| Status | Body | Description |
|---|---|---|
201 | object | Created. The consent is pending until the consumer signs on the hosted page or the token expires. |
400 | object | The body did not match the schema. details.issues carries the Zod issues; the refusals with their own message are a "*" entry in intent.withdrawal_allowlist and a intent.dda_allowlist entry that is not a CPF (11 digits) or a CNPJ (14 digits). |
Response 201
| Field | Type | Required | Description |
|---|---|---|---|
consent_url | string | yes | The hosted page for this token, composed from the server's configured base. Hand it to the consumer on the hosted surface. It is returned on partner too, where it is a usable fallback: the same token also answers on the page. |
expires_at | string | yes | ISO 8601. The token expires 24 hours after this call; a consumer opening the link after that sees an expired consent and nothing is created. This is the TOKEN's TTL, not the mandate's: the mandate's own TTL is intent.mandate_ttl_seconds, counted from the moment the consumer signs. |
surface | "hosted" | "partner" | yes | Echo of the requested surface, which is fixed for this token's life. hosted: the consumer signs on the page at consent_url and the submit must NOT carry an attestation. partner: you render the screen and call GET /v1/consents/{token} and POST /v1/consents/{token}/submit yourself, and the submit MUST carry attestation. |
token | string | yes | One-shot, ctk_-prefixed, unguessable. Put it in the URL of the hosted consent page and hand that URL to the consumer. It is consumed by the consumer's submission and cannot be replayed. |
curl -X POST https://api.codespar.dev/v1/consents/init \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "agt_0000000000000000",
"consumer_email_hint": "person@example.com",
"intent": {
"purpose": "string",
"cap_minor": 1,
"per_tx_cap_minor": 1,
"currency": "BRL",
"mandate_ttl_seconds": 0,
"merchant_allowlist": [
"*"
],
"merchant_pin_kind": "pix-key",
"withdrawal_allowlist": [
"string"
],
"dda_allowlist": [
"string"
],
"periodic_cap": {
"window": "day",
"cap_minor": 1
},
"slots": [
{
"currency": "BRL",
"rail": "string",
"cap_minor": 1,
"per_tx_cap_minor": 1
}
],
"display_name": "Example",
"intent_note": "string",
"shipping": {}
},
"callback_url": "https://example.com/hook",
"surface": "hosted"
}'POST /v1/consents/init HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEY
Content-Type: application/json
{
"agent_id": "agt_0000000000000000",
"consumer_email_hint": "person@example.com",
"intent": {
"purpose": "string",
"cap_minor": 1,
"per_tx_cap_minor": 1,
"currency": "BRL",
"mandate_ttl_seconds": 0,
"merchant_allowlist": [
"*"
],
"merchant_pin_kind": "pix-key",
"withdrawal_allowlist": [
"string"
],
"dda_allowlist": [
"string"
],
"periodic_cap": {
"window": "day",
"cap_minor": 1
},
"slots": [
{
"currency": "BRL",
"rail": "string",
"cap_minor": 1,
"per_tx_cap_minor": 1
}
],
"display_name": "Example",
"intent_note": "string",
"shipping": {}
},
"callback_url": "https://example.com/hook",
"surface": "hosted"
}import os
import requests
res = requests.post(
"https://api.codespar.dev/v1/consents/init",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
json={
"agent_id": "agt_0000000000000000",
"consumer_email_hint": "person@example.com",
"intent": {
"purpose": "string",
"cap_minor": 1,
"per_tx_cap_minor": 1,
"currency": "BRL",
"mandate_ttl_seconds": 0,
"merchant_allowlist": [
"*"
],
"merchant_pin_kind": "pix-key",
"withdrawal_allowlist": [
"string"
],
"dda_allowlist": [
"string"
],
"periodic_cap": {
"window": "day",
"cap_minor": 1
},
"slots": [
{
"currency": "BRL",
"rail": "string",
"cap_minor": 1,
"per_tx_cap_minor": 1
}
],
"display_name": "Example",
"intent_note": "string",
"shipping": {}
},
"callback_url": "https://example.com/hook",
"surface": "hosted"
},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/consents/init", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"agent_id": "agt_0000000000000000",
"consumer_email_hint": "person@example.com",
"intent": {
"purpose": "string",
"cap_minor": 1,
"per_tx_cap_minor": 1,
"currency": "BRL",
"mandate_ttl_seconds": 0,
"merchant_allowlist": [
"*"
],
"merchant_pin_kind": "pix-key",
"withdrawal_allowlist": [
"string"
],
"dda_allowlist": [
"string"
],
"periodic_cap": {
"window": "day",
"cap_minor": 1
},
"slots": [
{
"currency": "BRL",
"rail": "string",
"cap_minor": 1,
"per_tx_cap_minor": 1
}
],
"display_name": "Example",
"intent_note": "string",
"shipping": {}
},
"callback_url": "https://example.com/hook",
"surface": "hosted"
}),
});
const data = await res.json();const result = await cs.api.post("/v1/consents/init", {
body: {
agent_id: "agt_0000000000000000",
consumer_email_hint: "person@example.com",
intent: {
purpose: "string",
cap_minor: 1,
per_tx_cap_minor: 1,
currency: "BRL",
mandate_ttl_seconds: 0,
merchant_allowlist: [
"*"
],
merchant_pin_kind: "pix-key",
withdrawal_allowlist: [
"string"
],
dda_allowlist: [
"string"
],
periodic_cap: {
window: "day",
cap_minor: 1
},
slots: [
{
currency: "BRL",
rail: "string",
cap_minor: 1,
per_tx_cap_minor: 1
}
],
display_name: "Example",
intent_note: "string",
shipping: {}
},
callback_url: "https://example.com/hook",
surface: "hosted"
}
});{
"token": "string",
"expires_at": "string",
"surface": "hosted",
"consent_url": "https://example.com/hook"
}GET /v1/consents/{token}
https://api.codespar.dev/v1/consents/{token}Read a pending consent
Returns what a human needs to see before authorizing: the org, the agent and the intent that will be signed. No credential — the token in the URL is the authentication, and it is the same token on both surfaces.
The hosted page calls this to render itself. On the partner surface you call it to render your own screen, and surface in the response tells you which you are on.
It never returns a secret, a mandate or a provisioned id: nothing here signs anything. A token that was already used answers 409 and an expired one 410, so the screen can say which happened.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
token | string | yes | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | The consent is pending and these are its human-readable parts. |
404 | object | No such token. |
409 | object | The token was already submitted. One-shot by construction (a partial unique index on the pending state), so this is what a replay gets. |
410 | object | The token expired. Start a new consent. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
agent_id | string | yes | The agent the mandate will authorize. |
consumer_email_hint | string,null | yes | What the partner passed at creation, to help the consumer recognize the request. |
expires_at | string | yes | ISO 8601. After this the token answers 410. |
intent | object | yes | The intent as it was minted: purpose, caps, currency, mandate TTL and the allowlists that will be signed. Show it — this is what the human is authorizing. |
org_name | string | yes | The org's display name, for the screen. An organization when it cannot be read. |
rails_supported | array of string | yes | Legal values for rail on the submit below. |
surface | "hosted" | "partner" | yes | The surface this token was opened for. partner means the submit requires attestation; hosted means it refuses one. Read it before submitting rather than assuming: the org chose it when it created the consent. |
token | string | yes | — |
curl -X GET https://api.codespar.dev/v1/consents/{token} \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/consents/{token} HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/consents/{token}",
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/consents/{token}", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/consents/{token}", {
path: {
token: "string"
}
});{
"token": "string",
"org_name": "Example",
"agent_id": "agt_0000000000000000",
"consumer_email_hint": "person@example.com",
"intent": {},
"expires_at": "string",
"rails_supported": [
"string"
],
"surface": "hosted"
}POST /v1/consents/{token}/submit
https://api.codespar.dev/v1/consents/{token}/submitSubmit a consent and receive the signed mandate
Consumes the token and returns the signed consumer mandate. In one transaction: the consumer's secret is provisioned if this is their first consent, the funding source is created from provider_token (encrypted through the vault, never stored in the clear), the consent record is written with the IP and user-agent of THIS call, the mandate is signed with the consumer's secret and the token is consumed. No credential: the token is the authentication, and it is one-shot.
WHO may call it is decided by the surface the org chose at creation, and the two are not interchangeable:
hosted— the consumer's browser submits from our page.attestationis REFUSED (400attestation_not_accepted): our page witnessed the act, and the IP and user-agent on the consent record are the consumer's.partner— your server submits.attestationis REQUIRED (400attestation_required): the IP and user-agent recorded are your server's, so the only statement about the human is yours. It is signed into the mandate asconsent_attestationand cannot be edited afterwards without breaking the signature.
attestation.method says how the human authorized: partner_session (an authenticated session in your product), in_person, verified_code (a code YOU issued and verified), partner_biometric (YOU ran a biometric check at the moment of the act), or sandbox_fixture (no human authorized: a CodeSpar test surface signed a sandbox fixture; accepted ONLY for a test project, 403 attestation_sandbox_fixture_not_permitted on a live one). partner_biometric is not a flavour of partner_session: in a dispute a session says someone was logged in and a biometric says a body was present. We capture and verify no biometric ourselves — this records what YOU assert, sealed by the mandate signature so it cannot be edited afterwards; the evidence behind it stays with you. asserted_at is the instant you say they authorized, in Unix seconds. reference is your own record id, [A-Za-z0-9_-]{1,120}, and never personal data — it is joined into the signed string, so the character set is the schema's, not a suggestion.
attestation.evidence (optional) is what you OBSERVED of the act, signed with the rest of the mandate. channel is required and decides which other keys may be present, because a value a channel cannot expose was inferred, and an inferred value inside a signed record reads exactly like an observed one:
whatsapp,sms,email,llm_chat—contact,message_id,session_id,provider_ts. These hand you a conversation, not a connection.web_chat— the above plusip,user_agent,geo. A browser has no install, so nodevice_id_hash.app— the above plusdevice_id_hash.other— everything, because we cannot say what a channel we have not named exposes. An auditor readingotherknows exactly that.
A key outside its channel's set, or a key the object does not declare, is a 400 attestation_evidence_invalid with details.key naming it — never a silent drop. contact is the e-mail or E.164 phone the consumer acted from; it is hashed server-side into the signed contact_hash and never stored in the clear. device_id_hash you hash yourself (64 hex characters). geo is {country, region, city} and refuses coordinates.
method: "verified_code" additionally REQUIRES evidence.contact to carry a contact verification we ran for this consumer in the last 24h (POST /v1/consumers/{consumerId}/contact-verifications), or the submit is refused with 409 contact_verification_required and the token is left unspent. A rung that is only asserted is not stronger than the one below it.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
token | string | yes | — |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
attestation | object | no | — |
consumer_id | string | yes | — |
display_label | string | no | — |
provider_token | string | yes | — |
rail | "pix-consent" | "card-token" | "ted-debit-auth" | "usd-ach-debit" | "usdc-onchain" | yes | — |
Responses
| Status | Body | Description |
|---|---|---|
201 | object | The mandate is signed and stored, and the token is spent. Store the mandate and its signature: both are presented on every spend. |
400 | object | The body did not match the schema (invalid_body, with the Zod issues in details.issues), the attestation does not match the token's surface (attestation_required on partner without one, attestation_not_accepted on hosted with one), or the evidence carries a key its channel cannot expose or one the object does not declare (attestation_evidence_invalid, details.key). |
403 | object | attestation.method is sandbox_fixture and the consent's project is not a test project: a fixture attestation is accepted only in test. Nothing was signed. |
404 | object | No such token. |
409 | object | The token was already submitted, or a concurrent submit won the race (nothing was created twice: the loser rolls back whole); or method: "verified_code" was claimed without a contact verification for this consumer in the last 24h, in which case nothing was created at all and the token is still pending. |
410 | object | The token expired. Start a new consent. |
Response 201
| Field | Type | Required | Description |
|---|---|---|---|
callback_url | string,null | yes | The callback_url given at creation, echoed so the caller can post the mandate on. |
mandate | object | yes | The signed consumer mandate. Store it server-side with its signature; the agent presents both on every spend. |
mandate_id | string | yes | — |
signature | string | yes | HMAC of the mandate's canonical payload, hex. |
curl -X POST https://api.codespar.dev/v1/consents/{token}/submit \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"consumer_id": "csm_0000000000000000",
"rail": "pix-consent",
"provider_token": "string",
"display_label": "Example",
"attestation": {
"method": "partner_session",
"asserted_at": 0,
"reference": "string",
"evidence": {
"channel": "whatsapp",
"message_id": "message_0000000000000000",
"session_id": "ses_0000000000000000",
"provider_ts": 0,
"ip": "string",
"user_agent": "string",
"device_id_hash": "string",
"geo": {
"country": "string",
"region": "string",
"city": "string"
},
"contact": "string"
}
}
}'POST /v1/consents/{token}/submit HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEY
Content-Type: application/json
{
"consumer_id": "csm_0000000000000000",
"rail": "pix-consent",
"provider_token": "string",
"display_label": "Example",
"attestation": {
"method": "partner_session",
"asserted_at": 0,
"reference": "string",
"evidence": {
"channel": "whatsapp",
"message_id": "message_0000000000000000",
"session_id": "ses_0000000000000000",
"provider_ts": 0,
"ip": "string",
"user_agent": "string",
"device_id_hash": "string",
"geo": {
"country": "string",
"region": "string",
"city": "string"
},
"contact": "string"
}
}
}import os
import requests
res = requests.post(
"https://api.codespar.dev/v1/consents/{token}/submit",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
json={
"consumer_id": "csm_0000000000000000",
"rail": "pix-consent",
"provider_token": "string",
"display_label": "Example",
"attestation": {
"method": "partner_session",
"asserted_at": 0,
"reference": "string",
"evidence": {
"channel": "whatsapp",
"message_id": "message_0000000000000000",
"session_id": "ses_0000000000000000",
"provider_ts": 0,
"ip": "string",
"user_agent": "string",
"device_id_hash": "string",
"geo": {
"country": "string",
"region": "string",
"city": "string"
},
"contact": "string"
}
}
},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/consents/{token}/submit", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"consumer_id": "csm_0000000000000000",
"rail": "pix-consent",
"provider_token": "string",
"display_label": "Example",
"attestation": {
"method": "partner_session",
"asserted_at": 0,
"reference": "string",
"evidence": {
"channel": "whatsapp",
"message_id": "message_0000000000000000",
"session_id": "ses_0000000000000000",
"provider_ts": 0,
"ip": "string",
"user_agent": "string",
"device_id_hash": "string",
"geo": {
"country": "string",
"region": "string",
"city": "string"
},
"contact": "string"
}
}
}),
});
const data = await res.json();const r = await cs.api.response("post", "/v1/consents/{token}/submit", {
path: {
token: "string"
},
body: {
consumer_id: "csm_0000000000000000",
rail: "pix-consent",
provider_token: "string",
display_label: "Example",
attestation: {
method: "partner_session",
asserted_at: 0,
reference: "string",
evidence: {
channel: "whatsapp",
message_id: "message_0000000000000000",
session_id: "ses_0000000000000000",
provider_ts: 0,
ip: "string",
user_agent: "string",
device_id_hash: "string",
geo: {
country: "string",
region: "string",
city: "string"
},
contact: "string"
}
}
}
});
// r.status is one of the documented statuses (200, 403),
// each with its own body shape in r.data; nothing here throws on 403.
if (r.ok) {
console.log(r.data);
}{
"mandate_id": "mandate_0000000000000000",
"mandate": {},
"signature": "string",
"callback_url": "https://example.com/hook"
}Boletos you owe (DDA)
4 operations under /v1/consumers/{consumerId}/dda (GET POST DELETE): parameters, status codes, refusal bodies, and the same request in curl, HTTP, Python and TypeScript.
Mandates
5 operations under /v1/mandates (GET POST): parameters, status codes, refusal bodies, and the same request in curl, HTTP, Python and TypeScript.