Desarrolladores
Integrá FisKit en tu código
Una API REST con JSON. Un pedido para emitir, otro para consultar. Esta guía cubre lo que necesitás para llevarla a producción.
Tu primera factura en 1 minuto
- 1En el panel, conectá ARCA en homologación y elegí el punto de venta por defecto (el 1 sirve).
- 2Creá una clave sk_test_ en API Keys y guardala como variable de entorno FISKIT_API_KEY.
- 3Mandá este pedido. Es una Factura C (monotributo) por $1.500 a consumidor final:
curl https://api.fiskit.com.ar/v1/invoices \ -H "Authorization: Bearer $FISKIT_API_KEY" \ -H "Idempotency-Key: order-1001" \ -H "Content-Type: application/json" \ -d '{ "voucher_type": "invoice_c", "amounts": { "total": 1500 } }'{
"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": []
}Si ARCA la autoriza, la respuesta trae el número y el CAE. Lo que no mandás lo completa la API: sin receiver es a consumidor final, sin concept es por productos y sin point_of_sale usa el del panel. Todo se puede mandar explícito cuando lo necesites.
Librerías oficiales
Librerías oficiales y de código abierto que ya traen todo: reintentos seguros que nunca duplican una factura, errores claros, el PDF y la verificación de webhooks. Con cualquier otro lenguaje, la API es HTTP y JSON: los ejemplos de abajo funcionan igual.
npm install @fiskit/node import { Fiskit } from "@fiskit/node"; const fiskit = new Fiskit(process.env.FISKIT_API_KEY);const invoice = await fiskit.invoices.create( { voucher_type: "invoice_c", amounts: { total: 1500 } }, { idempotencyKey: "order-8812" },);console.log(invoice.number, invoice.cae);Con agentes de IA
Si programás con un agente (Claude Code, Cursor, Copilot, Codex u otro), dale estas dos fuentes: tienen todo lo que necesita para integrar FisKit sin adivinar.
- https://fiskit.com.ar/llms.txt · resumen de la API para modelos de lenguaje: reglas, librerías y errores, en texto plano.
- https://api.fiskit.com.ar/v3/api-docs · especificación OpenAPI con cada campo, tipo y respuesta.
O copiá esta indicación y pegala en tu agente:
Integrate electronic invoicing with FisKit (Argentina, ARCA ex AFIP).
Read https://fiskit.com.ar/llms.txt first, and https://api.fiskit.com.ar/v3/api-docs for the exact schema.
- Use the official library for this language if there is one (listed in llms.txt).
- API key from the FISKIT_API_KEY environment variable; sk_test_ keys for development.
- Always send an idempotency key derived from my order id, so a retry never issues two invoices.
- Handle 202 (pending, retried by FisKit) and errors by their "code" field.Servidor MCP: que el agente facture por vos
Con el servidor MCP, tu agente no solo escribe código: emite facturas, busca un CUIT en el padrón y manda el PDF por vos. Con una clave sk_test_ todo va a homologación; con sk_live_ emite facturas reales y te pide confirmar los datos antes.
claude mcp add fiskit --env FISKIT_API_KEY=sk_test_... -- npx -y @fiskit/mcp
# Claude Desktop, Cursor, Windsurf, VS Code:
{
"mcpServers": {
"fiskit": {
"command": "npx",
"args": ["-y", "@fiskit/mcp"],
"env": { "FISKIT_API_KEY": "sk_test_..." }
}
}
}Lo básico
- URL base:
https://api.fiskit.com.ar - Formato: JSON, con nombres en snake_case. Fechas en formato AAAA-MM-DD y montos con hasta 2 decimales.
- Ambientes: las claves sk_test_ van contra la homologación de ARCA (sin validez fiscal) y las sk_live_ contra producción. El código es el mismo: solo cambia la clave.
- Requisito: antes de usar una clave, conectá ARCA en ese ambiente desde el panel (ver la guía paso a paso).
Autenticación
Mandá la clave en el header Authorization: Bearer. Guardala como variable de entorno en tu servidor: nunca en el código, en el repositorio ni en el navegador o una app móvil. Si se filtra, revocala desde el panel y creá otra.
Authorization: Bearer sk_test_…Las claves live solo funcionan con el email de la cuenta confirmado.
Dónde va en tu proyecto
Facturás siempre desde tu servidor, nunca desde el navegador ni desde una app: la clave tiene que quedar oculta.
- La clave, en una variable de entorno: en desarrollo, en el archivo .env; en producción, en las variables de entorno de tu hosting. Nunca escrita en el código ni subida al repositorio: agregá .env a tu .gitignore.
- El código, en un archivo propio que se encarga de facturar, por ejemplo src/services/fiskit.js, billing/fiskit.py, app/Services/FiskitService.php, Services/FiskitService.cs o FiskitService.java.
- Cuándo: llamalo cuando la venta queda confirmada, por ejemplo cuando se acredita el pago. Usá el id de la venta como Idempotency-Key: si reintentás, nunca se duplica.
- Qué guardar: el id y el cae de la respuesta, junto a la venta en tu base de datos. Si llega pending, guardá el id y consultala más tarde.
FISKIT_API_KEY=sk_test_….envEl archivo de servicio
# fiskit.sh: issues the invoice of a sale from a script or a cron job# Usage: ./fiskit.sh <sale-id> <total>curl https://api.fiskit.com.ar/v1/invoices \ -H "Authorization: Bearer $FISKIT_API_KEY" \ -H "Idempotency-Key: sale-$1" \ -H "Content-Type: application/json" \ -d "{\"voucher_type\": \"invoice_c\", \"amounts\": {\"total\": $2}}"Casos comunes
Factura B con IVA 21 % a un consumidor final con DNI: mandás el total y la alícuota, y calculamos el IVA
curl https://api.fiskit.com.ar/v1/invoices \ -H "Authorization: Bearer $FISKIT_API_KEY" \ -H "Idempotency-Key: order-1002" \ -H "Content-Type: application/json" \ -d '{ "voucher_type": "invoice_b", "receiver": { "document_type": "dni", "document_number": 30123456 }, "amounts": { "total": 121, "vat_rate": "21" } }'Factura A de servicios a una empresa (requiere el período del servicio)
curl https://api.fiskit.com.ar/v1/invoices \ -H "Authorization: Bearer $FISKIT_API_KEY" \ -H "Idempotency-Key: order-1003" \ -H "Content-Type: application/json" \ -d '{ "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" } }'Nota de crédito C que anula la factura C número 1
curl https://api.fiskit.com.ar/v1/invoices \ -H "Authorization: Bearer $FISKIT_API_KEY" \ -H "Idempotency-Key: refund-1001" \ -H "Content-Type: application/json" \ -d '{ "voucher_type": "credit_note_c", "amounts": { "total": 1500 }, "associated_vouchers": [ { "voucher_type": "invoice_c", "point_of_sale": 1, "number": 1 } ] }'Control total: varias alícuotas, con todos los montos detallados
curl https://api.fiskit.com.ar/v1/invoices \ -H "Authorization: Bearer $FISKIT_API_KEY" \ -H "Idempotency-Key: order-1004" \ -H "Content-Type: application/json" \ -d '{ "point_of_sale": 1, "voucher_type": "invoice_b", "concept": "products", "receiver": { "document_type": "dni", "document_number": 30123456, "vat_condition_id": 5 }, "amounts": { "total": 231.5, "net": 200, "vat": 31.5 }, "vat_breakdown": [ { "rate": "21", "taxable_base": 100, "amount": 21 }, { "rate": "10.5", "taxable_base": 100, "amount": 10.5 } ] }'Qué comprobante emitir
| voucher_type | Si sos… | y le facturás a… |
|---|---|---|
| invoice_a | Responsable inscripto | otro responsable inscripto |
| invoice_b | Responsable inscripto | consumidor final, monotributista o exento |
| invoice_c | Monotributista | cualquiera |
Para anular o corregir una factura se emite una nota de crédito (credit_note_a, credit_note_b, credit_note_c) o de débito (debit_note_…) que la referencia en associated_vouchers. Ante la duda sobre tu caso, consultá con tu contador.
Respuestas y estados
Casi siempre la respuesta es final. Si ARCA tarda o se corta la conexión, FisKit responde 202 y termina el trabajo en segundo plano: consultá la factura hasta que tenga un estado final.
| HTTP | status | Qué significa |
|---|---|---|
| 200 | authorized | ARCA la autorizó. Tiene number, cae y cae_due_date. |
| 400 | rejected | ARCA la rechazó. Los motivos están en error.authority_errors. No se usó ningún número. |
| 202 | pending / processing | En curso. Si ARCA está caída y mandaste Idempotency-Key, FisKit la reintenta solo durante 24 h; te avisamos por webhook o consultala más tarde. |
| 202 | reconciling | Se cortó la comunicación con ARCA. FisKit verifica si quedó emitida, sin duplicarla. Consultala más tarde. |
| 400 | failed | No se pudo emitir. Revisá error.message. |
Consultar una factura
curl https://api.fiskit.com.ar/v1/invoices/20e0287c-eba4-4e17-803a-e60a4561298f \ -H "Authorization: Bearer $FISKIT_API_KEY"PDF de la factura
Cada factura autorizada tiene su PDF listo para mandarle a tu cliente, con el diseño de ARCA, el CAE y el código QR obligatorio. Tus datos (razón social, domicilio, condición frente al IVA) se cargan una vez en el panel, en Conectar con ARCA.
Opcionalmente sumá datos que solo van al PDF; ARCA no los recibe y no cambian los montos:
- receiver.name y receiver.address: quién compra. Sin document_type sigue siendo consumidor final.
- receiver.email: al autorizarse, le mandamos el PDF por email a tu cliente (las respuestas te llegan a vos). Para reenviarla: POST /v1/invoices/id/email.
- items: las líneas del comprobante, con description, quantity (por defecto 1) y unit_price.
- notes: un texto libre, por ejemplo el número de pedido.
Factura con datos para el PDF
curl https://api.fiskit.com.ar/v1/invoices \ -H "Authorization: Bearer $FISKIT_API_KEY" \ -H "Idempotency-Key: order-1005" \ -H "Content-Type: application/json" \ -d '{ "voucher_type": "invoice_c", "receiver": { "name": "Juan Pérez", "email": "juan@example.com" }, "amounts": { "total": 3000 }, "items": [ { "description": "Plan mensual", "quantity": 2, "unit_price": 1500 } ], "notes": "Pedido #1005" }'Descargar el PDF
curl https://api.fiskit.com.ar/v1/invoices/20e0287c-eba4-4e17-803a-e60a4561298f/pdf \ -H "Authorization: Bearer $FISKIT_API_KEY" \ -o factura.pdfConsultar un CUIT
Antes de facturar, consultá el padrón de ARCA: te devuelve nombre, domicilio fiscal y condición frente al IVA, y el receiver_vat_condition_id listo para mandar en la factura. Funciona con claves de test y de producción.
curl https://api.fiskit.com.ar/v1/taxpayers/30712345671 -H "Authorization: Bearer $FISKIT_API_KEY"{
"object": "taxpayer",
"cuit": 30712345671,
"name": "ACME SA",
"type": "company",
"active": true,
"vat_condition": "registered",
"receiver_vat_condition_id": 1,
"voucher_letter_for_registered_issuer": "A",
"monotributo_category": null,
"address": {
"street": "AV CORRIENTES 1234",
"city": "CIUDAD AUTONOMA BUENOS AIRES",
"province": "CIUDAD AUTONOMA BUENOS AIRES",
"postal_code": "1043"
}
}Webhooks
Configurá una URL en el panel (API Keys → Webhooks) y FisKit le manda un POST cada vez que una factura llega a un estado final. Así no tenés que consultarla: sirve sobre todo para las que quedaron en 202 mientras ARCA estaba caída.
- invoice.authorized: ARCA la autorizó; trae number y cae.
- invoice.rejected: ARCA la rechazó; los motivos están en authority_errors.
- invoice.failed: no se pudo emitir después de reintentar 24 h.
El cuerpo es un evento con la factura en data.object, con los mismos campos que la respuesta de la API. Respondé cualquier 2xx en menos de 10 segundos; si no, reintentamos espaciado durante un día. Usá webhook-id para no procesar dos veces el mismo evento.
{
"id": "evt_5b1c0f2a9d4e4c7f8a6b3e2d1c0b9a87",
"object": "event",
"type": "invoice.authorized",
"created": "2026-11-10T14:03:22Z",
"livemode": true,
"data": {
"object": {
"id": "20e0287c-eba4-4e17-803a-e60a4561298f",
"object": "invoice",
"status": "authorized",
"voucher_type": "invoice_c",
"point_of_sale": 1,
"number": 42,
"cae": "76451234567890",
"cae_due_date": "2026-11-20",
"authority_observations": [],
"authority_errors": []
}
}
}Verificar la firma
Cada envío trae los headers webhook-id, webhook-timestamp y webhook-signature, según Standard Webhooks. Calculá el HMAC-SHA256 de id.timestamp.cuerpo con tu secreto y compará:
import crypto from "node:crypto";
// Express: app.post("/webhooks/fiskit", express.text({ type: "*/*" }), handler)
function verify(req) {
const id = req.header("webhook-id");
const timestamp = req.header("webhook-timestamp");
const secret = Buffer.from(process.env.FISKIT_WEBHOOK_SECRET.replace("whsec_", ""), "base64");
const expected = crypto.createHmac("sha256", secret)
.update(`${id}.${timestamp}.${req.body}`)
.digest("base64");
const received = req.header("webhook-signature").split(" ").map((s) => s.replace("v1,", ""));
const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) < 300;
return fresh && received.some((s) => s.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(s), Buffer.from(expected)));
}Idempotencia: nunca dupliques una factura
Mandá un header Idempotency-Key único por factura, por ejemplo el id de la venta en tu sistema. Si reintentás con la misma clave y el mismo cuerpo, recibís la misma factura, nunca una nueva. Así podés reintentar sin miedo ante un timeout.
- Misma clave, mismo cuerpo: devuelve la factura original.
- Misma clave, cuerpo distinto: error 409 idempotency_key_reused. Usá una clave nueva para una factura nueva.
- Sin clave: funciona, pero un reintento puede emitir dos facturas. En producción, mandala siempre.
Montos
- total es lo único obligatorio. En comprobantes C alcanza con el total.
- Comprobantes A y B: mandá vat_rate (por ejemplo "21") y calculamos net, vat y vat_breakdown.
- Si tenés varias alícuotas o querés control total, mandá los montos vos: total tiene que ser untaxed (no gravado) + net (neto gravado) + exempt (exento) + vat (IVA), y vat_breakdown lleva una línea por alícuota cuyas bases suman net y cuyos importes suman vat.
- Alícuotas: "0", "2.5", "5", "10.5", "21" y "27".
- Moneda extranjera: agregá currency con code (por ejemplo "DOL") y exchange_rate.
- Servicios (concept: services o products_and_services): service_period es obligatorio, con from, to y payment_due_date.
Receptor
Sin receiver, la factura es a consumidor final sin identificar. Con dni o final_consumer, si no mandás vat_condition_id se toma 5 (consumidor final). Las facturas A siempre llevan receptor con CUIT y su condición.
document_type
| Valor | Significado |
|---|---|
| cuit | CUIT (11 dígitos) |
| cuil | CUIL |
| dni | DNI |
| final_consumer | Consumidor final sin identificar (document_number: 0) |
vat_condition_id (condición frente al IVA, RG 5616)
| Valor | Significado |
|---|---|
| 1 | IVA Responsable Inscripto |
| 4 | IVA Sujeto Exento |
| 5 | Consumidor Final |
| 6 | Responsable Monotributo |
| 7 | Sujeto No Categorizado |
| 8 | Proveedor del Exterior |
| 9 | Cliente del Exterior |
| 10 | IVA Liberado - Ley 19.640 |
| 13 | Monotributista Social |
| 15 | IVA No Alcanzado |
| 16 | Monotributo Trabajador Independiente Promovido |
Errores
Todos los errores tienen el mismo formato. Usá code en tu código (es estable) y mostrá message a las personas. request_id identifica el pedido si nos escribís.
{
"error": {
"type": "invalid_request_error",
"code": "parameter_invalid",
"message": "The total (110) must be the sum of untaxed, net, exempt, other taxes and VAT (100)",
"param": "amounts.total",
"request_id": "req_6f1c2b9e8a7d4c3b"
}
}| HTTP | code | Qué hacer |
|---|---|---|
| 400 | parameter_missing / parameter_invalid | Falta un campo o tiene un valor inválido. error.param dice cuál. |
| 400 | invoice_rejected | ARCA rechazó la factura. Leé error.authority_errors. |
| 400 | credentials_missing | No conectaste ARCA en este ambiente. Hacelo desde el panel. |
| 401 | api_key_missing / api_key_invalid | Falta la clave o no es válida (o fue revocada). |
| 403 | email_not_verified | Confirmá el email de la cuenta para usar claves live. |
| 402 | monthly_quota_exceeded | El CUIT llegó a las facturas reales del plan gratis este mes. Mejorá el plan o esperá al mes siguiente. |
| 402 | overage_payment_overdue | El CUIT llegó al límite del plan y hay facturas extra de un mes anterior vencidas. Pagalas en Plan y pagos para seguir emitiendo. |
| 404 | resource_missing | No existe una factura con ese id en este ambiente. |
| 409 | idempotency_key_reused | Usaste la misma Idempotency-Key con otro cuerpo. |
| 429 | rate_limited | Superaste el límite de pedidos. Esperá lo que indica el header X-RateLimit-Reset. |
| 502 / 503 | authority_unavailable / authority_bad_response | ARCA no responde. Reintentá con la misma Idempotency-Key. |
Límites
Facturas reales por mes según tu plan, contadas por CUIT (las de prueba no cuentan). Además, 600 pedidos por minuto con claves live y 300 con claves test, por empresa. Cada respuesta trae X-RateLimit-Limit, X-RateLimit-Remaining y X-RateLimit-Reset.
Antes de pasar a producción
- La clave está en una variable de entorno del servidor, no en el código.
- Cada factura lleva una Idempotency-Key única, guardada junto a la venta.
- Tu código maneja el 202: guarda el id y consulta la factura más tarde.
- Ante 400 invoice_rejected, mostrás o registrás los motivos de ARCA.
- Probaste todos tus tipos de comprobante con la clave sk_test_.
- Conectaste ARCA en Producción y creaste la clave sk_live_.
¿Te trabaste? Escribinos a fiskitcontact@gmail.com y te ayudamos.