Skip to main content

Overview

Base URL, authentication, errors and conventions — plus what this document does not describe.

4 min read
View MarkdownEdit on GitHub

API reference

The CodeSpar API is REST over HTTPS: resource-oriented paths, JSON bodies, conventional status codes. Every operation on these pages is generated from the OpenAPI document served at https://api.codespar.dev/openapi.json — the same one a generated client reads — and every one of them can be run from its own page with your key.

Base URL

There is one. The key prefix picks the environment, not the host: csk_test_ and csk_live_ reach this same URL and act on different data. A call that seems to return nothing is often a live key reading a sandbox project, or the reverse.

Authentication

Every operation takes a bearer token.

GET /v1/whoami is the right first call, and the right first thing to run when something is not working: it answers whether the key is valid, which organization and project it is bound to, which environment it acts in, and which scopes it holds — four questions at once. An OAuth 2.1 access token from the flow advertised at /.well-known/oauth-authorization-server is accepted on the same header.

Errors

A refusal is explicit and carries a code. A call that would exceed a mandate cap fails rather than partially succeeding, and the failure names the cap.

Every response carries X-Request-Id, including failures. It is the single most useful thing to quote when you ask for help: it finds your exact call instead of a description of it.

2xxWorked
200The request worked.
201The thing was created. The body is the thing.
202Accepted and still settling. Poll the status operation, or subscribe to the event.
204It worked and there is nothing to return.
3xxRedirect
302A redirect, used by the hosted flows.
4xxYour request
400The request is malformed, or a field is wrong. The body names which.
401No credential, or one this API does not recognise.
402Payment required — the x402 paywall's own answer.
403The credential is valid and does not carry the scope, the role, or the mandate this call needs.
404Not found — including anything belonging to another organization. Existence is not probeable across the tenant boundary, so a resource that is not yours is answered exactly like one that never existed.
409A conflict with the state that already exists — a duplicate, or a transition the resource cannot make from where it is.
410Gone: it existed and was withdrawn.
413The payload is too large.
422Well-formed and refused on its meaning, not its shape.
424A dependency this call needs failed. Not your input.
429Too many requests. Back off.
5xxOur side, or an upstream
500Ours to fix. Quote the X-Request-Id.
501Declared and not implemented on this rail yet.
502An upstream provider answered badly.
503An upstream provider, or this service, is unavailable. Usually worth retrying.

The body of a refusal

⚠️ Two shapes coexist in this API, and a client that parses one as the other reads undefined where it expected a string. The newer routes answer:

{
  "error": {
    "code": "not_found",
    "message": "paywall not found"
  },
  "request_id": "req_01J8…"
}

Older routes answer a bare { "error": "not_found" }, sometimes with an issues array alongside it when the refusal is a validation failure. Each operation's page shows the shape that operation actually returns — read it there rather than assuming.

Conventions

Amounts are in minor units. 1990 is R$ 19,90. The crypto rails are the exception and take major units; each says so on its own page.

Idempotency is yours to pass. Where an operation moves money it takes an idempotency_key, and reusing the same key verbatim on a retry is what keeps a retry from becoming a second payment. A new key for the same operation is a new operation.

Asynchrony is normal on the money rails. A Pix cash-out answers PROCESSING when it was accepted and is settling; a cobrança com vencimento answers with no document until the clearing house registers it. Neither is a failure. Subscribe a trigger, or poll the operation's own status call.

A resource that is not yours is a 404, never a 403. Existence is not probeable across the tenant boundary.

How to read an operation

Under each operation's heading there is a bar: the verb in its own colour, then the full URL, with the {parameters} you have to fill in picked out. Marks can appear below it, and they do not all come from the same place, which is the part worth knowing.

Deprecated and No credential are read straight from the published document: the first is its own deprecated flag, the second is an operation that declares no security requirement at all.

Moves money and Can move money are not in the document. There is no field for them: no tag, no scope, nothing a generator can read as a fact. They come from a rule in scripts/api-reference/generate.mjs, and the rule has two halves. The mechanical half reads the document and flags a write as suspect when its body carries an amount field, its description uses a money verb, its path names a movement, or it lives under the wallet or facilitator tree. Suspicion on its own marks nothing: an amount field also matches operations that record a negotiated price or store a cap, and a money verb matches prose. The other half is a verdict per suspect, written by a person and justified by a sentence copied from that operation's own description; the generator refuses to run while any suspect lacks one, and refuses again if the sentence stops appearing in the description, so a verdict cannot outlive the text it rests on. Today 5 of the 213 operations carry Moves money and 3 carry Can move money, the second being operations that run whatever tool the caller names, which may be a paid one. Read the sentence under the mark: it is the evidence, not a summary.

The example is the same request twice, in curl and in TypeScript. A third tab appears only where the published client has a method for that route, which is a handful of session operations and no more: @codespar/sdk is a session client, not a REST client, so an SDK tab on the rest would be a method that does not exist.

Client libraries

The examples here are curl, and you do not have to write HTTP: the SDK is the TypeScript client, the CLI drives the same operations from a terminal, and the hosted MCP server exposes the meta-tools to an agent with no HTTP client at all.

Where the money verbs are, and they are not here

Charging someone, refunding, issuing or paying a boleto, invoicing and checkout are not REST routes. Measured against this document: /v1/charges, /v1/refunds, /v1/boletos, /v1/invoices and /v1/checkout do not exist, and neither does any route under another name that does those jobs.

They exist as meta-tool actions instead — codespar_charge, codespar_invoice, codespar_checkout, and codespar_pay with method=boleto or action=pix_devolution — reachable over MCP or through the SDK. A reader who comes here looking for how to take a payment will not find it on these pages, and that is a gap in the API rather than a gap in this reference. Until it closes, the meta-tools are where the money verbs live, and their schemas are published at https://api.codespar.dev/v1/meta-tools.json without a credential.

What IS here is the platform surface: consumers, wallets, mandates, connections, projects, sessions, triggers, DDA, Pix keys, receipts and the rest — the things you configure and read, as opposed to the things that move money.

What this document does not describe

These pages cover all 213 operations the published document describes, across 54 resource groups. They are complete with respect to that document and say nothing about surfaces it does not describe yet — silently, because a generator cannot report what its input never mentioned. When you cannot find an operation here, the right conclusion is "the spec does not describe it yet", not "it does not exist".

Every example on these pages is synthesised from the schema, not captured from a real call: the published document carries no example values. The shapes are right; the values are placeholders.

Overview | CodeSpar