Módulo Integraciones — API REST

Referencia técnica de la API REST v1 de Profactur. Base URL: https://profactur.com/api/v1

Contenido

Primeros pasos

¿Qué es el módulo Integraciones?

El módulo Integraciones expone una API REST para operar tu cuenta de Profactur desde tus propios sistemas: emitir facturas con CAE de ARCA, consultar comprobantes, administrar clientes y productos, y recibir notificaciones por webhooks.

Requisitos

Sin ambos requisitos, la API responde 403 (ver Autenticación).

Generá tu primera API Key

En el panel, andá a Configuración → Integraciones → API REST, completá un nombre, elegí los scopes que necesitás y generá la clave. La clave cruda se muestra una sola vez: guardala en un lugar seguro, no se vuelve a mostrar.

Primera llamada en 5 minutos

curl -X GET https://profactur.com/api/v1/account \
  -H "Authorization: Bearer TU_API_KEY"

Autenticación

Todas las requests requieren el header:

Authorization: Bearer {tu_api_key}

Scopes disponibles

Cada API Key tiene asignados uno o más scopes que limitan a qué endpoints puede acceder.

ScopePermiteEndpoints
account:readConsultar cuenta y consumoGET /account, GET /account/usage
invoices:readConsultar comprobantesGET /invoices, GET /invoices/{id}, GET /invoices/{id}/pdf
invoices:writeEmitir comprobantesPOST /invoices
customers:readConsultar clientesGET /customers, GET /customers/{id}
customers:writeCrear y editar clientesPOST /customers, PUT /customers/{id}
products:readConsultar productosGET /products, GET /products/{id}
webhooks:manageAdministrar webhooksGET /webhooks, POST /webhooks, DELETE /webhooks/{id}

Errores de autenticación

CódigoHTTPDescripción
INVALID_API_KEY401La key no existe o fue revocada
INTEGRATIONS_MODULE_INACTIVE403El módulo Integraciones no está activo
PLAN_REQUIRED403Se requiere un plan ProFactur activo
INSUFFICIENT_SCOPE403La key no tiene el scope necesario

Sandbox (modo pruebas)

Las keys sandbox permiten desarrollar y probar tu integración sin emitir facturas reales, sin consumir cuota del plan y sin escribir datos en tu cuenta. Se crean desde Configuración → Integraciones eligiendo el ambiente «Sandbox» — se distinguen a simple vista por el prefijo pf_test_ (producción usa pf_live_). Podés tener hasta 2 keys sandbox activas, que no cuentan para el límite de keys de tu plan. No hace falta tener el certificado ARCA configurado: podés probar la API antes de terminar el setup fiscal.

Sandbox NO es «producción con un flag». Diferencias que tenés que conocer antes de integrar:

Comportamiento por endpoint

EndpointCon key sandbox
POST /invoicesValidación completa (tipos, cliente, punto de venta) + emisión simulada: responde 201 con id: 0, voucher_number: 0, CAE simulado y sandbox: true. No llama a ARCA ni consume cuota.
POST/PUT /customersValidación completa + echo simulado con id: 0 y sandbox: true. No persiste.
GET (invoices, customers, products, account)Leen tus datos reales — solo lectura, sin efectos.
POST/DELETE /webhooks403 SANDBOX_UNSUPPORTED.

Identificar respuestas sandbox

Toda respuesta de escritura simulada incluye meta.environment: "sandbox" y data.sandbox: true (redundante a propósito, para que tu logging distinga el ambiente sin ambigüedad). El rate limit en sandbox es el de tu plan × 3.

Ejemplo

