Desarrolladores

Emite tu primer documento en minutos

Una API REST con JSON, idempotencia obligatoria y errores con código estable. Esta guía te lleva desde la llave hasta el webhook de aceptación.

URL basehttps://api.indefac.com/api/v1

Antes de empezar

La API pública vive bajo /api/v1. Toda respuesta exitosa trae el resultado en data; todo error trae error con un código estable.

  • Ambientes. Cada empresa tiene certificación y producción. La llave de API fija la empresa y el ambiente: no hay que indicarlos en cada llamada.
  • Formato. JSON en UTF-8, fechas ISO 8601 e instantes en UTC.
  • Límites. 300 solicitudes por minuto por llave y 20 emisiones por segundo por empresa. Al superarlos, la API responde 429 con la cabecera Retry-After.
  • Montos. Tu sistema envía los totales calculados; IndeFac los recalcula y los compara. Una diferencia mayor a $1 se rechaza con el detalle de lo esperado.

1. Crea una llave de API

Las llaves se crean en la aplicación, en Ajustes → Llaves de API, y solo las puede crear el administrador de la empresa.

  1. Elige el ambiente: parte por certificación.
  2. Marca los alcances que tu sistema necesita, por ejemplo documents:write y documents:read.
  3. Copia la llave: se muestra una sola vez. Tiene la forma ifk_<prefijo>_<secreto>.
  4. Envíala en cada llamada en la cabecera Authorization: Bearer.

Alcances disponibles

documents:writedocuments:readreceived:readreceived:writefolios:readwebhooks:manage

Comprueba la llave

curl
curl https://api.indefac.com/api/v1/me \
  -H "Authorization: Bearer $INDEFAC_API_KEY"
200 OK
{
  "data": {
    "tenant": { "rut": "76123456-7", "name": "Comercial Ejemplo SpA" },
    "environment": "certification",
    "scopes": ["documents:write", "documents:read"],
    "plan": "essential"
  }
}

2. Emite un documento

Una sola llamada valida, asigna folio, timbra y firma. La respuesta llega de inmediato; el envío al SII sigue en segundo plano.

curl · POST /api/v1/documents
curl -X POST https://api.indefac.com/api/v1/documents \
  -H "Authorization: Bearer $INDEFAC_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: FAC-2026-000123" \
  -d '{
    "externalId": "FAC-2026-000123",
    "type": 33,
    "issueDate": "2026-10-13",
    "receiver": {
      "rut": "76184649-3",
      "name": "COMERCIAL LOS AROMOS SPA",
      "businessLine": "Venta al por mayor",
      "address": "Av. Providencia 1234",
      "commune": "Providencia",
      "city": "Santiago",
      "email": "[email protected]"
    },
    "payment": { "form": 2, "dueDate": "2026-11-12" },
    "lines": [
      {
        "number": 1,
        "name": "Servicio mensual",
        "quantity": 1,
        "unit": "UN",
        "unitPrice": 160283,
        "amount": 160283
      }
    ],
    "totals": { "net": 160283, "exempt": 0, "vat": 30454, "total": 190737 },
    "sendEmail": true
  }'

Respuesta

201 Created
{
  "data": {
    "id": "0199d2b4-5c1e-7a52-9a53-3f2f7f1f6c10",
    "type": 33,
    "folio": 1284,
    "issueDate": "2026-10-13",
    "status": "queued",
    "ted": "<TED version=\"1.0\">…</TED>",
    "pdf417Base64": "iVBORw0KGgoAAAANSUhEUgAA…",
    "links": {
      "xml": "https://api.indefac.com/api/v1/documents/0199d2b4-5c1e-7a52-9a53-3f2f7f1f6c10/xml",
      "pdf": "https://api.indefac.com/api/v1/documents/0199d2b4-5c1e-7a52-9a53-3f2f7f1f6c10/pdf",
      "ticket": "https://api.indefac.com/api/v1/documents/0199d2b4-5c1e-7a52-9a53-3f2f7f1f6c10/ticket",
      "public": "https://indefac.com/p/8f3c1d…"
    },
    "totals": { "net": 160283, "exempt": 0, "vat": 30454, "additionalTaxes": 0, "total": 190737 }
  }
}

Con 201 el documento ya existe, tiene folio y su PDF está listo. El campo status avanza después: queued, sent, accepted, accepted_with_remarks o rejected.

3. Idempotencia

Toda emisión debe poder repetirse sin riesgo. Identifica cada documento con el campo externalId de tu sistema o con la cabecera Idempotency-Key (hasta 128 caracteres).

  • Misma clave, mismo cuerpo: la API responde 200 con el documento ya emitido. No se usa otro folio.
  • Misma clave, otro cuerpo: la API responde 409 con el código DUPLICATE_IDEMPOTENCY_KEY_MISMATCH.
  • Sin respuesta por un corte de red: repite la llamada tal cual. Es exactamente el caso para el que existe.
