---
title: codespar_wallet
description: The agent's governed funds. Read the balance and Pix key, read the wallet ledger, mint a Pix copia-e-cola to top the wallet up, or manage the account's Pix keys. Buy-side companion to codespar_pay.
---

import { Callout } from "fumadocs-ui/components/callout";
import { Tabs, Tab } from "fumadocs-ui/components/tabs";

<MetaToolHeader tool="codespar_wallet" />

This is the wallet the agent spends FROM. Fund it with `receive`, then spend with [`codespar_pay`](/docs/concepts/meta-tools/pay). It is distinct from [`codespar_ledger`](/docs/concepts/meta-tools/ledger), the double-entry books.

## Actions

<MetaToolActions tool="codespar_wallet" />

## Example

`action: "balance"`, the default: the spendable funds and the Pix key bound to the wallet.

<Split min={380}>
<SplitPane label="The same call, four ways">

<Tabs items={["MCP", "TypeScript", "Python", "CLI"]}>
<Tab value="MCP">

```json title="arguments"
{
  "name": "codespar_wallet",
  "arguments": {
    "action": "balance",
    "consumer_id": "consumer_0000"
  }
}
```

</Tab>
<Tab value="TypeScript">

```ts
const result = await session.execute("codespar_wallet", {
  action: "balance",
  consumer_id: "consumer_0000"
});
```

</Tab>
<Tab value="Python">

```python
result = session.execute("codespar_wallet", {
  "action": "balance",
  "consumer_id": "consumer_0000"
})
```

</Tab>
<Tab value="CLI">

```bash
codespar tool codespar_wallet \
  -i '{
       "action": "balance",
       "consumer_id": "consumer_0000"
     }'
```

</Tab>
</Tabs>

</SplitPane>
<SplitPane label="Result">

```json title="result"
{
  "wallet_id": "wlt_0000000000000000",
  "balances": [
    {
      "currency": "BRL",
      "balance_minor": 1000,
      "available_minor": 1000,
      "updated_at": "2026-09-10T12:00:00.000Z"
    }
  ],
  "funding_sources": [
    {
      "funding_source_id": "cfs_0000000000000000",
      "rail": "pix-celcoin",
      "currency": "BRL",
      "status": "active",
      "provider": "celcoin",
      "provider_balance_minor": 999999,
      "provider_error": null,
      "as_of": "2026-09-10T12:00:01.000Z",
      "pix_key": "a1b2c3d4-0000-4000-8000-000000000000",
      "pix_key_type": "EVP",
      "account_masked": "****1234"
    }
  ]
}
```

`balances[]` is the balance: one entry per currency (`currency`, `balance_minor`, `available_minor`, `updated_at`), read from the same row `GET /v1/wallets/:id` returns and the figure every spend is validated against. `wallet_id` is `null` and `balances` is empty when the consumer has no wallet in this project yet; `balance` never creates one.

`funding_sources[]` lists the project's funding sources, each with `provider` (`celcoin`), `provider_balance_minor` (the provider's live balance, for reconciliation only, never the figure to decide a spend on), `provider_error` and `as_of`, plus the registered Pix key (`pix_key`, `pix_key_type`, `account_masked`). When the provider could not be read, `provider_balance_minor` is `null` and `provider_error` says why: `provider_unavailable` (asked, no readable answer) or `source_inactive` (the source is not active or has no account, so the provider was not asked); `balances[]` is unaffected either way. A funding source no longer carries `balance_minor`: that name used to hold the provider figure and read as the balance, and it was removed on purpose.

</SplitPane>
</Split>

## When to use

- **Before a spend**, to know whether the wallet can cover it: `balance`.
- **To fund the wallet**: `receive` mints a Pix copia-e-cola (static by default, dynamic with `dynamic: true`) that a payer settles; the credit lands when the inbound webhook confirms. Show the code to the payer and re-read `balance` afterwards.
- **To reconcile**: `statement` is the wallet ledger (funds, holds, debits), newest first, up to `limit` entries. With a period (`from` and `to`, or `days`) it answers the statement the dashboard shows instead: one line per operation, the period's opening, inflows, outflows and closing, and `next_cursor` for the next page.
- **To manage the account's Pix keys** from a conversation: `pix_keys_list`, `pix_keys_register`, `pix_keys_delete` (see [Pix keys](#pix-keys) below). None of them moves money.

Every action is scoped to one consumer; `consumer_id` defaults to the session user and is the same id [`codespar_shop`](/docs/concepts/meta-tools/shop) and `codespar_pay` use.

## Arguments