curl -X POST https://profactur.com/api/v1/invoices \
  -H "Authorization: Bearer pf_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "invoice_type": "FC_B",
    "point_of_sale_id": 3,
    "customer": { "name": "Juan Pérez", "document_type": "DNI", "document_number": "30111222" },
    "items": [ { "description": "Test", "quantity": 1, "unit_price": 1000, "tax_rate": 21 } ]
  }'
{
  "success": true,
  "data": {
    "id": 0,
    "full_number": "FC B 00003-00000000",
    "voucher_number": 0,
    "cae": "91234567890123",
    "cae_due_date": "2026-07-15",
    "is_fiscalized": true,
    "sandbox": true,
    "total": 1210.0
  },
  "meta": { "request_id": "…", "timestamp": "…", "environment": "sandbox" }
}

Referencia de endpoints

Todas las respuestas usan el mismo envelope: { success, data, meta } en éxito; { success, error, meta } en error (ver Errores).

Account

GET/api/v1/account

Datos de la cuenta y plan activo. Scope: account:read

curl https://profactur.com/api/v1/account \
  -H "Authorization: Bearer TU_API_KEY"
{
  "success": true,
  "data": {
    "company": {
      "id": 2,
      "name": "Mi Empresa S.R.L.",
      "fantasy_name": "Mi Empresa",
      "cuit": "30712345678",
      "tax_condition": "RI",
      "is_demo": false,
      "can_fiscalize": true
    },
    "plan": {
      "name": "Mediano",
      "slug": "mediano",
      "invoices_per_month": 5000
    }
  },
  "meta": { "request_id": "9b1c...", "timestamp": "2026-06-29T12:00:00-03:00" }
}

GET/api/v1/account/usage

Consumo del mes actual vs. el límite del plan. Scope: account:read

{
  "success": true,
  "data": {
    "period": "2026-06",
    "invoices_this_month": 120,
    "invoices_limit": 5000,
    "invoices_remaining": 4880,
    "unlimited": false
  },
  "meta": { "request_id": "...", "timestamp": "..." }
}

Facturas

GET/api/v1/invoices

Listado paginado (50 por página). Filtros opcionales: desde, hasta (fecha YYYY-MM-DD), status (draft, confirmed, fiscal_pending, fiscalized, cancelled) y source (web, api, pos, batch — origen del comprobante; usá source=api para reconciliar solo lo emitido desde tu integración). Scope: invoices:read

curl "https://profactur.com/api/v1/invoices?desde=2026-06-01&status=fiscalized&source=api" \
  -H "Authorization: Bearer TU_API_KEY"
{
  "success": true,
  "data": [
    {
      "id": 1024,
      "full_number": "00003-00000128",
      "voucher_number": 128,
      "voucher_type": { "afip_code": 6, "letter": "B", "short_name": "FC B", "name": "Factura B" },
      "date": "2026-06-29",
      "customer": { "id": 55, "name": "Juan Pérez", "document_type": "DNI", "document_number": "30111222", "tax_condition": "CF" },
      "currency": "ARS",
      "subtotal": 1000.00,
      "tax_amount": 210.00,
      "total": 1210.00,
      "status": "fiscalized",
      "payment_status": "pending",
      "source": "api",
      "cae": "75123456789012",
      "cae_due_date": "2026-07-09",
      "is_fiscalized": true,
      "created_at": "2026-06-29T12:00:00-03:00"
    }
  ],
  "meta": {
    "request_id": "...", "timestamp": "...",
    "pagination": { "total": 128, "per_page": 50, "current_page": 1, "last_page": 3 }
  }
}

POST/api/v1/invoices

Emite una factura y solicita el CAE a ARCA. Scope: invoices:write. Este es el único endpoint que consume cuota facturable (cuando el CAE se emite con éxito).

invoice_type admite FC_A, FC_B, FC_C, ND_A/B/C, NC_A/B/C. Los comprobantes letra A requieren customer_id de un cliente registrado (no admiten cliente inline). Para B/C podés mandar customer_id o un objeto customer inline.

curl -X POST https://profactur.com/api/v1/invoices \
  -H "Authorization: Bearer TU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "invoice_type": "FC_B",
    "point_of_sale_id": 3,
    "customer": {
      "name": "Juan Pérez",
      "document_type": "DNI",
      "document_number": "30111222",
      "address": "Av. Siempreviva 742"
    },
    "items": [
      {
        "description": "Servicio de consultoría",
        "quantity": 1,
        "unit_price": 1000.00,
        "discount_percent": 0,
        "tax_rate": 21
      }
    ],
    "date": "2026-06-29"
  }'

