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.
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 signedLinks
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
}
}'| Field | What it sets |
|---|---|
items | What is owed. The total is computed by the server from the items, in BRL. No request field sets the amount |
surfaces | Where 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_days | Days from issuance to the charge's due date. The charge is a Pix with due date, also payable as a boleto |
payer_fields | Which 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_id | The 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_version | Your 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).
Payment links
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.
Related
- 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.
Connect Links
Hosted OAuth flow that lets your end users connect their own Stripe, Mercado Pago, Shopify, and other provider accounts, without you building or maintaining a connection UI.
Gate
Gate is the x402 gateway. Put an x402 paywall in front of any API or MCP server and get paid in USDC on Base. Share a gateway URL, charge agents per call, no signup, no card, no session.