Skip to main content

Consents

2 operations under /v1/consents (POST): parameters, status codes, refusal bodies, and the same request in curl, HTTP, Python and TypeScript.

5 min read
View MarkdownEdit on GitHub

Consents

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

Every operation below requires a Bearer token. See Authentication.

POST /v1/consents

POSThttps://api.codespar.dev/v1/consents

Start 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 consumer signs on the hosted page. The mandate is created server-side when the consumer submits there: 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, and the page's own calls are not operations of this document: nothing a partner can call with a key produces a signed mandate.

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

FieldTypeRequiredDescription
agent_idstringyes
callback_urlstring (uri)no
consumer_email_hintstring (email)no
intentobjectyes

Responses

StatusBodyDescription
201objectCreated. The consent is pending until the consumer signs on the hosted page or the token expires.
400objectThe 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

FieldTypeRequiredDescription
expires_atstringyesISO 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.
tokenstringyesOne-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.
Example request
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"
         ],
         "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"
     }'
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"
    ],
    "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"
}
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"
        ],
        "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"
    },
)
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"
      ],
      "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"
  }),
});

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"
      ],
      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"
  }
});
Example response 201
application/json
{
  "token": "string",
  "expires_at": "string"
}

POST /v1/consents/init

POSThttps://api.codespar.dev/v1/consents/init
Deprecated

Start 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 consumer signs on the hosted page. The mandate is created server-side when the consumer submits there: 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, and the page's own calls are not operations of this document: nothing a partner can call with a key produces a signed mandate.

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

FieldTypeRequiredDescription
agent_idstringyes
callback_urlstring (uri)no
consumer_email_hintstring (email)no
intentobjectyes

Responses

StatusBodyDescription
201objectCreated. The consent is pending until the consumer signs on the hosted page or the token expires.
400objectThe 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

FieldTypeRequiredDescription
expires_atstringyesISO 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.
tokenstringyesOne-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.
Example request
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"
         ],
         "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"
     }'
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"
    ],
    "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"
}
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"
        ],
        "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"
    },
)
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"
      ],
      "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"
  }),
});

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"
      ],
      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"
  }
});
Example response 201
application/json
{
  "token": "string",
  "expires_at": "string"
}
Consents | CodeSpar