Respuesta 201 Created con el comprobante (mismo formato que el detalle, incluyendo items y el cae).

Errores específicos: INVALID_CUSTOMER, INVALID_POINT_OF_SALE, INVALID_PRODUCT, INVALID_VOUCHER_TYPE (422); INVOICE_NOT_ALLOWED (422, cuenta demo o sin certificado ARCA); AFIP_ERROR (502, ARCA rechazó el comprobante).

GET/api/v1/invoices/{id}

Detalle de un comprobante, con sus items. Scope: invoices:read. 404 NOT_FOUND si no pertenece a tu empresa.

GET/api/v1/invoices/{id}/pdf

Devuelve el PDF del comprobante (Content-Type: application/pdf, inline). Scope: invoices:read

Clientes

GET/api/v1/customers

Listado paginado (50 por página). Filtro opcional search (nombre, documento o CUIT). Scope: customers:read

POST/api/v1/customers

Crea un cliente. Scope: customers:write. tax_condition: CF, RI, MT, EX, NR.

curl -X POST https://profactur.com/api/v1/customers \
  -H "Authorization: Bearer TU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "ACME S.A.",
    "customer_type": "company",
    "document_type": "CUIT",
    "tax_id": "30712345678",
    "tax_condition": "RI",
    "email": "[email protected]"
  }'
{
  "success": true,
  "data": {
    "id": 56,
    "name": "ACME S.A.",
    "fantasy_name": null,
    "customer_type": "company",
    "document_type": "CUIT",
    "document_number": null,
    "tax_id": "30712345678",
    "tax_condition": "RI",
    "email": "[email protected]",
    "phone": null,
    "mobile": null,
    "address": null,
    "city": null,
    "state": null,
    "zip": null,
    "credit_limit": 0,
    "payment_term_days": 0,
    "is_active": true,
    "created_at": "2026-06-29T12:00:00-03:00"
  },
  "meta": { "request_id": "...", "timestamp": "..." }
}

GET/api/v1/customers/{id}

Detalle de un cliente. Scope: customers:read

PUT/api/v1/customers/{id}

Actualiza un cliente (campos opcionales; company_id nunca se reasigna). Scope: customers:write

Productos

GET/api/v1/products

Listado paginado (50 por página). Filtros: search (nombre, SKU, código de barras) y active_only. Scope: products:read

{
  "success": true,
  "data": [
    {
      "id": 7,
      "sku": "SRV-001",
      "barcode": null,
      "name": "Servicio de consultoría",
      "description": null,
      "cost_price": 0,
      "tax_rate": 21,
      "is_taxable": true,
      "is_service": true,
      "track_stock": false,
      "is_active": true,
      "created_at": "2026-06-29T12:00:00-03:00"
    }
  ],
  "meta": { "request_id": "...", "timestamp": "...", "pagination": { "total": 1, "per_page": 50, "current_page": 1, "last_page": 1 } }
}

GET/api/v1/products/{id}

Detalle de un producto. Scope: products:read

Webhooks

GET/api/v1/webhooks

Endpoints de webhook registrados. Scope: webhooks:manage

POST/api/v1/webhooks

Registra un endpoint. Scope: webhooks:manage. La url debe ser HTTPS. El secret de firma se devuelve una sola vez en la respuesta.

curl -X POST https://profactur.com/api/v1/webhooks \
  -H "Authorization: Bearer TU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://tu-sistema.com/webhooks/profactur",
    "events": ["invoice.created", "invoice.cae_approved"]
  }'
{
  "success": true,
  "data": {
    "id": 4,
    "url": "https://tu-sistema.com/webhooks/profactur",
    "events": ["invoice.created", "invoice.cae_approved"],
    "is_active": true,
    "last_triggered_at": null,
    "created_at": "2026-06-29T12:00:00-03:00",
    "secret": "whsec_xxxxxxxxxxxxxxxxxxxx"
  },
  "meta": { "request_id": "...", "timestamp": "..." }
}

