Notta Docs

Estados del DTE

Todos los estados de un DTE con su categoría, cuál es terminal y qué hacer con cada uno, más la cadencia real del seguimiento ante el SII.

Emitir un DTE es asíncrono: POST /dtes responde 202 y el documento sigue viajando. Esta página es la lista completa de los estados por los que pasa, qué significa cada uno y cómo esperar el resultado sin escribir un bucle que no termina nunca.

El bloque estado

Toda respuesta que trae sii_status trae también estado, el mismo valor ya clasificado. sii_status no cambia y sigue siendo el contrato: estado se agrega. Está en GET /dtes, GET /dtes/{id}, el 202 de POST /dtes y en las tres respuestas de POST /dtes/{id}/refresh-status.

{
  "sii_status": "-11",
  "estado": {
    "code": "-11",
    "label": "procesando en el SII",
    "descripcion": "No es un estado del documento: es el código con el que FALLA la consulta de estado mientras el SII todavía no registra el envío recién subido.",
    "terminal": false,
    "poll_activo": true,
    "categoria": "en_proceso",
    "accion": "esperar"
  }
}
CampoQué es
codeEl mismo valor de sii_status. Es un string, no un enum cerrado, ver El universo del SII es abierto.
labelGlosa corta en español, para mostrar.
descripcionQué pasó y qué significa, en prosa.
terminaltrue cuando el SII ya dio su veredicto sobre el documento. Es la condición de corte de cualquier espera.
poll_activotrue cuando hay un seguimiento nuestro consultándole el estado al SII en este momento.
categoriaEnum cerrado: en_proceso · aceptado · rechazado · requiere_accion · archivistico.
accionEnum cerrado: esperar · reintentar_consulta · reemitir · contactar_soporte · accion_en_sii · ninguna.

categoria y accion son estables igual que el next_action de los errores: están diseñados para que ramifiques por código, sin leer label ni descripcion.

Los estados

CódigoQué significaTerminalCategoríaAcción
queuedEl documento se creó en Notta y está en cola para firmarse y subirse. Todavía no salió.noen_procesoesperar
sendingNotta está subiendo el documento al SII. Todavía no hay track_id.noen_procesoesperar
signedEl XML ya está firmado con tu certificado y todavía no se sube. Etapa interna de Notta.noen_procesoesperar
SOKEl SII validó el esquema del sobre. Etapa del envío, no un veredicto.noen_procesoesperar
CRTEl SII validó la carátula del sobre (los datos de tu resolución). Etapa del envío.noen_procesoesperar
FOKEl SII validó la firma electrónica del sobre. Etapa del envío.noen_procesoesperar
PDREl SII tiene el envío en proceso y todavía no lo resolvió.noen_procesoesperar
PRDEl mismo estado que PDR: el instructivo del SII usa las dos grafías.noen_procesoesperar
-11La consulta de estado falla porque el SII todavía no registra el envío recién subido. Transitorio, y es el camino normal.noen_procesoesperar
EPREl SII procesó el envío y no reportó rechazos ni reparos: el documento quedó aceptado.aceptadoninguna
RPRAceptado con reparos: el SII dejó observaciones. El documento es válido y exigible; no hay que reemitirlo.aceptadoninguna
acceptedDocumento importado del Respaldo del SII: el Servicio ya lo había aceptado cuando se emitió en tu sistema anterior. No hay envío que consultar.archivisticoninguna
RFRRechazo por firma: el SII no pudo validar la firma electrónica. No es un problema de tus datos. El folio queda quemado.rechazadocontactar_soporte
RCTRechazo por carátula: los datos de la resolución que te autoriza a facturar no calzan con los del SII. Reemitir antes de corregirlo repite el rechazo y gasta otro folio.rechazadocontactar_soporte
RSCRechazo por schema: el XML no pasó la validación XSD del SII. Es un problema de cómo se genera el documento. El folio queda quemado.rechazadocontactar_soporte
RCHEl SII aceptó el envío y rechazó este documento por su contenido. Corrige el dato observado y emite uno nuevo con folio nuevo.rechazadoreemitir
stuckEl SII no dio veredicto dentro de la ventana del seguimiento y el proceso se agotó. No es un rechazo: el documento puede estar aceptado y no lo sabemos.norequiere_accionreintentar_consulta
sin_permiso_siiEl SII respondió 106 al consultar: el RUT de tu certificado no está enrolado como usuario autorizado de la empresa. Es un fallo de la consulta, no un veredicto. Enrola al usuario en Mi SII → Usuarios autorizados.norequiere_accionaccion_en_sii

