Factura SDK
Errores y respuestas

Errores y respuestas

Códigos HTTP, formato de los errores (data_errors y errors), automatizaciones con 202 y paginación.


Toda la API responde JSON. Las respuestas con error traen siempre un message que explica qué pasó en castellano y, según el caso, data_errors (un problema por campo) o errors (una lista de reglas que no se cumplieron). Esta página reúne los códigos y los formatos; cada referencia tiene su propia tabla con los casos puntuales.

Códigos HTTP

HTTPQué significa
200Listo. En POST /v1/invoices, el comprobante tiene estado final.
201Creado.
202Aceptado, sigue en segundo plano: una automatización con automation_id, o un comprobante en queued o processing (lo normal en POST /v1/invoices, que por defecto es asíncrono).
204Listo, sin cuerpo. Solo DELETE /v1/issuers/:id.
400La solicitud no se puede procesar: errores de forma, una regla del comprobante, un emisor sin verificar, un certificado vencido, un límite del plan. Ver 400.
401Falta el access token, o es inválido o está revocado.
403El token o el usuario no puede hacer eso.
404El recurso no existe en el proyecto.
409Conflicto con algo que ya existe o que está pasando ahora.
5xxUn problema de nuestro lado, o ARCA caída. Reintentá con espera creciente.

Formato de los errores

Todo error trae statusCode, igual al código HTTP de la respuesta. Un error de forma (falta un campo, tiene el tipo incorrecto, se pasa del largo, no está en el catálogo) responde 400 con data_errors: un mensaje por campo, anidado igual que la solicitud, y sin message.

{
  "statusCode": 400,
  "data_errors": {
    "invoice_type": "El campo Tipo de comprobante es obligatorio",
    "receiver": {
      "vat_condition": "El campo Condición frente al IVA del receptor es invalido"
    }
  }
}

Una regla de negocio del comprobante responde 400 con errors: una lista con code y message. Los códigos están en Comprobantes.

{
  "message": "El comprobante no cumple las validaciones. Revise el detalle en errors.",
  "errors": [
    {
      "code": "amounts_mismatch",
      "message": "El total (12000) debe ser igual a net + vat + exempt + untaxed + tributes (12100)"
    }
  ]
}

Todo lo demás responde con un message que explica la causa y, cuando apunta a un campo concreto, ese campo en data_errors. Para tu código, fijate en el status y en las claves de data_errors; message es para mostrar o loguear.

{
  "message": "El emisor 20111111112 todavía no está verificado. Verificalo con POST /v1/issuers/0f8f3c2a-5b1e-4c7d-9a2e-6d4b8e1f2a3c/verify (o desde el dashboard) para empezar a emitir.",
  "data_errors": {
    "issuer_cuit": "El emisor no está verificado"
  }
}

400: solicitud inválida

CasoDónde mirar
Errores de forma.data_errors, por campo.
El comprobante no cumple una regla.errors, con el code de cada regla.
El emisor está pending (sin verificar) o inactive.data_errors.issuer_cuit.
El emisor no tiene certificado, o el que tiene venció o está revocado.data_errors.issuer_cuit al emitir; data_errors.certificate_id al verificar. message dice cómo resolverlo.
El emisor no tiene punto de venta.data_errors.issuer_cuit al emitir; data_errors.sales_point al verificar.
La condición frente al IVA del emisor no emite comprobantes.data_errors.vat_condition.
status en un emisor sin verificar, o tax_id en un PUT.data_errors.status, data_errors.tax_id.
certificate_id que no existe o no está active, al crear o modificar un emisor.data_errors.certificate_id.
Certificado inválido, vencido, o clave que no corresponde.data_errors.cert, data_errors.key, data_errors.tax_id.
URL de webhook inválida.data_errors.url.
El Idempotency-Key supera los 255 caracteres.message.
Cancelar un comprobante de verificación.message.
Alcanzaste el límite de comprobantes o de CUITs de tu plan, o el presupuesto mensual de pago por uso.message, con el link para cambiar de plan o el presupuesto. Ver límites del plan.
Alcanzaste el límite de automatizaciones de tu plan.message, con el link para cambiar de plan.
La suscripción del proyecto está vencida, o impaga por más de una semana.message, con el link a la facturación.

401: sin autenticación

MensajeCausa
”Necesitás un access_token. Podés obtenerlo desde https://app.afipsdk.com/“No mandaste el header Authorization.
”El token proporcionado es invalido.”El token no existe o está revocado.
”Este endpoint requiere un access token de FacturaSDK (uno por ambiente). Crealo en el dashboard: Configuración → Access tokens.”Usaste el access token de Afip SDK del proyecto. Ver Access tokens.

403: sin permiso

Desde el dashboard, tu rol en el proyecto (developer) solo permite consultar; pedile el cambio a un owner o admin.

404: no existe

Un recurso que no está en tu proyecto no existe para esa solicitud: un emisor borrado, un certificado inexistente, una automatización de otro proyecto o un token de PDF que no corresponde al comprobante responden 404. En POST /v1/invoices, un issuer_cuit sin emisor responde 404 con data_errors.issuer_cuit.

409: conflicto

CasoQué hacer
Ya hay un emisor con ese CUIT.Usá ese emisor, o borralo y creá otro.
Con sync: true, otra solicitud está emitiendo el comprobante de esa Idempotency-Key.Repetí en unos segundos con la misma clave.
Cancelar un comprobante que ya está processing o que ya terminó.Esperá el resultado; un aprobado se anula con una nota de crédito.
El PDF de un comprobante que todavía no está aprobado.Esperá el estado final.
Ya hay un endpoint de webhook activo, o uno con esa URL.Modificá o reactivá el existente.
Dos solicitudes cargaron el mismo certificado a la vez.Repetí: vas a recibir el existente.