409 Conflict
HTTP/1.1 409 Conflict
Content-Type: application/json; charset=utf-8

{
  "error": {
    "code": "DUPLICATE_IDEMPOTENCY_KEY_MISMATCH",
    "message": "La clave FAC-2026-000123 ya se usó con otro contenido."
  }
}

4. Consulta el estado

El estado ante el SII y ante el receptor se consulta por documento. Devuelve también la lista cronológica de eventos.

curl · GET /api/v1/documents/{id}/status
curl https://api.indefac.com/api/v1/documents/0199d2b4-5c1e-7a52-9a53-3f2f7f1f6c10/status \
  -H "Authorization: Bearer $INDEFAC_API_KEY"
200 OK
{
  "data": {
    "status": "accepted",
    "sii": { "trackId": "7841236590", "status": "accepted", "checkedAt": "2026-10-13T14:02:11Z" },
    "receiver": { "status": "pending" },
    "events": [
      { "type": "issued", "occurredAt": "2026-10-13T14:01:07Z" },
      { "type": "sent", "occurredAt": "2026-10-13T14:01:12Z" },
      { "type": "accepted", "occurredAt": "2026-10-13T14:02:11Z" }
    ]
  }
}

No hace falta consultar en un ciclo: suscribe un webhook y la API te avisará cuando el estado cambie.

5. Recibe webhooks

Registra una dirección de tu sistema y elige los eventos. IndeFac envía un POST con el evento y firma el cuerpo con el secreto del webhook.

curl · POST /api/v1/webhooks
curl -X POST https://api.indefac.com/api/v1/webhooks \
  -H "Authorization: Bearer $INDEFAC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://erp.miempresa.cl/indefac/eventos",
    "events": ["document.accepted", "document.rejected", "received.new"]
  }'

Cabeceras de cada entrega

X-IndeFac-EventNombre del evento, por ejemplo document.accepted.
X-IndeFac-Event-IdIdentificador del evento, igual en cada reintento y reenvío (también va en el cuerpo como eventId). Úsalo para descartar repetidos.
X-IndeFac-DeliveryIdentificador de la entrega. Cambia si pides un reenvío.
X-IndeFac-Signaturet=<unix>,v1=<hex>: v1 es el HMAC-SHA256 de <t>. seguido del cuerpo, con el secreto. Rechaza firmas con más de 5 minutos de diferencia.
HTTP
POST /indefac/eventos HTTP/1.1
X-IndeFac-Event: document.accepted
X-IndeFac-Event-Id: 0199d2b5-0f00-7b3e-9a11-2c4d6e8f0a12
X-IndeFac-Delivery: 0199d2b5-11aa-7c4e-8f0b-6a1f0c9b2d77
X-IndeFac-Signature: t=1792160531,v1=5f8a1c…

{
  "eventId": "0199d2b5-0f00-7b3e-9a11-2c4d6e8f0a12",
  "event": "document.accepted",
  "occurredAt": "2026-10-13T14:02:11Z",
  "tenantRut": "76123456-7",
  "environment": "production",
  "data": { "id": "0199d2b4-5c1e-7a52-9a53-3f2f7f1f6c10", "type": 33, "folio": 1284, "externalId": "FAC-2026-000123" }
}

Verifica la firma

C#
using System.Security.Cryptography;
using System.Text;

// X-IndeFac-Signature: t=<unix>,v1=<hex>  con  v1 = HMAC-SHA256(secreto, "<t>." + cuerpo)
static bool IsValidSignature(string rawBody, string signatureHeader, string secret)
{
    var parts = signatureHeader.Split(',').Select(p => p.Split('=', 2)).Where(p => p.Length == 2)
        .ToDictionary(p => p[0].Trim(), p => p[1].Trim());
    if (!parts.TryGetValue("t", out var t) || !parts.TryGetValue("v1", out var v1)) return false;

    // Firmas de más de 5 minutos no valen: una entrega capturada no se puede repetir después.
    var age = DateTimeOffset.UtcNow - DateTimeOffset.FromUnixTimeSeconds(long.Parse(t));
    if (age.Duration() > TimeSpan.FromMinutes(5)) return false;

    var hash = HMACSHA256.HashData(Encoding.UTF8.GetBytes(secret), Encoding.UTF8.GetBytes(t + "." + rawBody));
    var expected = Convert.ToHexString(hash).ToLowerInvariant();
    return CryptographicOperations.FixedTimeEquals(Encoding.UTF8.GetBytes(expected), Encoding.UTF8.GetBytes(v1.ToLowerInvariant()));
}

Si tu sistema no responde con un código 2xx, la entrega se reintenta al minuto, a los 5 y 30 minutos, a las 2 y a las 12 horas. Después queda marcada como fallida y se avisa al administrador. Puedes ver las entregas con `GET /api/v1/webhooks/{id}/deliveries` y pedir un reenvío con `POST …/deliveries/{deliveryId}/redeliver` (hasta 60 por hora).

