Skip to main content

Collect

Direct charges, with no store or cart in between. A Collect link issues a Pix with due date or a boleto that a person pays on the hosted page or from the QR you deliver, a recurring offer charges a fixed monthly amount through Pix Automático, and a payment link takes x402/USDC from an agent.

5 min read
View MarkdownEdit on GitHub

Early access

Collect runs in Test projects. A Live project, or a Test project the deployment has not opened to Collect, gets 404 collect_disabled on every operation that would start something, with details.reason saying why (live, project_not_listed, switch_off). Reads of what a project already started stay open.

Collect is how your project charges a payer directly. You describe what is owed, publish a link, and the payer pays it: a person on the hosted page or from a Pix QR you deliver on WhatsApp, an agent through the link's 402. CodeSpar issues the charge at the bank, records the payment when the bank confirms it, and credits your project's account. It sends nothing to the payer; the link and the QR are material you deliver.

The routes are /v1/collect and /v1/payment-links. In the dashboard, Collect is the Collect screen of a project. Bill charges its monthly statements through Collect links.

The flow

POST /v1/collect/links ──▶ draft ──publish──▶ published ──▶ payer opens the page, or you deliver the QR
                                                   │ the bank issues a Pix with due date (30 s to 60 min)
                                                   │ the payer pays
                                                   │ the bank confirms: collect.payment.paid
                                                   └──▶ payment recorded, your account credited, receipt signed

A Collect link is one obligation: one link, one charge, one valid payment. You create it with its first draft and publish when it is ready. Nothing is shown to payers before the publish, and a published version never changes; a change is a new draft and a new publish.

curl -X POST https://api.codespar.dev/v1/collect/links \
  -H "Authorization: Bearer $CODESPAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "version": {
      "title": "Invoice 1208, instalment 1 of 2",
      "items": [{ "name": "Consulting, October", "quantity": 1, "unit_amount_minor": 118000 }],
      "surfaces": ["page", "whatsapp"],
      "due_in_days": 5
    }
  }'
FieldWhat it sets
itemsWhat is owed. The total is computed by the server from the items, in BRL. No request field sets the amount
surfacesWhere the link is paid: page (the hosted page), whatsapp (the Pix QR you deliver), agent (the 402 at the gateway, priced in USDC by usdc_price)
due_in_daysDays from issuance to the charge's due date. The charge is a Pix with due date, also payable as a boleto
payer_fieldsWhich payer fields the page asks for. A Pix with due date requires the payer's document (CPF or CNPJ) and address; prefill them on the link or let the page collect them
consumer_idThe receiving consumer: the payment settles into that consumer's account. Omitted, Collect uses the project's default receiver (PUT /v1/collect/receivers/default)
reference, reference_versionYour own id for the obligation and its version. The same pair always answers the same link (replay: true); a new version is created only when no earlier one can still be paid

A link moves through draft, published, paused and archived. Pause stops new attempts and the page says so; resume publishes again; archive is terminal and cancels the open charge at the bank first.

The link's url is the hosted page. On the whatsapp surface you issue an attempt yourself (POST /v1/collect/links/{linkId}/attempts) and read its material from GET /v1/collect/attempts/{attemptId}/artifact: the copia-e-cola, the QR as PNG, the amount, the due date and the page as fallback. The material exists once the bank has issued the charge; until then the route answers 409 artifact_not_ready with retry_after, and the event collect.attempt.ready tells you when to read it.

Payments

A payment is recorded only on the bank's confirmation, in the same transaction as the credit to your account, and it carries a signed receipt. GET /v1/collect/links/{linkId}/payments lists the link's payments with their status and the evidence behind each (confirmation). The event collect.payment.paid is the one that means money arrived; a notification without the bank's confirmation is not a payment.

A payment that arrives after the link's validity is an exception, not a credit. It is kept apart, collect.payment.exception fires, and the decision is yours before decide_by: keep it (POST /v1/collect/payments/{paymentId}/honor) or refund it (POST /v1/collect/payments/{paymentId}/refund). An undecided one is refunded.

Recurring offers

A link created with billing: "recurring" is an offer shared by many payers. Each payer adheres to it with Pix Automático: they pay the first charge now and authorize the next ones in their bank app, at a fixed monthly amount. The offer's page shows the terms and never a payer's QR; POST /attempts on the offer creates the payer's adhesion.

GET /v1/collect/links/{linkId}/subscriptions lists the adhesions, each with where the authorization stands, the next charge, the payments received and the failed charges. An adhesion is active once the payer's bank accepts the authorization. The outcomes stay distinct: authorization_cancelled (the payer or the bank), past_due (a failed cycle on a live authorization), authorization_expired (paid once, never authorized), and cancelling then cancelled when you cancel it (POST /v1/collect/subscriptions/{subscriptionId}/cancel). Archiving an offer whose adhesions keep charging is refused unless the body says {"subscriptions": "cancel"}.

The receiver of a recurring offer must be a legal person (CNPJ) with a registered name, in its own account. A receiver that does not qualify gets collect_recurring_receiver_not_eligible. Recurring offers run in Test; Live recurring is not open yet (recurrence_live_unavailable).

The earlier Collect surface is the payment link, /v1/payment-links: one shareable URL served by the gateway where an agent pays over x402/USDC and a person pays via Pix QR, verified before fulfillment, with the same receipt model. It stays available, with its own reference.

In Test

Nothing in Test moves real money. The charge is issued at the bank's sandbox, and the payer button pays it from the shared Test payer: POST /v1/collect/attempts/{attemptId}/test-pay for a link, POST /v1/collect/subscriptions/{subscriptionId}/test-pay for an adhesion's first payment. The payment then follows the same path as a real one: confirmation, record, credit, receipt.

Keys and surfaces

A key with every scope reaches all of Collect. A narrow key needs collect:read for the reads and collect:write to create, publish, pause, archive, issue an attempt or decide on a late payment.

Every operation above is in the Collect API reference, typed in the TypeScript SDK's REST client and derived into the CLI's codespar collect commands. From an agent, codespar_charge issues a single charge with a due date and its recurrence; links, offers and their payments are the project's own operations, not the agent's.

  • Bill: usage closed into one monthly statement, charged through a Collect link.
  • Gate: the x402 paywall for agents paying per call.
  • Triggers: collect.link.published, collect.attempt.ready, collect.attempt.failed, collect.payment.paid, collect.payment.exception, collect.refund.required.
  • Test mode: what a Test project can and cannot do.
Collect | CodeSpar