stuck y sin_permiso_sii son terminal: false a propósito: en los dos casos el SII no juzgó el documento. Por eso refresh-status sí los vuelve a consultar, y por eso no emitas un duplicado mientras estén así: quemarías otro folio por un documento que probablemente ya está aceptado.

Dos estados históricos que el filtro sigue aceptando

awaiting_sii y aceptado_con_reparos se publicaron alguna vez y hoy ningún proceso de Notta los escribe: producción no tiene ni un documento con ellos. Por eso salieron de la tabla. Si escribiste una query con ellos leyendo nuestra documentación anterior, ?sii_status=awaiting_sii y ?sii_status=aceptado_con_reparos siguen siendo valores válidos en GET /dtes y no devuelven 422: no vas a recibir filas, pero tu integración no se rompe. En código nuevo usa RPR en lugar de aceptado_con_reparos.

Cómo esperar un DTE

Hay dos caminos, y las reglas de decisión son las mismas en los dos.

  • Push (webhook): registras una URL con POST /api/v1/webhooks y Notta te hace POST con cada transición, firmada y con reintentos. No esperas nada: tu handler recibe el data con los mismos terminal, categoria y accion de abajo. Es el camino si puedes exponer una URL https pública. Ver Webhooks.
  • Pull: consultas tú. Es lo que sigue esta sección, y es además el camino de recuperación cuando una entrega de webhook no llega (GET /dtes?polled_since=).

Tres reglas, en orden:

  1. Corta por estado.terminal, nunca por sii_status === "EPR". Un rechazo también es final: un bucle que espera EPR sobre un RCH gira para siempre.
  2. Ramifica por estado.categoria, no por el código. Son cinco casillas y cubren todo el universo, incluidos los códigos que todavía no conocemos.
  3. Un código que no reconoces no es terminal: sigue esperando. Nunca lo trates como éxito.
async function esperarTerminal(id: string, token: string) {
  const limite = Date.now() + 15 * 60_000;
  while (Date.now() < limite) {
    const res = await fetch(`https://app.notta.cl/api/v1/dtes/${id}`, {
      headers: { Authorization: `Bearer ${token}` },
    });
    const dte = await res.json();
 
    if (dte.estado?.terminal) {
      switch (dte.estado.categoria) {
        case "aceptado":
        case "archivistico":
          return dte; // listo: descarga el PDF en links.pdf
        case "rechazado":
          // El folio se quemó. `estado.accion` dice si reemitir o escalar.
          throw new Error(`${dte.sii_status}: ${dte.sii_glosa ?? dte.estado.descripcion}`);
      }
    }
    // No terminal (o un código desconocido): se sigue esperando.
    await new Promise((r) => setTimeout(r, 5_000));
  }
  throw new Error("se agotó la espera; el documento sigue en vuelo");
}

El cliente de TypeScript de Notta trae este mismo bucle listo, con backoff y con el corte por estado.terminal. Todavía no está publicado en npm, así que hoy el camino es el fetch de arriba; cuando se publique, la llamada equivalente es:

const res = await client.waitForTerminal(id, { timeout: 15 * 60_000 });
if (res.ok && res.data.estado.categoria === "rechazado") {
  // el folio se quemó: hay que emitir uno nuevo corregido
}

No leas `sii_status` como si fuera un enum cerrado

El SII no publica una lista cerrada de códigos, y Notta guarda verbatim lo que responde. Un switch (sii_status) con un default que asume éxito es un bug esperando su código nuevo; un switch (estado.categoria) no, porque las categorías sí son nuestras y sí son cerradas.

Cuánto tarda: la cadencia del seguimiento

No hace falta que fuerces nada. Cada DTE emitido publica un seguimiento durable que lo lleva a su estado final por su cuenta, con esta cadencia de consultas al SII:

TramoConsultas al SII
Primeros 5 minutos11: la inmediata, más una cada 30 segundos
Del minuto 5 al 105: una por minuto
Del minuto 15 a las 15 h 45 min6: a los 5 min, 30 min, 1 h, 2 h, 4 h y 8 h de la anterior
Desde las 15 h 45 min7: una cada 24 horas