Eventos

document.issueddocument.sentdocument.accepteddocument.accepted_with_remarksdocument.rejecteddocument.receiver_accepteddocument.receiver_accepted_with_remarksdocument.receiver_claimeddocument.annulledreceived.newreceived.acceptedreceived.claimedreceived.deadline_soonfolios.lowfolios.exhaustedcertificate.expiringjob.failedimport.finishedreconciliation.finished

6. Errores

Todo error tiene la misma forma. El campo code es estable y es el que tu sistema debe leer; message es para personas.

400 Bad Request
{
  "error": {
    "code": "VALIDATION",
    "message": "El documento tiene campos que no cumplen el formato.",
    "details": [
      { "field": "receiver.rut", "message": "El dígito verificador no corresponde." },
      { "field": "lines[0].name", "message": "Máximo 80 caracteres." }
    ]
  }
}
Código HTTPCódigoQué significa
400VALIDATIONUn campo no cumple el formato del SII. details trae la lista de campos.
401UNAUTHENTICATEDFalta la llave o no es válida.
403FORBIDDENLa llave no tiene el alcance que la operación requiere.
409DUPLICATE_IDEMPOTENCY_KEY_MISMATCHLa clave de idempotencia ya se usó con otro contenido.
422TOTALS_MISMATCHLos totales enviados no coinciden con los recalculados.
422FOLIO_EXHAUSTEDNo quedan folios para ese tipo de documento.
422CERTIFICATE_EXPIREDEl certificado digital de la empresa venció.
422PLAN_LIMITSe alcanzó el límite del plan y el plan no admite documentos adicionales.
422PLAN_FEATURELa función no está incluida en el plan de la empresa.
422LEGAL_ACCEPTANCE_REQUIREDFalta que el administrador acepte un texto legal de empresa.
429RATE_LIMITEDSe superó el límite de solicitudes. Espera lo que indica Retry-After.
500INTERNALError de la plataforma. Repetir la llamada es seguro gracias a la idempotencia.

SDK y colección de Postman

Si tu sistema está en .NET o en Node.js no necesitas armar las llamadas a mano: los clientes oficiales envuelven la API con idempotencia, reintentos y verificación de la firma de los webhooks.

IndeFac.Client es el paquete de NuGet para .NET 8 y .NET 10, sin dependencias.

Instalación

NuGet · IndeFac.Client
dotnet add package IndeFac.Client

Versión 0.1.0: se incluye en el repositorio del servicio (sdk/dotnet). La publicación en nuget.org está próxima.

Ejemplo de uso

C# · IndeFac.Client
using IndeFac.Client;

var http = new HttpClient { BaseAddress = new Uri("https://api.indefac.com/") };
var indefac = new IndeFacClient(http, new IndeFacClientOptions
{
    ApiKey = Environment.GetEnvironmentVariable("INDEFAC_API_KEY")!,
});

var emitted = await indefac.Documents.IssueAsync(new DocumentRequest
{
    Type = 33,
    ExternalId = "VENTA-000123",          // tu identificador: es también la llave de idempotencia
    Receiver = new Party
    {
        Rut = "76192083-9", Name = "Cliente SpA", BusinessLine = "Comercio",
        Address = "Av. Siempre Viva 123", Commune = "Santiago", City = "Santiago",
    },
    Payment = new PaymentInfo { Form = 2 },
    Lines = [new DocumentLine { Name = "Servicio mensual", Quantity = 1, UnitPrice = 100000 }],
});
Console.WriteLine($"Folio {emitted.Data.Folio} · {(emitted.Status == 201 ? "emitido" : "ya existía")}");

var pdf = await indefac.Documents.PdfAsync(emitted.Data.Id);
await File.WriteAllBytesAsync(pdf.FileName ?? "documento.pdf", pdf.Content);
  • Emisión idempotente. El cliente siempre envía una llave de idempotencia: la que le pases, el externalId del documento o una generada para esa llamada. Un reintento nunca emite dos veces.
  • Reintentos con criterio. Ante 429 respeta Retry-After; ante 502, 503, 504 o una falla de red repite solo lo que es seguro repetir.
  • Errores con código estable. Cada error trae code, el detalle por campo y un traceId para entregar a soporte.
  • Webhooks. Ambos clientes verifican la cabecera X-IndeFac-Signature sobre el cuerpo crudo de la entrega.

Prueba primero en el sandbox. Con una llave de certificación nada tiene valor tributario: es el ambiente para probar la integración. En el panel, entra a Integraciones y abre la pestaña Sandbox.

Colección de Postman

La colección trae todas las operaciones de /api/v1, generada desde la misma definición OpenAPI que la referencia. Impórtala en Postman y define las variables baseUrl y apiKey (tu llave ifk_…).

Descargar la colección de Postman

Referencia de la API

Todos los endpoints, parámetros y esquemas, generados desde la definición OpenAPI que publica la propia API.

Cargando la referencia…