Account
12 operations under /v1/account (GET POST): parameters, status codes, refusal bodies, and the same request in curl, HTTP, Python and TypeScript.
Base URL: https://api.codespar.dev
Every operation below requires a Bearer token. See Authentication.
GET /v1/account/agent-activity
https://api.codespar.dev/v1/account/agent-activityBRL spent and received per agent, over a window
One row per agent of the organization plus any agent id this project's entries are attributed to, most spent first. spent_minor and received_minor (money received only, as in the summary) are BRL over the window, with the summary's geometry: the last 24 hours for 24h, the last 7 or 30 São Paulo calendar days for 7d and 30d. window defaults to 30d. last_activity_at is the newest entry attributed to the agent in this project, in any currency and at any time, and null when there is none. tool_calls counts the tool calls made in this project over the window by the sessions the agent opened (agent_id at session creation); a session opened without an agent counts for nobody. spent_within_mandate_minor is the part of spent_minor the mandate itself allowed: every debit except those a person approved above the mandate's signed per-transaction cap.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
window | "24h" | "7d" | "30d" | no | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | The query did not match the schema. details.issues carries the Zod issues. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
agents | array of object | yes | — |
currency | "BRL" | yes | — |
window | "24h" | "7d" | "30d" | yes | — |
curl -X GET https://api.codespar.dev/v1/account/agent-activity \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/account/agent-activity HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/account/agent-activity",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/account/agent-activity", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/account/agent-activity");{
"window": "24h",
"currency": "BRL",
"agents": [
{
"agent_id": "agt_0000000000000000",
"spent_minor": "1000",
"spent_within_mandate_minor": "1000",
"received_minor": "1000",
"last_activity_at": "2026-01-15T12:00:00.000Z",
"tool_calls": 0
}
]
}GET /v1/account/balances
https://api.codespar.dev/v1/account/balancesAccount balances, per currency
The sum of the balance caches of every non-closed wallet in the caller's project, one row per currency. held_minor is balance_minor − available_minor, the money open holds reserve. open_holds counts HOLD entries that no release or debit settles yet, paired by metadata.hold_ref, by withdrawal_id for owner withdrawals, or by attempt_id — the same pairing the hold sweeper uses.
Scoped to the organization AND the project: another project's wallets are in no sum.
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
as_of | string (date-time) | yes | — |
currencies | array of object | yes | — |
project_id | string | yes | — |
curl -X GET https://api.codespar.dev/v1/account/balances \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/account/balances HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/account/balances",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/account/balances", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/account/balances");{
"project_id": "prj_0000000000000000",
"currencies": [
{
"currency": "BRL",
"balance_minor": "1000",
"available_minor": "1000",
"held_minor": "1000",
"open_holds": 0
}
],
"as_of": "2026-01-15T12:00:00.000Z"
}POST /v1/account/fund/sandbox
https://api.codespar.dev/v1/account/fund/sandboxCredits test balance, in BRL, in a TEST project.
Add test balance (test projects only)
Credits test balance, in BRL, in a TEST project. Nothing is sent to any bank or provider; rail is the label the person picked. The entry is a FUND marked test_funding: true on GET /v1/account/ledger, and it is never counted as money received.
target picks the wallet. account (the default) is the project's account wallet, created on first use: the account Wallet shows it, but no agent spends from it. consumer is the consumer wallet a mandate spend reserves and debits: mandate_id's consumer, else consumer_id, else the one consumer the organization's active BRL mandates name; none or several answer test_funding_target_required with the candidates. Naming mandate_id or consumer_id implies consumer. A consumer wallet created here starts with exactly the amount funded.
At most 1000000 centavos per call. idempotency_key is required: the same key with the same amount and wallet answers the first entry with 200 and idempotent_replay: true; with another amount or wallet, idempotency_key_conflict. An admin or owner performs it from the dashboard; an API key is refused with 403 admin_session_required, whatever its scopes.
A Test project with its own bound provider account (payments active) is credited THERE instead: the amount goes to the bound account, claimed before it is sent and copied into the binding consumer's wallet ledger (consumer_id names it), with no platform total. An earlier credit still in flight or whose result is unknown blocks the next (sandbox_funding_open_claim); a platform guard's refusal before the send answers sandbox_funding_refused (422), a provider refusal sandbox_funding_failed.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
amount_minor | integer | yes | BRL centavos, at most TEST_FUNDING_MAX_MINOR. |
consumer_id | string | no | — |
idempotency_key | string | yes | — |
mandate_id | string | no | — |
rail | "pix" | "ted" | no | — |
target | "account" | "consumer" | no | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
201 | object | Credited. |
400 | object | The body did not match the schema, or names a mandate or consumer with target: account. |
403 | object | The acting member is below admin, the dashboard forwarded no member, or the credential is an API key (admin_session_required: this is administrative, whatever the key's scopes). |
404 | object | mandate_id names no mandate of this organization. |
409 | object | fund_requires_test_project: the project is live, nothing is written. test_funding_target_required: no target and not exactly one candidate (details.candidates). idempotency_key_conflict: the key already funded another amount or another wallet. sandbox_funding_open_claim: a bound project's earlier credit is still open; nothing was sent. |
422 | object | test_funding_amount_over_cap: above the per-call cap; details.cap_minor says how much. sandbox_funding_refused: a bound project's credit was refused by a platform guard before it was sent (details.reason); nothing was sent. |
500 | object | The credit did not land; nothing was written. On a bound project, wallet_credit_failed: the account was credited and its ledger copy failed. |
502 | object | A bound project's credit could not be executed at the provider. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
amount_minor | string | yes | — |
balance | object | yes | — |
client_code | string,null | no | A bound customer account's credit: the clientCode sent to the provider, also the FUND's external_ref. |
consumer_id | string,null | yes | The consumer whose wallet was funded; null for the account wallet. |
currency | "BRL" | yes | — |
deposit_id | string,null | no | A bound customer account's credit: the provider's id of that credit. |
entry_id | string | yes | The FUND's wallet_ledger id, as GET /v1/account/ledger lists it. |
idempotent_replay | boolean | yes | — |
mandate_id | string,null | yes | — |
target | "account" | "consumer" | yes | — |
test_funding | true | yes | — |
wallet_id | string | yes | — |
curl -X POST https://api.codespar.dev/v1/account/fund/sandbox \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount_minor": 1000,
"idempotency_key": "string",
"rail": "pix",
"target": "account",
"mandate_id": "mandate_0000000000000000",
"consumer_id": "csm_0000000000000000"
}'POST /v1/account/fund/sandbox HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEY
Content-Type: application/json
{
"amount_minor": 1000,
"idempotency_key": "string",
"rail": "pix",
"target": "account",
"mandate_id": "mandate_0000000000000000",
"consumer_id": "csm_0000000000000000"
}import os
import requests
res = requests.post(
"https://api.codespar.dev/v1/account/fund/sandbox",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
json={
"amount_minor": 1000,
"idempotency_key": "string",
"rail": "pix",
"target": "account",
"mandate_id": "mandate_0000000000000000",
"consumer_id": "csm_0000000000000000"
},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/account/fund/sandbox", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"amount_minor": 1000,
"idempotency_key": "string",
"rail": "pix",
"target": "account",
"mandate_id": "mandate_0000000000000000",
"consumer_id": "csm_0000000000000000"
}),
});
const data = await res.json();const r = await cs.api.response("post", "/v1/account/fund/sandbox", {
body: {
amount_minor: 1000,
idempotency_key: "string",
rail: "pix",
target: "account",
mandate_id: "mandate_0000000000000000",
consumer_id: "csm_0000000000000000"
}
});
// r.status is one of the documented statuses (200, 403, 422),
// each with its own body shape in r.data; nothing here throws on 403.
if (r.ok) {
console.log(r.data);
}{
"entry_id": "entry_0000000000000000",
"wallet_id": "wlt_0000000000000000",
"target": "account",
"consumer_id": "csm_0000000000000000",
"mandate_id": "mandate_0000000000000000",
"amount_minor": "1000",
"currency": "BRL",
"test_funding": true,
"idempotent_replay": true,
"balance": {
"balance_minor": "1000",
"available_minor": "1000"
},
"client_code": "string",
"deposit_id": "deposit_0000000000000000"
}POST /v1/account/fund/sandbox/initial
https://api.codespar.dev/v1/account/fund/sandbox/initialCredits 10000 centavos of test money to the project's account wallet
Grant the initial test balance (test projects only, once)
Credits 10000 centavos of test money to the project's account wallet, once per project: the first call answers 201 with granted: true, every later call, and the loser of two concurrent calls, answers 200 with granted: false and the same entry. The amount, the wallet and the idempotency reference are the server's, so the route takes no body (send none, or {}). The entry is a FUND with test_funding_kind: initial_balance on GET /v1/account/ledger and is never counted as money received. No agent spends from the account wallet. To add more, POST /v1/account/fund/sandbox.
A Test project with its own bound provider account (payments active) is credited nothing here: the account's initial balance is credited once by its provisioning, and this route reads it. Confirmed: 200, granted: false, the credit's entry and amount, and initial_credit. Still on its way: 409 sandbox_funding_open_claim with details.initial_credit. Off: 200 with entry_id null and amount_minor "0". A Test project the provisioning reaches and not yet bound gets no simulated balance meanwhile: 409 sandbox_funding_open_claim, initial_credit.state preparing_account.
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
201 | object | Granted now. |
400 | object | A body other than {} was sent. |
403 | object | The acting member is below admin, the dashboard forwarded no member, or the credential is an API key (admin_session_required: this is administrative, whatever the key's scopes). |
409 | object | fund_requires_test_project: the project is live, nothing is written. sandbox_funding_open_claim: the project's initial balance is still being prepared or added by its provisioning (details.initial_credit); nothing was written. |
422 | object | A bound project's credit was refused by a platform guard before it was sent (details.reason); nothing was sent. |
500 | object | The credit did not land; nothing was written. Retrying is safe. |
502 | object | A bound project's credit could not be executed at the provider. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
amount_minor | string | yes | — |
balance | object | yes | — |
currency | "BRL" | yes | — |
entry_id | string,null | yes | Null only for a bound project whose initial credit is off. |
granted | boolean | yes | True on the call that credited it; false when it already had been, and always for a bound project. |
initial_credit | object | no | A bound project's: the provisioning's initial credit of its account. |
target | "account" | yes | — |
test_funding | true | yes | — |
wallet_id | string,null | yes | — |
curl -X POST https://api.codespar.dev/v1/account/fund/sandbox/initial \
-H "Authorization: Bearer $CODESPAR_API_KEY"POST /v1/account/fund/sandbox/initial HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.post(
"https://api.codespar.dev/v1/account/fund/sandbox/initial",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/account/fund/sandbox/initial", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const r = await cs.api.response("post", "/v1/account/fund/sandbox/initial");
// r.status is one of the documented statuses (200, 403, 422),
// each with its own body shape in r.data; nothing here throws on 403.
if (r.ok) {
console.log(r.data);
}{
"entry_id": "entry_0000000000000000",
"wallet_id": "wlt_0000000000000000",
"target": "account",
"amount_minor": "1000",
"currency": "BRL",
"test_funding": true,
"granted": true,
"balance": {
"balance_minor": "1000",
"available_minor": "1000"
},
"initial_credit": {
"state": "preparing_account",
"step": "waiting",
"reason": "string",
"amount_minor": "1000",
"origin": "provisioning",
"parts": [
{
"origin": "provisioning",
"amount_minor": "1000",
"entry_id": "entry_0000000000000000"
}
],
"entry_id": "entry_0000000000000000",
"confirmed_at": "string"
}
}POST /v1/account/fund/sandbox/refill
https://api.codespar.dev/v1/account/fund/sandbox/refillcrediting only the difference to the account wallet
Refill the test balance (test projects only, confirmed)
Brings the project's test balance (available + held, every wallet) back up to 10000 centavos, crediting only the difference to the account wallet; a balance already there answers 200 with refilled: false and credits nothing. At most 3 refills per São Paulo calendar day per project; the credit and the quota use are one row in one transaction, and a refill that credits nothing uses no quota. Adicionar fundos is a separate action and never counts. An explicit, confirmed act of a person: the body must be { "confirm": true }. The entry is test funding (test_funding_kind: refill) and never money received. Admin or owner, from the dashboard; an API key is refused with 403 admin_session_required.
A Test project with its own bound provider account (payments active) refills THAT account: its balance is read at the provider and only the difference to the target is credited there, claimed before it is sent, with NO daily quota (refill.daily_limit and left_today null). An unreadable balance credits nothing (502 test_funding_failed).
Request body
| Field | Type | Required | Description |
|---|---|---|---|
confirm | true | yes | The person confirmed the refill; anything else is refused. |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
201 | object | Refilled. |
400 | object | The body is not { "confirm": true }; nothing was written. |
403 | object | The acting member is below admin, the dashboard forwarded no member, or the credential is an API key (admin_session_required: this is administrative, whatever the key's scopes). |
409 | object | fund_requires_test_project: the project is live, nothing is written. sandbox_funding_open_claim: a bound project's earlier credit is still open; nothing was sent. |
422 | object | A bound project's credit was refused by a platform guard before it was sent (details.reason); nothing was sent. |
429 | object | The day's refills are used up; details.resets_at says when the next one is allowed (also Retry-After). |
500 | object | The credit did not land; nothing was written. Retrying is safe. |
502 | object | A bound project's refill: the account's balance could not be read (test_funding_failed), or the credit could not be executed at the provider (sandbox_funding_failed). |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
add_funds | object | yes | — |
amount_minor | string | yes | What was credited: the difference up to the target, or 0. |
balance | object | yes | — |
currency | "BRL" | yes | — |
entry_id | string,null | yes | — |
environment | "live" | "test" | yes | — |
mode | "sandbox_float" | "account" | yes | test_balance_mode of the project; live is always sandbox_float. |
refill | object | yes | — |
refilled | boolean | yes | False when the balance was already at the target: nothing was credited, no quota used. |
test_funding | true | yes | — |
wallet_id | string | yes | — |
curl -X POST https://api.codespar.dev/v1/account/fund/sandbox/refill \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"confirm": true
}'POST /v1/account/fund/sandbox/refill HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEY
Content-Type: application/json
{
"confirm": true
}import os
import requests
res = requests.post(
"https://api.codespar.dev/v1/account/fund/sandbox/refill",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
json={
"confirm": True
},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/account/fund/sandbox/refill", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"confirm": true
}),
});
const data = await res.json();const r = await cs.api.response("post", "/v1/account/fund/sandbox/refill", {
body: {
confirm: true
}
});
// r.status is one of the documented statuses (200, 403, 422),
// each with its own body shape in r.data; nothing here throws on 403.
if (r.ok) {
console.log(r.data);
}{
"environment": "live",
"mode": "sandbox_float",
"balance": {
"total_minor": "1000",
"available_minor": "1000",
"held_minor": "1000"
},
"refill": {
"target_minor": "1000",
"daily_limit": 1000,
"used_today": 0,
"left_today": 0,
"resets_at": "2026-01-15T12:00:00.000Z",
"room_minor": "1000"
},
"add_funds": {
"max_per_call_minor": "1000",
"counts_toward_refills": false
},
"refilled": true,
"entry_id": "entry_0000000000000000",
"wallet_id": "wlt_0000000000000000",
"amount_minor": "1000",
"currency": "BRL",
"test_funding": true
}GET /v1/account/ledger
https://api.codespar.dev/v1/account/ledgerThe project's ledger, every wallet merged
Every ledger entry of every wallet in the caller's project (closed wallets included: the ledger is history), newest first by entry id. Page with before set to the previous page's next_before; next_before is null on the last page.
hold_ref returns that hold and every release, debit and fee that settles it — an id that is not a hold of this project returns no entries.
category returns the entries stamped with that spend category at write time: recebivel on money received, fornecedor on a payable's payment, compra on a codespar_shop purchase, and otherwise what the agent declared on codespar_pay. It is never inferred from the description, so an entry no writer categorised is in no category and is listed only without the filter.
source and phase are the writer's own stamps, as written: they name an entry no writer described, e.g. consumer-payments + settle-fund is a payment's settlement FUND. A phase is only unique within its source; both are open vocabularies and null where unstamped.
proposal_id is the payment proposal the entry was booked for: a pay proposal's, read from its attempt id (<proposal>_t<n>_hold|_debit); a receive proposal's, on the FUND of the Pix charge it sent. Only when that proposal is in this org and project; the proposal_id filter returns those entries.
reverses, on a REVERSE entry, is the id of the entry it reverses when that entry is in this project; null on every other kind.
agent_id is resolved through the entry's consumer mandate, else, on the FUND of a Pix charge a receive proposal sent, that proposal's agent, else the wallet's agent. balance_after_minor is the running BOOKED balance of the project in the entry's currency, over every entry and not only the filtered ones: holds and releases leave it unchanged, as they leave balance_minor unchanged. At the newest entry it equals the sum of balance_minor over the project's wallets in that currency.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
agent_id | string | no | — |
before | string | no | — |
category | "compra" | "fornecedor" | "assinatura" | "recebivel" | no | — |
currency | string | no | — |
hold_ref | string | no | — |
kind | "fund" | "hold" | "release" | "debit" | "reconcile" | "reverse" | "fee" | no | — |
limit | integer | no | — |
mandate_id | string | no | — |
proposal_id | string | no | — |
since | string (date-time) | no | — |
until | string (date-time) | no | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | The query did not match the schema. details.issues carries the Zod issues. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
entries | array of object | yes | — |
next_before | string,null | yes | — |
curl -X GET https://api.codespar.dev/v1/account/ledger \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/account/ledger HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/account/ledger",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/account/ledger", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/account/ledger");{
"entries": [
{
"id": "obj_0000000000000000",
"wallet_id": "wlt_0000000000000000",
"currency": "BRL",
"kind": "fund",
"amount_minor": "1000",
"posted_at": "2026-01-15T12:00:00.000Z",
"mandate_id": "mandate_0000000000000000",
"agent_id": "agt_0000000000000000",
"agent_display_name": "Example",
"origin_kind": "agent",
"description": "string",
"rail": "string",
"external_ref": "string",
"hold_ref": "string",
"receipt_id": "receipt_0000000000000000",
"authorization": "string",
"category": "compra",
"test_funding": true,
"test_funding_kind": "initial_balance",
"proposal_id": "proposal_0000000000000000",
"reverses": "string",
"source": "string",
"phase": "string",
"executor": "codespar_simulator",
"balance_after_minor": "1000"
}
],
"next_before": "string"
}GET /v1/account/payments
https://api.codespar.dev/v1/account/paymentsThe project's payments, newest first
One row per spend attempt the payment lifecycle claimed in the caller's project, on every door (by mandate id, signed envelope, MCP, payment link, an approved execution), with its status: settled is paid and on the ledger (debit_entry_id), failed provably moved nothing, uncertain is held for reconciliation, the rest are in flight. A spend refused at a gate before any money step leaves no attempt and is not listed. agent_id is the agent of the mandate the payment was spent under. Page with before set to the previous page's next_before, an opaque cursor; next_before is null on the last page.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
agent_id | string | no | — |
before | string | no | — |
limit | integer | no | — |
mandate_id | string | no | — |
status | "claimed" | "dispatching" | "dispatched" | "settle_failed" | "settled" | "uncertain" | "failed" | no | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | The query did not match the schema, or before is not a cursor this route issued. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
next_before | string,null | yes | — |
payments | array of object | yes | — |
curl -X GET https://api.codespar.dev/v1/account/payments \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/account/payments HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/account/payments",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/account/payments", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/account/payments");{
"payments": [
{
"attempt_id": "attempt_0000000000000000",
"mandate_id": "mandate_0000000000000000",
"agent_id": "agt_0000000000000000",
"agent_display_name": "Example",
"amount_minor": "1000",
"currency": "BRL",
"rail": "string",
"payee": "string",
"label": "Example",
"status": "claimed",
"failure_code": "string",
"receipt_id": "receipt_0000000000000000",
"debit_entry_id": "debitentry_0000000000000000",
"executor": "codespar_simulator",
"created_at": "2026-01-15T12:00:00.000Z",
"updated_at": "2026-01-15T12:00:00.000Z"
}
],
"next_before": "string"
}GET /v1/account/sessions
https://api.codespar.dev/v1/account/sessionsThe project's sessions, as the activity list draws them
Each session with its agent, origin, tool calls, reviews and money, newest first, one read per page. agent_id is the agent the session was created as, else the Playground scene's agent. origin_kind is playground for a Playground session, else person when a dashboard person opened it, else agent when an agent is named, else api. opened_by_user_id is that person's Clerk user id on any origin, null when an API key or token opened the session. tool_calls.denied counts approvals of the session a person denied; review counts its approvals pending and decided. amount_minor sums the BRL debited by payments made in the session (a spend that sent session_id, or a payment made through a tool of the session), and is null when there is none: it is never estimated. total counts the sessions matching the filters, cursor aside. Page with before set to the previous page's next_before, an opaque cursor.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
agent_id | string | no | — |
before | string | no | — |
limit | integer | no | — |
origin | "agent" | "person" | "playground" | "api" | no | — |
since | string (date-time) | no | — |
status | "active" | "closed" | "error" | no | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | The query did not match the schema, or before is not a cursor this route issued. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
next_before | string,null | yes | — |
sessions | array of object | yes | — |
total | integer | yes | Sessions matching the filters, cursor aside. |
curl -X GET https://api.codespar.dev/v1/account/sessions \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/account/sessions HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/account/sessions",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/account/sessions", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/account/sessions");{
"sessions": [
{
"id": "obj_0000000000000000",
"status": "string",
"created_at": "2026-01-15T12:00:00.000Z",
"closed_at": "2026-01-15T12:00:00.000Z",
"agent_id": "agt_0000000000000000",
"agent_display_name": "Example",
"origin_kind": "agent",
"opened_by_user_id": "openedbyuser_0000000000000000",
"tool_calls": {
"count": 1,
"errors": 0,
"denied": 0
},
"mandate_id": "mandate_0000000000000000",
"amount_minor": "1000",
"review": {
"pending": 0,
"decided": 0
},
"title": "Example",
"scene": "string",
"conversation_id": "conversation_0000000000000000",
"executor": "codespar_simulator"
}
],
"next_before": "string",
"total": 0
}GET /v1/account/spenders
https://api.codespar.dev/v1/account/spendersThe mandates that can spend from this project
One row per mandate whose funding source belongs to the caller's project, active or paused and not expired, newest first: who may spend from this project's wallet. A mandate over another project's source is not listed, and neither is an agent of the organization with no mandate here. In a Test project with its own bound account, the Shop's mandates count only when signed over that account (one over another source is not in force there). usage is the mandate's own, from the same sums its cap gates run: spent_minor overall and period_spent_minor in the current window of its periodic cap.
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
spenders | array of object | yes | — |
curl -X GET https://api.codespar.dev/v1/account/spenders \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/account/spenders HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/account/spenders",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/account/spenders", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/account/spenders");{
"spenders": [
{
"mandate_id": "mandate_0000000000000000",
"agent_id": "agt_0000000000000000",
"agent_display_name": "Example",
"agent_status": "string",
"display_name": "Example",
"status": "active",
"currency": "BRL",
"rail": "string",
"cap_minor": "1000",
"per_tx_cap_minor": "1000",
"periodic_cap": {
"window": "day",
"cap_minor": "1000"
},
"expires_at": "2026-01-15T12:00:00.000Z",
"usage": {
"spent_minor": "1000",
"period_spent_minor": "1000"
}
}
]
}GET /v1/account/statement
https://api.codespar.dev/v1/account/statementThe account statement (extrato) of a period, as PDF or one page as PNG
The statement of the project's account over [from, to] (calendar days in America/Sao_Paulo, inclusive, at most 92 days, to not after today): holder, document, institution, agency and account; opening balance, inflows, outflows and closing balance over the BOOKED balance (the ledger kinds that move it: fund, debit, fee, reverse); reserves (holds open at the end of the period) shown apart and outside the balance identity; one row per balance-moving entry, newest first; A4, paginated, the column header on every page.
closing = opening + inflows - outflows is asserted in code against an independent aggregate: a period that does not reconcile answers 500 statement_not_reconciled and no document. The wallet set is the one the account balances sum (a Test project with a bound account: that account's wallet), so the closing balance at to = today equals the account's booked balance.
format=png&page=N answers ONE page of the same statement as an image (1-based, 25 lines a page, at most 8 pages; the PDF carries every line), the pages the hosted MCP connector attaches, so a client that does not render an embedded PDF reads it. Its X-CodeSpar-Document-Pages header says how many pages there are, and a page out of range answers 404 statement_page_not_found with details.pages.
Read-only; nothing is cached. The Wallet's CSV export is unchanged and separate.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
currency | string | no | The ledger currency (default BRL). |
format | "pdf" | "png" | no | pdf (default): the whole period. png: ONE page of the statement as an image, chosen by page, the same pages the hosted MCP connector attaches (25 lines each, at most 8 pages; the PDF carries every line). |
from | string | yes | First day of the period, YYYY-MM-DD, America/Sao_Paulo. |
page | integer | no | With format=png: the page, 1-based. The answer's X-CodeSpar-Document-Pages header says how many there are. |
to | string | yes | Last day, inclusive; not after today; at most 92 days after from. |
Responses
| Status | Body | Description |
|---|---|---|
200 | — | The statement (PDF), or one page of it (PNG, with X-CodeSpar-Document-Pages). |
400 | object | format is neither pdf nor png (document_format_invalid); page is not a whole number of 1 or more (statement_page_invalid); or the period is malformed, from is after to, to is after today, or the span exceeds the maximum (statement_period_invalid, details.reason). |
404 | object | With format=png: page is beyond the statement's pages (details.pages). |
500 | object | The ledger of the period does not reconcile; logged, nothing served. |
503 | object | The renderer (the hosted browser worker) is not configured or did not answer. Retriable; Retry-After is set. |
curl -X GET https://api.codespar.dev/v1/account/statement \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/account/statement HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/account/statement",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/account/statement", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/account/statement", {
query: {
from: "2026-01-15",
to: "2026-01-15"
}
});GET /v1/account/summary
https://api.codespar.dev/v1/account/summaryBRL money in, agent spend, tool calls and sessions over a window
BRL only. received_minor sums the funding entries stamped as money received (category recebivel): test funding and the fund legs that mirror a payment on its way out are not received. A TEST project's body also carries, apart, the test funding the account received (initial test balance, Adicionar fundos, refills; never a transfer between the project's wallets): test_funding_minor in the totals, in previous and in each series bucket; it is not a receipt rail and never appears in received_by_rail. A live project's body has no such field. spent_by_agents_minor sums debits attributed to an agent. The window is N whole buckets ending with the current, partial one: 24 hours for 24h, 7 or 30 São Paulo calendar days for 7d and 30d. series has exactly those N buckets, oldest first, and sums to the two totals; previous covers the N buckets immediately before. received_by_rail groups that received funding by rail (pix, boleto, ted): metadata.rail, or for an entry written before the rail was stamped, the rail its source fixes (a BolePix charge-in: its Collect payment's rail, else pix); null where neither says. tool_calls and sessions are counts over the same window and scope (tool calls by called_at, sessions by creation); sessions.active_now counts the sessions open at the time of the read that were created or made a tool call in the last 15 minutes, whatever the window: nothing closes an idle session, so an open session idle for longer is not counted. sessions.active_idle_ms states that threshold (900000). window defaults to 7d.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
window | "24h" | "7d" | "30d" | no | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | The query did not match the schema. details.issues carries the Zod issues. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
currency | "BRL" | yes | — |
previous | object | yes | — |
received_by_rail | array of object | yes | Received funding by rail (pix, boleto, ted): metadata.rail, or for an entry written before the rail was stamped, the rail its source fixes (a BolePix charge-in: its Collect payment's rail, else pix). Null where neither says: money received only. Test funding is not a receipt rail and never appears here; a TEST project carries it in test_funding_minor. |
received_minor | string | yes | — |
series | array of object | yes | — |
sessions | object | yes | Sessions created in the window; active_now = sessions still open (status 'active') at the time of the read that were created, or made a tool call, in the last 15 minutes, whatever the window. A session stays 'active' until it is closed and nothing expires it, so an open session idle for longer is not counted. |
spent_by_agents_minor | string | yes | — |
test_funding_minor | string | no | TEST projects only: the test funding the account received (initial test balance, Adicionar fundos, refills; never a transfer between the project's wallets). Never part of received_minor. Absent in a live project. |
tool_calls | object | yes | session_tool_calls rows with called_at in the window; errors = status 'error'. Counts, not money. |
window | "24h" | "7d" | "30d" | yes | — |
curl -X GET https://api.codespar.dev/v1/account/summary \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/account/summary HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/account/summary",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/account/summary", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/account/summary");{
"window": "24h",
"currency": "BRL",
"received_minor": "1000",
"spent_by_agents_minor": "1000",
"test_funding_minor": "1000",
"previous": {
"received_minor": "1000",
"spent_by_agents_minor": "1000",
"test_funding_minor": "1000"
},
"series": [
{
"bucket_start": "2026-01-15T12:00:00.000Z",
"received_minor": "1000",
"spent_by_agents_minor": "1000",
"test_funding_minor": "1000"
}
],
"received_by_rail": [
{
"rail": "string",
"received_minor": "1000"
}
],
"tool_calls": {
"total": 0,
"errors": 0
},
"sessions": {
"total": 0,
"active_now": 0,
"active_idle_ms": 900000
}
}GET /v1/account/test-balance
https://api.codespar.dev/v1/account/test-balanceThe test balance, the refill quota and the Adicionar fundos cap
One read for both test-credit actions. balance.total_minor is available + held over every wallet of the project, the number a refill compares to its target; refill is the day's quota (São Paulo calendar day) and the room a refill would credit now; add_funds is the separate per-call cap, which has no cumulative cap and never counts toward the refills. A Test project with its own bound provider account has no refill quota: refill.daily_limit and refill.left_today are null.
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
add_funds | object | yes | — |
balance | object | yes | — |
environment | "live" | "test" | yes | — |
mode | "sandbox_float" | "account" | yes | test_balance_mode of the project; live is always sandbox_float. |
refill | object | yes | — |
curl -X GET https://api.codespar.dev/v1/account/test-balance \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/account/test-balance HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/account/test-balance",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/account/test-balance", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/account/test-balance");{
"environment": "live",
"mode": "sandbox_float",
"balance": {
"total_minor": "1000",
"available_minor": "1000",
"held_minor": "1000"
},
"refill": {
"target_minor": "1000",
"daily_limit": 1000,
"used_today": 0,
"left_today": 0,
"resets_at": "2026-01-15T12:00:00.000Z",
"room_minor": "1000"
},
"add_funds": {
"max_per_call_minor": "1000",
"counts_toward_refills": false
}
}Wallets
15 operations under /v1/wallets (GET POST DELETE): parameters, status codes, refusal bodies, and the same request in curl, HTTP, Python and TypeScript.
Payables
15 operations under /v1/payables (GET POST PATCH): parameters, status codes, refusal bodies, and the same request in curl, HTTP, Python and TypeScript.