| Field | Type | Required | Description |
|---|---|---|---|
| `action` | `string` | No | `balance` \| `statement` \| `receive` \| `pix_keys_list` \| `pix_keys_register` \| `pix_keys_delete` \| `receipt_document` \| `statement_document`. Defaults to `balance`. |
| `consumer_id` | `string` | No | Whose wallet; defaults to the session user |
| `amount` | `number` | No | Top-up amount in minor units (centavos for BRL), for `receive` |
| `description` | `string` | No | Charge description shown to the payer (`receive`) |
| `dynamic` | `boolean` | No | `receive`: mint a dynamic copia-e-cola (location URL) instead of a static QR. Default `false` |
| `limit` | `number` | No | `statement`: lines per page. With a period, 1..200, default 200; without one, the latest raw ledger entries, 1..100, default 20 |
| `days` | `number` | No | `statement`: the last N days up to today (1..92), instead of `from` and `to` |
| `cursor` | `string` | No | `statement` with a period: the `next_cursor` of the previous page |
| `key_id` | `string` | For `pix_keys_delete` | The key to delete, as `pix_keys_list` answered it (`pk_…`); never the key value |
| `confirm` | `boolean` | No | `pix_keys_delete`: `true` only after the person confirmed in the conversation; absent or `false` answers `requires_confirmation` and sends nothing |
| `key_type` | `string` | No | `pix_keys_register`, live only: `EVP` \| `CPF` \| `CNPJ` \| `EMAIL` \| `PHONE`. Ignored in test (always a random key) |
| `key` | `string` | No | `pix_keys_register`, live only: the key value for every type but `EVP` |
| `receipt_id` | `string` | For `receipt_document` | The receipt (`rcpt_…`) of the payment, as `codespar_pay` or the payments list answered it |
| `format` | `string` | No | `receipt_document`: `pdf` (default) or `png` (the same page as an image, to share) |
| `from` | `string` | For `statement_document`; optional for `statement` | First day of the period, `YYYY-MM-DD` (America/São Paulo). With a period, `statement` answers one line per operation (payment, receipt, fund, fee, reversal; reserves are not lines), the period's opening, inflows, outflows and closing as the dashboard statement shows them, and `next_cursor` |
| `to` | `string` | For `statement_document`; optional for `statement` | Last day, inclusive; not after today; at most 92 days after `from` |

## Errors and what to do