Automatizaciones

Algunas operaciones entran a ARCA con la clave fiscal, o emiten comprobantes, y tardan más de lo que dura una solicitud. Responden 202 enseguida con un automation_id, y el trabajo sigue en segundo plano:

OperaciónResultado
POST /v1/certificates con source: "automation"{ certificate, created, authorization }. Ver Certificados.
POST /v1/issuers/:id/certificate con source: "automation"Igual, con issuer_id y delegation dentro de certificate.
POST /v1/certificates/:id/authorize{ certificate, authorization }. Ver Certificados.
POST /v1/issuers/sales-points{ tax_id, sales_points, next_number }. Ver Emisores.
POST /v1/issuers/sales-points/create{ tax_id, sales_point }. Ver Emisores.
POST /v1/issuers/:id/delegation{ issuer_id, certificate_id, delegation, authorization }. Ver Emisores.
POST /v1/issuers/:id/verify{ verified, issuer, invoice, credit_note }. Ver Emisores.

La respuesta 202 tiene esta forma:

{
  "status": "in_process",
  "automation_id": "a9e2c4b6-1d3f-4a5b-8c7d-2e4f6a8b0c1d",
  "message": "La creación del certificado en ARCA está en proceso. Consulte el resultado con GET /v1/automations/a9e2c4b6-1d3f-4a5b-8c7d-2e4f6a8b0c1d."
}

GET /v1/automations/:id devuelve el estado. Consultalo cada unos segundos hasta que deje de estar in_process: una automatización tarda desde segundos hasta un par de minutos (en el sandbox termina al instante).

Solicitud
const headers = { 'Authorization': 'Bearer <ACCESS_TOKEN>' };

let automation;

do {
  await new Promise((resolve) => setTimeout(resolve, 3000));

  const response = await fetch(`https://app.afipsdk.com/api/v1/automations/${automationId}`, { headers });

  automation = await response.json();
} while (automation.status === 'in_process');

if (automation.status === 'complete') {
  console.log(automation.data);
} else {
  console.error(automation.data.message);
}

Mientras corre, 200 con status: "in_process" y sin data:

{
  "id": "a9e2c4b6-1d3f-4a5b-8c7d-2e4f6a8b0c1d",
  "status": "in_process"
}

Cuando termina bien, 200 con status: "complete" y el resultado en data:

{
  "id": "a9e2c4b6-1d3f-4a5b-8c7d-2e4f6a8b0c1d",
  "status": "complete",
  "data": {
    "certificate": { "id": "c3a1e7d9-8b2f-4e6a-a1d4-5f7b9c0e2d31", "tax_id": "30715000004", "status": "active", "...": "..." },
    "created": true,
    "authorization": "created"
  }
}

Cuando falla, 400 con status: "error" y el error en data: siempre con message, y según la operación data_errors (una clave fiscal incorrecta, una delegación que falta) o los campos propios del resultado (verified, step, errors en una verificación).

{
  "id": "a9e2c4b6-1d3f-4a5b-8c7d-2e4f6a8b0c1d",
  "status": "error",
  "data": {
    "statusCode": 400,
    "message": "Clave fiscal incorrecta (simulado con INVALID_PASSWORD).",
    "data_errors": {
      "password": "Clave fiscal incorrecta"
    }
  }
}

Una automatización que falló no se reintenta sola: corregí la causa y volvé a llamar al endpoint que la inició. Una automatización que no existe, o que es de otro proyecto, responde 404.

El 202 de POST /v1/invoices es distinto: no hay automatización, el comprobante quedó en queued o processing y el resultado se consulta con GET /v1/invoices/:id o llega por webhook. Ver Comprobantes.

Paginación

Los listados (GET /v1/invoices, GET /v1/issuers, GET /v1/certificates, GET /v1/webhook-endpoints, GET /v1/webhook-endpoints/:id/deliveries) devuelven la misma forma, del más nuevo al más viejo:

{
  "data": [ { "...": "..." } ],
  "next_cursor": "7c1d9e4b-2f3a-4b8c-9d0e-1a2b3c4d5e6f",
  "total_count": 128
}
ParámetroDescripción
limitCantidad por página, de 1 a 100. Por defecto, 10. Un valor fuera de rango se ajusta al más cercano.
cursorEl next_cursor de la página anterior: el id del último elemento de esa página.

Para la página siguiente repetí la solicitud, con los mismos filtros, y cursor igual a next_cursor. Cuando next_cursor es null, no hay más páginas. total_count es el total que cumple los filtros. GET /v1/usage/tax-ids responde con la misma forma, pero su limit por defecto es 20 y su cursor es un valor opaco propio, que también se usa tal cual.

Límites del plan

Los planes de Factura SDK incluyen una cantidad de comprobantes aprobados y de CUITs emisores por período. Lo que pasa en sandbox y los comprobantes de verificación de los emisores no cuentan. Al superar lo incluido, el proyecto sigue emitiendo con pago por uso si lo tiene activado, hasta el presupuesto mensual que configuraste (50 USD por defecto); si no, POST /v1/invoices responde 400 con un message que incluye el link para cambiar de plan o aumentar el presupuesto. El mensaje nombra lo que se agotó: los comprobantes, los CUITs o el presupuesto de pago por uso.

{
  "statusCode": 400,
  "message": "Alcanzaste el límite de comprobantes que podés emitir en este período. Si necesitás emitir más comprobantes, podés ingresar en https://app.afipsdk.com/billing para cambiar tu plan. ID 1234"
}