---
title: codespar_invoice
description: Fiscal documents. Issue, read, or amend an NF-e or NFS-e in Brazil, or a consumer invoice in Mexico, Argentina and Colombia; the fiscal partner and Bling in Brazil, Facturapi, AFIP and Siigo elsewhere.
---

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

<MetaToolHeader tool="codespar_invoice" />

<Callout title="An unknown action is refused, not guessed" type="info">
Measured 2026-09-11: a typo in `action` (`"isue"`, `"emitir"`) is refused by name with `invalid_args`, and the error lists `issue | status | amend`. Nothing is dispatched, so no fiscal document is emitted.

This note used to say the opposite, and it was right when it was written: until 2026-09-09 an unrecognised `action` fell through into the emission path, so `action: "emitir"` issued a real document with legal effect and a cancellation window. [ent#1178](https://github.com/codespar/codespar-enterprise/issues/1178) closed that, and this page carried the warning for two days after the fix.

One thing did not change and is deliberate: **omitting `action` still issues.** Emission is the historical default and the schema promises it. The vocabulary is closed against typos, not against silence.
</Callout>

## Actions

<MetaToolActions tool="codespar_invoice" />

## Example

`action: "issue"` of an NFS-e (a service invoice), the default action.

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

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

```json title="arguments"
{
  "name": "codespar_invoice",
  "arguments": {
    "type": "nfse",
    "action": "issue",
    "recipient": {
      "name": "Example Buyer",
      "document": "00000000000",
      "email": "buyer@example.com"
    },
    "items": [
      {
        "description": "Example service",
        "quantity": 1,
        "unit_price": 10000
      }
    ]
  }
}
```

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

```ts
const result = await session.execute("codespar_invoice", {
  type: "nfse",
  action: "issue",
  recipient: {
    name: "Example Buyer",
    document: "00000000000",
    email: "buyer@example.com"
  },
  items: [
    {
      description: "Example service",
      quantity: 1,
      unit_price: 10000
    }
  ]
});
```

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

```python
result = session.execute("codespar_invoice", {
  "type": "nfse",
  "action": "issue",
  "recipient": {
    "name": "Example Buyer",
    "document": "00000000000",
    "email": "buyer@example.com"
  },
  "items": [
    {
      "description": "Example service",
      "quantity": 1,
      "unit_price": 10000
    }
  ]
})
```

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

```bash
codespar tool codespar_invoice \
  -i '{
       "type": "nfse",
       "action": "issue",
       "recipient": {
         "name": "Example Buyer",
         "document": "00000000000",
         "email": "buyer@example.com"
       },
       "items": [
         {
           "description": "Example service",
           "quantity": 1,
           "unit_price": 10000
         }
       ]
     }'
```

</Tab>
</Tabs>

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

Result shape: see runtime. The tool document says `status` returns the document's fiscal state (`autorizada`, `cancelada`, ...) and that an `amend` result indicates which mechanism applied (CC-e or cancel and reissue), and names no other field. The published document is [`/v1/meta-tools.json`](https://api.codespar.dev/v1/meta-tools.json).

</SplitPane>
</Split>

## When to use

- **Services** (SaaS, consulting, anything service-driven): `type: "nfse"`, the Brazilian Nota Fiscal de Serviços.
- **Products**: `type: "nfe"`. Issuance needs the operator's A1 certificate, state tax registration and per-item ICMS classification stamped in the dashboard first (see Notes).
- **Outside Brazil**: `type: "nfci"`, the consumer-invoice rail. It routes by country to Facturapi for the Mexican CFDI 4.0, AFIP for the Argentine Factura A/B/C, and Siigo for the Colombian factura electrónica. Connect the provider for your country first.
- **After a settlement**: the usual chain is charge → wait for settlement → invoice → ship → notify. The [Webhook Listener cookbook](/docs/cookbooks/webhook-listener) issues the invoice from the settlement event.
- **Reading or correcting**: `status` reads the fiscal state by `invoice_id`; `amend` writes a correction letter (CC-e) while the SEFAZ amendment window is open, or cancels and reissues as a substitute (tipo 3) once it is not, and the result says which applied.

## Arguments

| Field | Type | Required | Description |
|---|---|---|---|
| `type` | `string` | Yes | `nfe`, `nfse` or `nfci` |
| `action` | `string` | No | `issue` (emit, the default) \| `status` (read the fiscal state) \| `amend` (correct in place via CC-e, or cancel and reissue). See the warning above about unknown values. |
| `recipient` | `object` | For `issue` | Recipient details: name, document, email |
| `items` | `array` | For `issue` | Line items |
| `dueDate` | `string` | No | Due date, ISO 8601 |
| `invoice_id` | `string` | For `status`, `amend` | The existing document's id to read or amend |
| `correction` | `string` | For `amend` (CC-e) | Correction text for an in-window correction letter |
| `reason` | `string` | For `amend` | Why the document is being amended; drives correction letter versus cancel-and-reissue |

## Errors and what to do

| Error | Cause | What to do |
|---|---|---|
| `invalid_args` | `items` missing or empty (the dispatch also accepts `products` as an alias), or `recipient` absent when the connection has no default buyer. | Fix the call against the Arguments table. |
| `status: "rejected"` with a SEFAZ code | The tax authority refused the NF-e or NFS-e (a validation on the buyer's document, the service code, the item classification). | Surface the code to the operator. Do not retry blindly: a corrected document is a new issuance, and an uncorrected retry is refused again. |
| Latency around 2s | Production SEFAZ authorization at peak load; issuance is synchronous from the agent's perspective. | Wait for the result; do not fire a second `issue` for the same order. |

## Money and mandate

No money moves on this tool. It emits, reads and amends fiscal documents, which are irreversible acts with the tax authority: an authorized NF-e can be corrected or cancelled and reissued, never silently replaced. There is no mandate gate on issuance; the operator's fiscal credentials (see Notes) are what authorize it, so an agent that can reach this tool can issue documents in the operator's name. Keep the `action` value exact (see the warning at the top).

## Related

- [`codespar_charge`](/docs/concepts/meta-tools/charge): the settlement that usually precedes the invoice
- [`codespar_ship`](/docs/concepts/meta-tools/ship) and [`codespar_notify`](/docs/concepts/meta-tools/notify): the next steps in the sell-side chain
- [E-Commerce Checkout cookbook](/docs/cookbooks/ecommerce-checkout): charge → invoice → ship → notify
- [Webhook Listener cookbook](/docs/cookbooks/webhook-listener): issue the invoice from a settlement event
- [Glossary](/docs/glossary): SEFAZ, NF-e, NFS-e, CFDI

## Notes

**Sandbox status.** NFS-e issuance is validated against the provider sandbox today; production SEFAZ authorization is on the roadmap.

**Rails, measured 2026-09-11 against the catalog.** `nfse` routes to the fiscal partner or Bling (BRL, BR). `nfe` routes to the fiscal partner (BRL, BR; needs the A1 certificate, state tax registration and per-item ICMS classification). `nfci` is the one that answers the question this paragraph used to leave open: it carries all three non-Brazilian lines, selected by country — Facturapi for CFDI 4.0 (MXN, MX), AFIP for Factura A/B/C (ARS, AR), Siigo for factura electrónica (COP, CO).

The earlier page named `type: "invoice"` for Stripe Invoicing. There is no catalog line for that value, so it came back `eligibility_empty` for every tenant while `nfci`, which works, went unmentioned. The dead value is recorded in the API repository ([ent#1280](https://github.com/codespar/codespar-enterprise/issues/1280)).

**Operator setup.** The operator pre-stamps fiscal credentials in `/dashboard/auth-configs`: NFS-e needs the fiscal partner's API key and company id (service-rail tenants can stop here); NF-e adds the A1 digital certificate (most operators upload a PFX once and CodeSpar converts it to PEM and vaults it), the state tax registration (`inscricao_estadual`) and per-item ICMS / CFOP / NCM classifications; CFDI needs the Facturapi key and RFC; Factura AR needs the AFIP certificate, private key and CUIT.

**Result shape (SDK), as previously documented.** `{ id, access_key, number?, series?, pdf_url?, xml_url?, status: "authorized" | "pending" | "rejected", authorized_at? }`, where `access_key` is the neutral field the per-rail raw keys (BR `chave`, CFDI `uuid`, Factura AR `cae`) normalize into. This revision did not re-verify that list against the runtime.

**From the SDK.** There is no typed wrapper; call `session.execute("codespar_invoice", arguments)` with the same arguments as the MCP call. The dispatch also accepts `buyer` for `recipient` and `products` for `items`; the schema names `recipient` and `items`, so use those.