DELETE/api/v1/webhooks/{id}

Elimina un endpoint. Scope: webhooks:manage. Devuelve { "deleted": true }.

Operaciones y consumo

¿Qué cuenta como evento facturable?

Solo un POST /api/v1/invoices que resulte en un CAE emitido con éxito. Una factura emitida vía API = un evento facturable.

¿Qué NO consume cuota?

¿Cómo veo mi consumo?

En el panel: Configuración → Integraciones → Consumo. Ahí ves las facturas usadas del mes, el límite de tu plan y el excedente.

¿Qué pasa si supero el límite?

El servicio continúa activo: no se bloquean las emisiones. El excedente se factura manualmente según el costo por 1.000 facturas de tu plan (ver tabla de planes).

Webhooks

Registro

Usá POST /api/v1/webhooks con url (HTTPS requerido) y events (array). Guardá el secret que se devuelve una sola vez.

Eventos disponibles

Verificar la firma

Cada entrega incluye los headers X-ProFactur-Signature y X-ProFactur-Event. La firma es un HMAC-SHA256 del cuerpo crudo recibido, con el prefijo sha256=. Verificala con hash_equals sobre los bytes exactos del body:

$payload   = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_PROFACTUR_SIGNATURE'] ?? '';
$expected  = 'sha256=' . hash_hmac('sha256', $payload, $secret);

if (! hash_equals($expected, $signature)) {
    http_response_code(401);
    exit;
}

Reintentos

Si tu endpoint no responde 2xx, reintentamos hasta 3 veces con backoff de 60s → 300s → 900s.

Rate limits

El límite de requests por minuto y el cupo mensual de facturas dependen de tu plan Integrador:

PlanReq/minFacturas/mesPrecioExcedente /1.000
Starter 30 1.000 USD 99 USD 60
Pro 60 4.000 USD 199 USD 50
Business 120 8.000 USD 349 USD 25
Enterprise 200 20.000 USD 599 USD 12

Cada respuesta incluye los headers de rate limit:

HeaderDescripción
X-RateLimit-LimitLímite de requests por minuto de tu plan
X-RateLimit-RemainingRequests restantes en la ventana actual
X-RateLimit-ResetTimestamp Unix en que se reinicia la ventana

Al superar el límite, la API responde 429 RATE_LIMIT_EXCEEDED. Esperá los segundos indicados en el header Retry-After antes de reintentar.

Las keys sandbox tienen su propio bucket con el límite del plan × 3.

Headers de consumo

HeaderDescripción
X-Billable-Events-MonthFacturas emitidas vía API este mes
X-Billable-Events-RemainingFacturas restantes del cupo mensual (negativo si hay excedente)

Errores

Todos los errores usan el mismo envelope:

{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Descripción del error"
  },
  "meta": {
    "request_id": "uuid",
    "timestamp": "2026-06-29T12:00:00Z"
  }
}

En errores de validación, error.details incluye los campos inválidos.

Códigos de error

CódigoHTTPDescripción
INVALID_API_KEY401La key no existe o fue revocada
INTEGRATIONS_MODULE_INACTIVE403El módulo Integraciones no está activo
PLAN_REQUIRED403Se requiere un plan ProFactur activo
INSUFFICIENT_SCOPE403La key no tiene el scope necesario
RATE_LIMIT_EXCEEDED429Superaste el límite de requests por minuto
NOT_FOUND404El recurso no existe o no pertenece a tu empresa
VALIDATION_ERROR422Datos inválidos (detalle en error.details)
INVOICE_NOT_ALLOWED422La cuenta no puede emitir (demo, sin certificado, o límite de plan)
SANDBOX_UNSUPPORTED403Operación no disponible con key sandbox (ver Sandbox)
AFIP_ERROR502ARCA rechazó el comprobante
INTERNAL_ERROR500Error interno inesperado

