Facturiadocs
Empieza aquí

Quickstart

De una cuenta nueva a una boleta emitida en el sandbox, con el ciclo queued → accepted completo y sin tocar el SII.

Todo lo de esta página ocurre en modo test: no hay tráfico al SII, no hay certificado, no hay CAF y no hay costo. Cuando cambies sk_test_ por sk_live_, la misma llamada golpea el SII real.

1. Consigue tu primera llave

Una cuenta recién creada no tiene ninguna llave todavía. La salida es la sesión de cuenta: el mismo JWT de una hora que devuelve POST /account/login, presentado a la superficie del panel.

# Registrarte (o iniciar sesión). No necesitas un navegador: es una llamada normal.
TOKEN=$(curl -sS https://api.facturia.cl/account/register \
  -H "Content-Type: application/json" \
  -d '{"email":"tu@empresa.cl","password":"..."}' | jq -r .data.token)

# Mintear la primera llave con la sesión, no con una llave que todavía no tienes.
curl -sS https://api.facturia.cl/dashboard/api_keys \
  -H "Authorization: Bearer $TOKEN" \
  -H "Facturia-Mode: test" \
  -H "Content-Type: application/json" \
  -d '{"name":"primera llave","scopes":["documents:read","documents:write"],"empresas":["*"]}'

/dashboard/* es la superficie de sesión de cuenta. Sirve los mismos recursos que /v1 — los mismos serializadores, los mismos filtros, el mismo sobre de error — autenticada con un JWT de cuenta en vez de una llave sk_, porque un navegador nunca debe sostener una llave sk_.

El modo viaja en un header en la sesión

Una sesión no tiene prefijo que cargue su modo, así que lo hace Facturia-Mode: test|live, con live por defecto. Un valor no reconocido es un 400, nunca una caída silenciosa a live.

Guarda el secreto: se devuelve exactamente una vez.

export FACTURIA_KEY=sk_test_9lQpV3fA2mKcRt7wZxYb1Nde
export EMPRESA=77928532-4

2. Crea una empresa de sandbox

En modo test la empresa nace lista para emitir: sin certificado, sin mandato y sin certificación.

curl -sS https://api.facturia.cl/v1/empresas \
  -H "Authorization: Bearer $FACTURIA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"rut":"77928532-4","legal_name":"Connect SpA"}'

Solo rut y legal_name son obligatorios. El resto lo leemos del registro de contribuyentes del SII una vez cargado el certificado, y puedes sobrescribir cualquier campo después.

3. Emite una boleta electrónica

Dos campos.

curl -sS https://api.facturia.cl/v1/documents \
  -H "Authorization: Bearer $FACTURIA_KEY" \
  -H "Facturia-Empresa: $EMPRESA" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "tipo_dte": 39,
    "items": [{ "description": "Plan Pro mensual", "quantity": 1, "unit_price": 29990 }]
  }'

La respuesta es 201 y llega de inmediato:

{
  "id": "doc_01K2R7Q4XW9M3B8ZC5YHTVJD6N",
  "object": "document",
  "mode": "test",
  "status": "queued",
  "tipo_dte": 39,
  "folio": 1,
  "issue_date": "2026-08-14",
  "totals": { "net": 25202, "iva": 4788, "exempt": 0, "total": 29990 },
  "pdf_url": "https://api.facturia.cl/v1/documents/doc_01K2R7.../pdf",
  "xml_url": "https://api.facturia.cl/v1/documents/doc_01K2R7.../xml",
  "sii": { "environment": "sandbox", "track_id": null, "estado": null },
  "created_at": "2026-08-14T13:04:11Z"
}

Fíjate en tres cosas: el folio ya está asignado (1, porque en modo test los folios parten en 1 por cada (empresa, tipo_dte)), los totales ya están calculados (una boleta trae los precios brutos, así que 29.990 se descompone en neto e IVA), y el status es queued, no accepted.

4. El ciclo de vida honesto: queuedsentaccepted

El POST es síncrono. La ida y vuelta al SII no lo es, y no fingimos lo contrario.

                      ┌───────────► rejected  (terminal)

queued ──► sent ──────┼───────────► reparo    (terminal-ish; válido pero observado)
   │                  │
   │                  └───────────► accepted ──► annulled (una NC lo anuló)

   └──► failed ──(retry)──► queued

En el sandbox el documento se asienta en accepted en aproximadamente un segundo, disparando cada webhook que dispararía un documento real. En producción el trayecto típico a un estado terminal es de 30 segundos a unos pocos minutos, y el SII puede tardar horas en sus peaks o en mantención.

curl https://api.facturia.cl/v1/documents/doc_01K2R7Q4XW9M3B8ZC5YHTVJD6N \
  -H "Authorization: Bearer $FACTURIA_KEY" -H "Facturia-Empresa: $EMPRESA"
# Los eventos del documento, del más antiguo al más nuevo
curl https://api.facturia.cl/v1/documents/doc_01K2R7.../events \
  -H "Authorization: Bearer $FACTURIA_KEY" -H "Facturia-Empresa: $EMPRESA"
{
  "object": "list",
  "data": [
    { "type": "document.created",  "at": "2026-08-14T13:04:11Z", "detail": { "folio": 1 } },
    { "type": "document.sent",     "at": "2026-08-14T13:04:19Z", "detail": { "track_id": "9000000001" } },
    { "type": "document.accepted", "at": "2026-08-14T13:04:20Z", "detail": { "estado": "DOK" } }
  ]
}

No construyas sobre polling apretado

Los webhooks son la integración prevista. Si igual necesitas hacer polling: no consultes GET /v1/documents/{document_id} más seguido que cada 10 segundos durante los primeros 2 minutos, y luego cada 60 segundos. Mejor aún, consulta el listado con status=sent&created_at[gte]=… una vez por minuto y reconcilia en bloque. El header Facturia-Poll-After en la respuesta del documento te dice el próximo momento útil, calculado por nuestro propio scheduler.

5. Baja el PDF

curl -L https://api.facturia.cl/v1/documents/doc_01K2R7.../pdf \
  -H "Authorization: Bearer $FACTURIA_KEY" -H "Facturia-Empresa: $EMPRESA" -o boleta.pdf

En modo test el PDF trae el layout real con una marca de agua SIN VALOR TRIBUTARIO.

6. Recibe el webhook en vez de preguntar

curl -sS https://api.facturia.cl/v1/webhook_endpoints \
  -H "Authorization: Bearer $FACTURIA_KEY" -H "Content-Type: application/json" \
  -d '{
    "url": "https://api.miempresa.cl/hooks/facturia",
    "enabled_events": ["document.accepted","document.rejected","document.reparo"]
  }'

El secret (whsec_…) se devuelve una sola vez. Cómo verificar la firma está en Webhooks · Firma.

Y ahora, en serio

Cambiar sk_test_ por sk_live_ cambia lo que pasa en el cable, no tu código. Lo que sí cambia es que la empresa necesita recorrer una puesta en marcha real: mandato, certificado, postulación, certificación y timbraje. Eso está en Empresas y lo maneja un solo endpoint que puedes dibujar como asistente: GET /v1/empresas/{empresa_id}/onboarding.

En esta página