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

  1. 1En el panel, conectá ARCA en homologación y elegí el punto de venta por defecto (el 1 sirve).
  2. 2Creá una clave sk_test_ en API Keys y guardala como variable de entorno FISKIT_API_KEY.
  3. 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    }  }'
Respuesta (200)
{
  "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.

O copiá esta indicación y pegala en tu agente:

Indicación para 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.

MCP
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_..." }
    }
  }
}

Herramientas y configuración →

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.

HTTP
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.
La clave · .env
FISKIT_API_KEY=sk_test_…
.gitignore
.env

El 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_typeSi sos…y le facturás a…
invoice_aResponsable inscriptootro responsable inscripto
invoice_bResponsable inscriptoconsumidor final, monotributista o exento
invoice_cMonotributistacualquiera

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.

HTTPstatusQué significa
200authorizedARCA la autorizó. Tiene number, cae y cae_due_date.
400rejectedARCA la rechazó. Los motivos están en error.authority_errors. No se usó ningún número.
202pending / processingEn 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.
202reconcilingSe cortó la comunicación con ARCA. FisKit verifica si quedó emitida, sin duplicarla. Consultala más tarde.
400failedNo 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.pdf

Consultar 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.

Consulta
curl https://api.fiskit.com.ar/v1/taxpayers/30712345671   -H "Authorization: Bearer $FISKIT_API_KEY"
Respuesta (200)
{
  "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.

invoice.authorized
{
  "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á:

Node.js
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

ValorSignificado
cuitCUIT (11 dígitos)
cuilCUIL
dniDNI
final_consumerConsumidor final sin identificar (document_number: 0)

vat_condition_id (condición frente al IVA, RG 5616)

ValorSignificado
1IVA Responsable Inscripto
4IVA Sujeto Exento
5Consumidor Final
6Responsable Monotributo
7Sujeto No Categorizado
8Proveedor del Exterior
9Cliente del Exterior
10IVA Liberado - Ley 19.640
13Monotributista Social
15IVA No Alcanzado
16Monotributo 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.

400
{
  "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"
  }
}
HTTPcodeQué hacer
400parameter_missing / parameter_invalidFalta un campo o tiene un valor inválido. error.param dice cuál.
400invoice_rejectedARCA rechazó la factura. Leé error.authority_errors.
400credentials_missingNo conectaste ARCA en este ambiente. Hacelo desde el panel.
401api_key_missing / api_key_invalidFalta la clave o no es válida (o fue revocada).
403email_not_verifiedConfirmá el email de la cuenta para usar claves live.
402monthly_quota_exceededEl CUIT llegó a las facturas reales del plan gratis este mes. Mejorá el plan o esperá al mes siguiente.
402overage_payment_overdueEl CUIT llegó al límite del plan y hay facturas extra de un mes anterior vencidas. Pagalas en Plan y pagos para seguir emitiendo.
404resource_missingNo existe una factura con ese id en este ambiente.
409idempotency_key_reusedUsaste la misma Idempotency-Key con otro cuerpo.
429rate_limitedSuperaste el límite de pedidos. Esperá lo que indica el header X-RateLimit-Reset.
502 / 503authority_unavailable / authority_bad_responseARCA 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.