Errores de ARCA

Un AFIP_ERROR con HTTP 502 significa que ARCA rechazó el comprobante. El motivo concreto devuelto por ARCA viene en el campo message del error, y error.details incluye arca_code (código del error primario) y arca_errors (lista cruda de errores del webservice).

Para los códigos más frecuentes, el envelope suma un campo hint: una explicación en español orientada a la acción, para resolver el rechazo sin buscar en los manuales de ARCA. Si el código no está catalogado, hint no aparece.

{
  "success": false,
  "error": {
    "code": "AFIP_ERROR",
    "message": "[10018] El campo ImpIVA es distinto a la suma de los importes por alícuota",
    "details": {
      "arca_code": "10018",
      "arca_errors": [ { "Code": 10018, "Msg": "..." } ]
    },
    "hint": "El desglose de IVA no coincide con el importe declarado. ImpIVA debe ser la suma exacta de los importes por alícuota."
  },
  "meta": { "request_id": "uuid", "timestamp": "2026-07-04T12:00:00Z" }
}

Catálogo de hints

Código ARCAHint
600El certificado usado no corresponde al CUIT emisor (ValidacionDeToken). Verificá en ARCA que el servicio wsfe esté delegado al certificado de tu empresa.
601El CUIT autenticado no coincide con el CUIT emisor del comprobante. El emisor debe ser el mismo CUIT con el que se generó el certificado.
10000Tu CUIT no está autorizado a emitir comprobantes clase A. Consultá con tu contador la habilitación en ARCA.
10005El punto de venta no existe en ARCA o no está habilitado para facturación por Web Services. Verificá en ARCA → Administración de puntos de venta que el punto sea de tipo "Web Services".
10013El número de documento (DocNro) del receptor es inválido para el tipo de documento informado. CUIT y CUIL van con 11 dígitos, sin guiones ni espacios.
10015Falta identificar al receptor: a partir de cierto importe el consumidor final debe informarse con DNI o CUIT. Revisá el tipo y número de documento del cliente.
10246Falta o es inválida la condición frente al IVA del receptor (RG 5616). Verificá la condición fiscal del cliente en ProFactur antes de emitir.
10016El número o la fecha del comprobante no se corresponde con el próximo a autorizar. Suele indicar numeración desfasada del punto de venta o fecha fuera del rango permitido (5 días hacia atrás para productos, 10 para servicios).
10018El desglose de IVA no coincide con el importe declarado. ImpIVA debe ser la suma exacta de los importes por alícuota.
10023Los importes de IVA por alícuota no cierran contra sus bases imponibles (tolerancia 0,01%). Recalculá cada IVA como base × alícuota con redondeo a 2 decimales.
10192El comprobante cae bajo el régimen de Factura de Crédito Electrónica (Ley 27.440). Para receptores grandes empresas corresponde emitir FCE (tipos 201/206/211).
500Error interno de ARCA. No es un problema de tu request: reintentá en unos minutos.
501Error interno de base de datos de ARCA. No es un problema de tu request: reintentá en unos minutos.
502Error interno de base de datos de ARCA. No es un problema de tu request: reintentá en unos minutos.
503El servicio de ARCA no está disponible. Reintentá en unos minutos.
CONFIGLa configuración ARCA de la cuenta está incompleta. Revisá el CUIT y el certificado en Configuración → Facturación Electrónica.
CERTEl certificado ARCA de la cuenta es inválido o expiró. Regenerá el certificado desde Configuración → Facturación Electrónica.
SERVICEEl servicio de ARCA no está disponible en este momento. Reintentá en unos minutos.

Los códigos CONFIG, CERT y SERVICE no vienen de ARCA: son errores de configuración de la cuenta detectados por Profactur antes de llamar al webservice.

¿Necesitás ayuda? Escribinos a [email protected]