| Error | Cause | What to do |
|---|---|---|
| `invalid_args` | An unknown `action`. Nothing is read or minted. | Fix the call against the Arguments table. |
| A wallet with no funding source | The consumer has no payment account yet (in live, an account comes from [`codespar_kyc`](/docs/concepts/meta-tools/kyc) `onboarding`). | Onboard the consumer first. In the test environment the sandbox wallet ships pre-connected. |
| `pix_key_only_key` | `pix_keys_delete` on the account's only key: the account keeps at least one. Nothing is sent. | Register another key first, then delete this one. |
| `pix_key_account_blocked`, `customer_account_suspended` | The account's space or binding is closed, or the customer account is paused; `details.reason` is `space_closed`, `binding_closed` or `blocked_paused`. Nothing is sent. | The keys do not change until the account is resumed or bound again. `pix_keys_list` still answers while paused. |
| `no_provider_account` | The consumer named has no account this project can act on. | Pass the consumer whose account it is (a test project's own account is `sbx_<projectId>`), or onboard the consumer. |
| `project_access_call_cap` | A test project's call budget for the window is used up. Nothing is sent. | Wait for the window; the response names it. |

## Money and mandate

Nothing on this tool moves money by itself. `receive` creates an instrument (the copia-e-cola) that a payer settles later; the wallet is credited only when the inbound webhook confirms the payment. Spending out of the wallet happens on `codespar_pay`, and every spend is mandate-gated server-side (per-currency caps, per-transaction caps, allowlists, expiry). The wallet holds the funds; the [mandate](/docs/concepts/mandates) holds the permission.

## Related

- [`codespar_pay`](/docs/concepts/meta-tools/pay): spend out of the wallet
- [`codespar_shop`](/docs/concepts/meta-tools/shop): buy at real stores; the checkout's Pix is paid from this wallet
- [`codespar_kyc`](/docs/concepts/meta-tools/kyc): `onboarding` provisions the account this wallet is funded from
- [Wallets concept](/docs/concepts/wallets): account wallets vs consumer wallets, invariants, reconciliation
- [`cs.wallet.pixKeys`](/docs/api/sdk/client#wallet): the same Pix key operations over the REST routes, in the SDK
- [Consumer wallet API](/docs/api/reference/wallets#consumer-wallets-multi-slot-mandate-wallet): REST endpoints, including slot transfer
- [Shopping Agent cookbook](/docs/cookbooks/shopping-agent): the balance a shopper's agent spends from

## Notes

**The multi-slot wallet.** A consumer's wallet is one wallet with per-currency slots (for example `BRL` and `USDC`) minted from a single mandate signature. There is no FX inside the wallet: each slot has its own cap and settled-spend ledger, and the payee type routes a payment to the matching slot (a URL or `0x` address routes to `USDC`; a Pix key or copia-e-cola routes to `BRL`). Moving balance between slots is a REST operation, documented in the [consumer wallet API](/docs/api/reference/wallets#consumer-wallets-multi-slot-mandate-wallet).

**Fund, check, spend.** `receive` with an `amount` → the payer settles the copia-e-cola → the webhook credits the wallet → `balance` shows the funds → `codespar_pay` spends them under the signed mandate. The settlement's receipt is the Control Record, mandate, payment and delivery bound in one signed document:

<ReceiptCard />

### Pix keys

The account's Pix keys, shaped for a conversation. `pix_keys_list` answers each key as `key_id`, `key_type` and `key_tail` (its last four characters), with `count`, `can_register` and `can_delete`; the full key never travels in a result, the person copies it on the dashboard page `panel_url` names (the Wallet page of the project, or the customer account's page). `pix_keys_register` registers one key: in the test environment always a random key (`EVP`), whatever `key_type` says; in live, `key_type` and `key` as the REST route takes them. `pix_keys_delete` takes a `key_id` from the list and `confirm`: without `confirm: true` it answers `status: "requires_confirmation"` with the key's tail and the consequence sentence (`consequence`, in Portuguese: once deleted, the key no longer points to this account), and sends nothing; the agent asks the person and calls again with `confirm: true`.

```json title="pix_keys_list result"
{
  "action": "pix_keys_list",
  "status": "ok",
  "consumer_id": "sbx_prj_0000000000000000",
  "account_masked": "••••27",
  "keys": [{ "key_id": "pk_0000000000000000abcd", "key_type": "EVP", "key_tail": "e0e4" }],
  "count": 1,
  "can_register": true,
  "register_type": "EVP",
  "can_delete": false,
  "delete_reason": "only_key",
  "panel_url": "/dashboard/projects/prj_0000000000000000/wallets"
}
```

The rules the dashboard panel applies hold here too. The account keeps at least one key (`can_delete: false` with `delete_reason: "only_key"`, and `pix_key_only_key` on a delete). A customer account paused in CodeSpar still lists and refuses the writes (`blocked_paused`); a closed binding or a closed space refuses both (`binding_closed`, `space_closed`). `status: "not_found"` (not this account's key now) and `status: "already_deleted"` (the bank says the entry was already gone) are answers, never errors, so a repeated deletion is harmless. Every bank call for a test project's own account counts in the project's call budget, as the panel's calls do.

`consumer_id` defaults to the session user; in a test project whose session user has no account of its own, to the project's own account (`sbx_<projectId>`), the one the Wallet page shows. The same three operations exist as REST routes (`/v1/consumers/:consumerId/pix-keys`), in the SDK ([`cs.wallet.pixKeys`](/docs/api/sdk/client#wallet)) and in the CLI (`codespar pix-keys`); those answer the key value, because the caller there is the account's operator and not a model reading a tool result.

### Documents

Documents on demand, generated from confirmed data and never from a new operation. `receipt_document` takes the `receipt_id` of a payment and answers the reference of its comprovante: `filename`, `content_type`, `sha256` (the hash of the structured data the page was rendered from, the same value the REST route sends in `X-CodeSpar-Document-Sha256`) and `path`, the authenticated API path of the bytes (`/v1/consumers/receipts/{id}/document?format=pdf|png`); the chat and the dashboard turn that path into a download, and nothing in the result is a public link. `format: "png"` answers the same page as an image, for a conversation. Before the payment is confirmed the action answers `status: "refused"` with `document_not_final`: the receipt reflects the confirmed state and the data available, not a promise. `statement_document` takes `from` and `to` (`YYYY-MM-DD`, at most 92 days, `to` not after today) and answers the reference of the account statement (extrato) as an A4 PDF: `totals` with `opening_minor`, `inflows_minor`, `outflows_minor`, `closing_minor` and `reserved_minor`, where the first four close the identity `opening + inflows − outflows = closing` (asserted on the server against an independent reading of the ledger; a period that does not close answers `statement_not_reconciled`, never a wrong page) and the reserves (holds) stay apart from it. A test project's documents carry the Test mark once, in the header; a statement is always `pdf`.

```json title="receipt_document result"
{
  "action": "receipt_document",
  "status": "ok",
  "document": {
    "kind": "receipt",
    "format": "pdf",
    "filename": "comprovante-rcpt_0000000000000000.pdf",
    "content_type": "application/pdf",
    "sha256": "9f2c…",
    "path": "/v1/consumers/receipts/rcpt_0000000000000000/document?format=pdf",
    "environment": "test",
    "receipt_id": "rcpt_0000000000000000",
    "amount_minor": 12500,
    "currency": "BRL"
  }
}
```

The same two documents exist as REST routes (`GET /v1/consumers/receipts/{id}/document`, `GET /v1/account/statement`), in the SDK ([`cs.receipts.document`](/docs/api/sdk/client#receipts), [`cs.account.statement`](/docs/api/sdk/client#account)), in the CLI (`codespar receipts document`, `codespar account statement`) and, in the stdio server, as the [`codespar_documents`](/docs/api/sdk/providers/mcp#documents-on-demand-codespar_documents) tool; those answer the bytes, because the caller there is the account's operator. The renderer is the platform's own headless browser, with no network and no scripts; while it is down the routes answer 503 `document_renderer_unavailable` and the dashboard keeps the download links in place.
