# FisKit > REST API for Argentine electronic invoicing (ARCA, formerly AFIP). One POST issues an invoice and returns its CAE (the authorization code). JSON, snake_case, HTTPS. - Base URL: https://api.fiskit.com.ar - OpenAPI 3 specification (every field, type and response): https://api.fiskit.com.ar/v3/api-docs - Human guide: https://fiskit.com.ar/developers ## Rules an integration must follow - Call the API from a server only. The key goes in the FISKIT_API_KEY environment variable, never in code, a repository, a browser or a mobile app. - Auth header: `Authorization: Bearer `. Keys starting with `sk_test_` use ARCA's testing environment (no tax validity); `sk_live_` keys issue real invoices. The code is identical; only the key changes. Develop and test with `sk_test_`. - Always send an `Idempotency-Key` header derived from the order or sale id (for example `order-8812`). Same key and same body returns the original invoice, so retries after a timeout never issue a second invoice. Same key with a different body returns 409 `idempotency_key_reused`. - A 200 with `status: "authorized"` is final: store `id`, `number` and `cae` next to the sale. - A 202 (`pending`, `processing` or `reconciling`) means FisKit finishes it in the background (retries for up to 24 h). Store the `id` and either wait for a webhook or GET the invoice later. Do not create a new invoice. - A 400 with code `invoice_rejected` means ARCA said no; the reasons are in `error.authority_errors`. No number was consumed. - Branch on `error.code` (stable); show `error.message` to people; log `error.request_id`. - Prerequisite: the account owner connects ARCA in the dashboard for each environment. Without it, requests fail with `credentials_missing`. ## Endpoints - `POST /v1/invoices`: issue a voucher. Needs `Idempotency-Key`. - `GET /v1/invoices/{id}`: look up an invoice (use it after a 202). - `GET /v1/invoices/{id}/pdf`: PDF in ARCA's layout with the mandatory QR code (`application/pdf`). - `POST /v1/invoices/{id}/email`: email the PDF; body `{"email": "..."}` or empty to use `receiver.email`. - `GET /v1/taxpayers/{cuit}`: ARCA registry lookup: name, address, VAT condition and the `receiver_vat_condition_id` to send. ## Which voucher_type - Issuer is monotributista: `invoice_c` to anyone. - Issuer is a registered VAT taxpayer (responsable inscripto): `invoice_a` to another registered VAT taxpayer; `invoice_b` to final consumers, monotributistas or exempt parties. - To cancel or correct: `credit_note_a|b|c` or `debit_note_a|b|c`, referencing the original in `associated_vouchers`. ## Request bodies Minimal Invoice C to an unidentified final consumer (the API fills receiver, concept and point of sale): ```json {"voucher_type": "invoice_c", "amounts": {"total": 1500}} ``` Invoice B with 21% VAT to a final consumer with a DNI (FisKit computes net and VAT from the total): ```json {"voucher_type": "invoice_b", "receiver": {"document_type": "dni", "document_number": 30123456}, "amounts": {"total": 121, "vat_rate": "21"}} ``` Invoice A for services (services require `service_period`): ```json {"voucher_type": "invoice_a", "concept": "services", "receiver": {"document_type": "cuit", "document_number": 30712345671, "vat_condition_id": 1}, "amounts": {"total": 1210, "vat_rate": "21"}, "service_period": {"from": "2026-11-01", "to": "2026-11-30", "payment_due_date": "2026-12-10"}} ``` Credit note C cancelling invoice C number 1 of point of sale 1: ```json {"voucher_type": "credit_note_c", "amounts": {"total": 1500}, "associated_vouchers": [{"voucher_type": "invoice_c", "point_of_sale": 1, "number": 1}]} ``` Amounts: `total` is the only required amount. Rates are strings: "0", "2.5", "5", "10.5", "21", "27". With several rates send `net`, `vat` and `vat_breakdown` yourself (total = untaxed + net + exempt + vat). Dates are YYYY-MM-DD; amounts have up to 2 decimals. Foreign currency: `currency: {"code": "DOL", "exchange_rate": ...}`. Receiver: `document_type` is `cuit`, `cuil`, `dni` or `final_consumer`. `vat_condition_id` (RG 5616): 1 registered VAT, 4 exempt, 5 final consumer (default), 6 monotributo. A invoices always need a CUIT receiver with `vat_condition_id`. Get it from `GET /v1/taxpayers/{cuit}` instead of guessing. PDF-only fields (not sent to ARCA): `receiver.name`, `receiver.address`, `receiver.email` (the PDF is emailed once authorized), `items[]` with `description`, `quantity`, `unit_price`, and `notes`. ## Responses ```json {"id": "20e0287c-eba4-4e17-803a-e60a4561298f", "object": "invoice", "status": "authorized", "point_of_sale": 1, "voucher_type": "invoice_c", "number": 1, "cae": "86451064578686", "cae_due_date": "2026-11-19", "authority_observations": [], "authority_errors": []} ``` ```json {"error": {"type": "invalid_request_error", "code": "parameter_invalid", "message": "...", "param": "amounts.total", "request_id": "req_6f1c2b9e8a7d4c3b"}} ``` Error codes: `parameter_missing`, `parameter_invalid` (see `error.param`), `invoice_rejected`, `credentials_missing` (400); `api_key_missing`, `api_key_invalid` (401); `monthly_quota_exceeded`, `overage_payment_overdue` (402); `email_not_verified` (403); `resource_missing` (404); `idempotency_key_reused` (409); `rate_limited` (429, wait for `X-RateLimit-Reset`); `authority_unavailable`, `authority_bad_response` (502/503: retry with the same Idempotency-Key). ## Webhooks Configured in the dashboard. Events: `invoice.authorized`, `invoice.rejected`, `invoice.failed`, with the invoice in `data.object`. Signed per Standard Webhooks: headers `webhook-id`, `webhook-timestamp`, `webhook-signature` (`v1,`, secret prefixed `whsec_`, base64). Verify on the raw body, reject timestamps older than 5 minutes, answer 2xx within 10 s, dedupe by `webhook-id`. Every official library has a verify function. ## Official libraries Prefer these over raw HTTP: they take the idempotency key, retry safely, raise typed errors and verify webhooks. - Node.js / TypeScript: `npm install @fiskit/node` (https://github.com/fiskitapp/fiskit-node) `new Fiskit(process.env.FISKIT_API_KEY).invoices.create(body, { idempotencyKey })` - Python 3.10+: `pip install fiskit` (https://github.com/fiskitapp/fiskit-python) `Fiskit().invoices.create(body, idempotency_key=...)` - PHP 8.1+ / Laravel: `composer require fiskit/fiskit-php` (https://github.com/fiskitapp/fiskit-php) `(new Fiskit\Fiskit(getenv('FISKIT_API_KEY')))->invoices->create($body, idempotencyKey: ...)` - C# / .NET: `dotnet add package Fiskit` (https://github.com/fiskitapp/fiskit-dotnet) `await new FiskitClient(apiKey).Invoices.CreateAsync(body, idempotencyKey: ...)` - Java 17+ / Spring Boot: groupId `ar.com.fiskit`, artifacts `fiskit-java` and `fiskit-spring-boot-starter` on Maven Central (https://github.com/fiskitapp/fiskit-java) `fiskit.invoices().create(body, idempotencyKey)` - Go 1.21+: `go get github.com/fiskitapp/fiskit-go` (https://github.com/fiskitapp/fiskit-go) `client.Invoices.Create(ctx, body, idempotencyKey)`; errors are `*fiskit.Error` with `Code` ## MCP server To let an agent issue invoices itself (not write code), the MCP server `@fiskit/mcp` exposes the tools lookup_taxpayer, issue_invoice, get_invoice, get_invoice_pdf and email_invoice. Run it with `npx -y @fiskit/mcp` and the FISKIT_API_KEY environment variable; FISKIT_READ_ONLY=true keeps only the lookups. https://github.com/fiskitapp/fiskit-mcp ## Limits Monthly real invoices per CUIT according to the plan (test invoices do not count). 600 requests per minute with live keys and 300 with test keys, per company; see `X-RateLimit-*` headers.