Son 29 consultas repartidas en una ventana de 7 d 15 h 45 min. Ese es el número exacto, no "7 días": si se agota sin veredicto, el documento queda en stuck y ahí sí necesita que alguien intervenga.

En la práctica el grueso se resuelve en los primeros minutos. La estadística de producción explica por qué -11 es tan común: 221 de los 283 documentos que llegaron a un estado terminal pasaron por él, con una permanencia promedio de 213 segundos y un máximo de 3 h 46 min. Ver un -11 no significa que algo salió mal; significa que el SII todavía no registró el envío.

Para enterarte de las transiciones sin preguntar, registra un webhook: cada una de esas consultas que cambia el estado dispara una entrega a tu URL. Si prefieres preguntar tú, GET /dtes/{id}/events es el endpoint canónico de pull: devuelve la historia completa en orden cronológico, con la glosa del SII, incluidos los estados transitorios que GET /dtes/{id} ya no muestra. Para muchos documentos a la vez, GET /dtes acepta sii_status y polled_since, ver Referencia API de DTEs.

refresh-status es una escotilla, no el seguimiento

POST /dtes/{id}/refresh-status existe para revivir un seguimiento que se murió. Tiene tres respuestas 2xx y dos de las tres no hacen nada:

RespuestaHTTPQué significa
refresh_enqueued202La única que actúa: se encoló una consulta nueva al SII.
already_terminal200El SII ya juzgó el documento. Puede ser un rechazo: trae sii_glosa y estado. Léelos antes de darlo por bueno.
poll_in_progress200El seguimiento sigue vivo. Trae retry_after en segundos: cuánto falta para que esta llamada deje de ser un no-op.

El gate que decide entre poll_in_progress y refresh_enqueued es el reloj: si sii_last_polled se movió hace menos de 25 h, se asume que el seguimiento está vivo y la llamada no encola nada. El umbral no es arbitrario: supera la espera legítima más larga de la cadencia (24 h) con una hora de margen. stuck y sin_permiso_sii se saltan ese gate: ahí ya no hay nadie consultando, y la escotilla es justo para eso.

curl -X POST https://app.notta.cl/api/v1/dtes/01977f2a-1b50-7000-8000-000000000001/refresh-status \
  -H "Authorization: Bearer ntt_cert_..." \
  -H "X-Cert-Id: 01977f00-0000-7000-8000-000000000abc" \
  -H "Idempotency-Key: $(uuidgen)"
{
  "id": "01977f2a-1b50-7000-8000-000000000001",
  "status": "already_terminal",
  "sii_status": "RCH",
  "sii_glosa": "Documento rechazado por el SII",
  "estado": {
    "code": "RCH",
    "label": "documento rechazado",
    "terminal": true,
    "poll_activo": false,
    "categoria": "rechazado",
    "accion": "reemitir"
  }
}

Llamarlo en bucle no acelera nada y abre una sesión más contra el SII con tu certificado por cada llamada.

El universo del SII es abierto

El instructivo del SII cierra su tabla de códigos de estado con una fila literal: «Otros (no enumerados)». El seguimiento de Notta guarda verbatim lo que responde el Servicio, así que en producción ya hay códigos fuera de la tabla de arriba: 106 y 107 viven hoy en el historial de estados de documentos reales.

Por eso el contrato está partido en dos mitades con reglas distintas:

  • estado.code es un string, no un enum. Declararlo cerrado obligaría a rechazar valores que la propia base de datos contiene.
  • estado.categoria y estado.accion sí son cerrados, porque son vocabulario de Notta. Un código que no conocemos igual cae en una casilla accionable: terminal: false, categoria: "en_proceso" y accion: "contactar_soporte". Nunca accion: "ninguna", que se leería como éxito.

Si te topas con un code que no está en esta página, trátalo como no terminal y escríbenos con el track_id del documento.

Próximos pasos

  • Referencia API de DTEs: GET /dtes/{id}/events, los filtros del listado y el contrato completo de refresh-status.
  • Webhooks: el mismo bloque estado, pero por push, con firma y reintentos.
  • Notta para agentes: el contrato de errores y el de estados, juntos, con el prompt de arranque.
  • Emitir Factura 33: la guía del caso más común, de punta a punta.
  • Errores: el catálogo de code y next_action para el otro lado del contrato.

Última actualización

En esta página