Skip to main content

Overview

Base URL, authentication, the request lifecycle, errors and conventions for every operation in the API.

3 min read
View MarkdownEdit on GitHub

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.

What is a route here, and what is a meta-tool

Charging is a route. POST /v1/charges issues one, and the rail is a FIELD rather than a path: method: "pix" mints an immediate copy-and-paste string, and method: "boleto" with a due_date issues a cobrança com vencimento. Reading, listing and withdrawing live under the same tree, and so do the cart checkout and Pix devolutions.

These are not routes, and no route under another name does them: issuing a fiscal document (NF-e), booking a delivery, sending a WhatsApp, SMS or email. They are meta-tool actions — codespar_invoice, codespar_ship, codespar_notify — reached over MCP, through the SDK or from the CLI. Their schemas are public at https://api.codespar.dev/v1/meta-tools.json, with no credential.

The rule of thumb: what you configure and read is a route, and what fans out across a provider catalogue is a meta-tool. Where both exist for one job, the route is the narrower contract and the meta-tool is the one that chooses the provider.

↻ REQUEST LIFECYCLE
Where a call is refused, and by what
1 · CREDENTIAL
Bearer token
csk_ key or OAuth 2.1 access token · 401 if absent or unrecognised
→
2 · TENANT
org + project
resolved from the KEY, never from the body or the path
→
3 · SCOPE
the grant this route needs
403 before the route reads anything
→
4 · OPERATION
one of the 340 below
404 for anything outside your org, existing or not
the key prefix picks the environment · GET /v1/whoami answers steps 1 to 3 in one call

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.

What each status code means here lists every one the document declares, and the error reference lists the application codes inside the body, with the fix for each.

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 (20 operations) means a successful call moves funds. Can move money (5) means the call runs whatever tool you name, and that tool may be a paid one. Neither mark is a field in the document: under each one there is a sentence from that operation's own description, and that sentence is the evidence.

Every operation carries the same request in five forms: curl, HTTP (the literal request on the wire, which is what you build from in Go, Java, .NET or PHP), Python and TypeScript (requests and fetch, not a wrapper), and SDK where the published client has a method for that route.

Client libraries

Nothing here requires a library of ours. Every operation on this page is plain HTTP with a Bearer token, and the HTTP tab on each one is the request your own client sends — mandate signing is server-side, so there is no cryptography to reimplement and no language this API is easier in.

Where a library helps, it is optional: the SDK is the TypeScript client, codespar on PyPI is the Python one, 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.

Coverage

340 operations across 68 resource groups, every one of them generated from the document the API publishes at /v1/openapi.json. Each page carries that operation's real parameters, real status codes and real refusal bodies, because they come from the same file the server answers from.

If you cannot find an operation here, it is not described in the published document yet. Say so in a support request with what you were trying to do; that is how these pages get the next one.

The example values are placeholders. The published document carries no example values, so the bodies on these pages are built from each schema: the fields, the types and the nesting are exactly right, and "consumer_0000" is not an id you can call.

Overview | CodeSpar