Notta Docs

Catálogo de errores

Referencia de los códigos que emite la API, con next_action accionable por código y request_id para correlacionar con soporte.

Todos los errores Notta siguen el shape { code, message, request_id, hint?, next_action?, detail?, docs_url? }. El campo next_action es enumerable y está diseñado para que un agente LLM pueda razonar qué hacer sin necesidad de leer el message.

Estructura

type ErrorResponse = {
  code: string;          // "nc.over_cedida.blocked"
  message: string;       // human-readable
  request_id: string;    // siempre presente — UUID por request
  hint?: string;         // fix sugerido
  next_action?: string;  // enum estable: "confirm_override_and_retry" | "retry_with_backoff" | ...
  detail?: Record<string, unknown>; // campos públicos del error, p. ej. { "retryAfterSec": 30 }
  docs_url?: string;     // link a la fila de ESTE código en el catálogo de abajo
  issues?: unknown[];    // solo en validation_failed — issues Zod con path + message por campo
};

Incluye el request_id al contactar soporte: es el identificador que correlaciona tu request con los logs del servidor.

Toda respuesta de la API lleva además ese mismo identificador en el header X-Request-Id, no solo las de error: también los 200, y también las que no son JSON (/dtes/{id}/xml, /pdf, /muestras). Si necesitas reportar una respuesta exitosa que te parece incorrecta —una lectura que devolvió de menos, un listado que no cuadra— ese header es lo que nos deja encontrar tu request exacto y con qué parámetros salió. Guárdalo también en el camino feliz.

docs_url apunta a la fila exacta de tu código en el catálogo de abajo (p. ej. https://notta.cl/docs/errors#e-caf.not_found). Viene en todo error cuyo código esté documentado aquí; si un error no lo trae, es que su código todavía no tiene fila — nunca un link roto.

Códigos por dominio

Cuando un código no trae next_action (marcado con —), la corrección sale del code más el detail.

Autenticación y permisos

CódigoHTTPDescripciónnext_action
unauthorized401Falta el header Authorization: Bearersend_bearer_token
invalid_api_key401La API key no matchea ninguna key activaregenerate_api_key
api_key_expired401API key expiradaregenerate_api_key
api_key_revoked401API key revocadaregenerate_api_key
forbidden403Dos causas distintas, y el message dice cuál: scope — la API key no tiene el permiso requerido (dte:write, caf:write, certificates:write, …); o rol — el usuario que actúa no puede hacerlo aunque la key tenga el scope (subir un certificado o pedir folios exige owner/admin). El de rol también llega por sesión del dashboard, donde cambiar de key no ayudause_api_key_with_required_scope (solo en el de scope)
cert_id_missing400La org no tiene ningún certificado cargado y el verbo firmaupload_certificate
cert_id_ambiguous400La org tiene varios certificados vigentes — especifica cuál firma con X-Cert-Idsend_cert_id_header
org.required412La sesión de dashboard no tiene organización — completa el onboarding de org (aplica a /certificates, /caf y /api-keys)
org.rut_missing412La organización de la sesión no tiene RUT registrado — complétalo en el onboarding antes de sincronizar el RCV
onboarding_production_not_authorized403La org todavía no está habilitada para emitir en producción (palena)
csrf_origin_mismatch403Origin no permitido para esta acción (protección CSRF de la sesión de dashboard; no aplica al camino Authorization: Bearer)
csrf_blocked403Variante del anterior que responde el propio endpoint (no el middleware): mutación cross-origin con sesión de dashboard. No aplica al camino Authorization: Bearer, que no tiene sesión que proteger

Validación del payload

CódigoHTTPDescripciónnext_action
validation_failed400El body no pasa el schema — revisa issues[] campo por campo. Incluye el RUT con dígito verificador inválido, salvo en POST /organizations/solicitar-acceso, que tiene código propio (invalid_rut)fix_body_and_retry
invalid_json400El body no es JSON válidofix_json_body_and_retry
idempotency_key_missing400Falta el header Idempotency-Key (obligatorio en todo POST de emisión)send_idempotency_key_header
idempotency_key_reused409Esa Idempotency-Key ya está tomada por otro pedido tuyo que sigue esperando aprobación (otro documento, u otra acción). No se emitió ni se registró nada; pending_id dice cuál la ocupa. Reintentar el MISMO pedido con la misma clave sí devuelve el pendiente originaluse_new_idempotency_key
emisor_rut_mismatch422El rut_emisor del body no coincide con el RUT del certificado
emisor.profile_incompleto422Faltan datos del perfil del emisor para emitir (detail.missing)complete_emisor_profile
items_empty400items vacío
items_exceed_max400items supera el máximo (límite SII; detail.count)
amount_arithmetic400Los montos declarados no cuadran aritméticamente
references_required400El tipo_dte exige references y no vienen
reference_invalid_cod_ref400cod_ref fuera del enum 1/2/3
text_correction_must_be_zero400NC de corrección de texto (cod_ref 2) debe llevar montos en cero
dte.tipo_not_supported400tipo_dte no soportado por este pipelinefix_tipo_dte_and_retry
dte.34.invalid_exenta422DTE 34 (Factura Exenta) con items afectosfix_tipo_dte_and_retry
dte.41.afecto_not_allowed422Boleta Exenta 41 con items afectosfix_tipo_dte_and_retry
dte.46.invalid_afecta422DTE 46 (Factura de Compra) con items exentos — es una operación 100% afecta (cambio de sujeto)remove_exento_items_and_retry
dte.mixed_retencion_references422Una NC/ND mezcla una referencia con retención total de IVA (Factura de Compra 46) y otra sin retención — no hay un total coherente para todo el documentosplit_into_separate_documents_by_retencion
dte.type.prod_not_certified422Tu organización no tiene ese tipo de documento habilitado para emitir en producción (gate por tipo). El SII autoriza los tipos de documento por empresa: escríbenos para revisarlocontact_support_to_enable_type
dte.sii_env_credential_mismatch422El sii_env del cuerpo dice un ambiente y tu credencial emite en el otro (detail.solicitado y detail.credencial). El ambiente sale de la API key, no del cuerpo: mandar el campo es opcional y sirve de confirmación. No se emitió nada. Para emitir en el ambiente que pediste, usa una API key de ese ambiente. Distinto de sii_env_mismatch, que es un CAF de otro ambienteuse_api_key_matching_sii_env
dte.field_too_long422Un campo supera el maxLength que exige el XSD del SII (detail trae field, element, maxLength, actualLength) — atrapado antes de firmar, sin quemar folioshorten_field_and_retry
idempotency_conflict409Mismo Idempotency-Key con otro body — detail.existingDteId apunta al DTE original

Lectura y estado del DTE

CódigoHTTPDescripciónnext_action
not_found404El recurso no existe o pertenece a otra org (DTEs y boletas). El mismo código responde cuando el endpoint no existe (método o ruta no soportados bajo /organizations/* e /invitations/*): si no reconoces la ruta que llamaste, revisa el método y el path antes que el id
dte.xml.not_yet_available404El DTE todavía no tiene XML — el workflow de firma sigue corriendopoll_self
dte.pdf.not_signed409El DTE existe pero no está firmado — el PDF requiere el TED (síguelo en xml_url)poll_self
dte.list.cursor_invalido422El cursor de GET /dtes no decodifica, viene alterado, o fue emitido con otro sort/dir — reintentar con el mismo token no lo arreglafix_query
dte.list.status_desconocido422sii_status trae un valor fuera del vocabulario del SII (message lista los válidos) — devolver 0 filas en silencio ocultaría un typofix_query
dte.totales.query_invalid400El periodo de GET /dtes/totales no tiene formato YYYY-MM — quítalo para el mes en curso
pending_action_not_found404No existe una solicitud pendiente con ese id en esta empresa. El id es el pending_id del 202 que devolvió tu pedido; una solicitud resuelta hace mucho puede haberse borrado por retenciónretry_original_request
pdf_url.not_configured503El entorno no tiene configurada la firma de enlaces de PDF (DTE_PDF_URL_SECRET) — no es un problema de tu documento
document.not_found404El documento no existe, es de otra org, o el id no es un UUID. Las compras del RCV tampoco son reusables como plantilla y responden lo mismo: no se filtra cuál de las razones fue
document.not_templatable422Solo las facturas 33/34 admiten reusarse como plantilla
dte.list.tipo_invalido422tipo_dte no es el código numérico de un documento (33, 52, 61…) — devolver la lista sin filtrar por un typo es indistinguible de "no tienes documentos de ese tipo"fix_query
dte.list.folio_invalido422folio no es un entero de hasta 10 dígitos — más allá de ese largo la conversión perdería precisión y buscaría otro foliofix_query
dte.list.rut_receptor_invalido422rut_receptor no tiene forma de RUT (acepta puntos, espacios y k minúscula; el dígito verificador no se valida)fix_query
dte.list.fecha_invalida422fecha_emision_desde/fecha_emision_hasta no son YYYY-MM-DDfix_query
dte.list.polled_since_invalido422polled_since no es un instante ISO-8601fix_query
dte.list.receptor_estado_invalido422receptor_estado no es uno de los cinco del vocabulario (reclamado, aceptado, en_plazo, aceptado_tacito, sin_info) — son los mismos que publica cada filafix_query
dte.refresh_status.no_track_id409El DTE aún no fue subido al SII — no hay track_id que consultarwait_for_emit_workflow
dte.refresh_status.no_cert409La org no tiene certificado activo para autenticar la consulta de estado
dte.resend_delivery.not_wired501El reenvío del DTE al receptor no está disponible en este entorno
dte.resend_delivery.tipo_not_supported422El tipo_dte no se envía al receptor (las boletas 39/41 van a consumidores por el pipeline REST)no_action
dte.resend_delivery.no_email422El DTE no tiene correo del receptor — incluye correo_receptor en el body para reenviarprovide_correo_receptor
dte.resend_delivery.no_aplica_cert422DTE de certificación cuya única dirección es la casilla de intercambio del SII, que no recibe documentos de prueba — incluye correo_receptor para mandárselo a una personaprovide_correo_receptor
dte.resend_delivery.importado_no_reenviable422El documento se importó del Respaldo del SII: Notta no lo emitió ni lo envió, así que no puede reenviarlo — descarga el PDF o el XML y mándalo por otro canaldownload_pdf_or_xml

Muestras impresas (certificación)

CódigoHTTPDescripciónnext_action
dte.muestras.empty422No hay DTEs firmados (33/34/56/61) para armar el lote de muestrasemit_and_sign_dtes_first
dte.muestras.no_samples422No hay DTEs firmados para enviar como muestras al SIIemit_and_sign_dtes_first
dte.muestras.too_large422El lote supera el límite de 3 MB del SIIpaginate_into_multiple_emails
dte.muestras.send_not_configured422El remitente de email no está configurado en el servidorcontact_support
dte.muestras.not_sent422El envío no salió por una razón no mapeada — message trae el motivo
dte.muestras.send_not_wired501El envío de muestras no está disponible en este entorno

Certificado y folios (CAF) en la emisión

CódigoHTTPDescripciónnext_action
cert.missing412Tu organización no tiene un certificado digital activo. Lo pide POST /caf/request antes de timbrar (el SII no firma la solicitud sin él) y el wizard de postulaciónupload_certificate
cert_not_found404No hay certificado digital cargado para la org
cert_expired422Certificado expirado (detail.expiredAt)
cert_subject_mismatch422El RUT del certificado no corresponde al emisor
cert.kms.auth_failed422Falló el descifrado del certificado (detail.phase) — rotación de KEK o seed inconsistentereseed_certificate
caf_not_found404No hay CAF cargado para ese tipo de DTE (detail.tipoDte)
caf_expired422CAF vencido (detail.expiredAt)upload_caf
caf_exhausted422CAF sin folios disponibles (detail.nextFolio)upload_caf
sii_env_mismatch422El CAF corresponde a otro ambiente SII (certificación vs producción) que el del DTE
dte.production_resolucion_missing409Falta la Resolución Exenta del SII (NroResol/FchResol) para emitir a producción — se ingiere del email del SII a la casilla del emisorcapture_sii_resolucion
dte.46.prod_not_certified422Tu organización no tiene el DTE 46 (Factura de Compra) autorizado para producción. Es el mismo gate por tipo de dte.type.prod_not_certified, con código propio por compatibilidademit_dte_46_in_cert

Gestión de certificados (/certificates)

CódigoHTTPDescripciónnext_action
cert.invalid_request400Falta file, password o label en el form-data
cert.invalid422No se pudo leer el .p12 (corrupto, sin llave privada, o clave equivocada)
cert.duplicate409Ese certificado (misma huella SHA-256) ya está cargado para la org
cert.not_found404El certificado no existe para la org
cert.expired409El certificado está vencido; no puede quedar como predeterminado
cert.last_remaining409Es el único certificado de la org y borrarlo la dejaría sin firma para timbrar, emitir o enviar al SII. Para rotar: sube el reemplazo y recién después elimina el anteriorupload_certificate

Gestión de CAF (/caf)

CódigoHTTPDescripciónnext_action
caf.invalid_request400Falta file en el form-data
caf.parse_error422El XML del CAF no trae los campos requeridos (RE/TD/D/H)
caf.wrong_tipo422El CAF subido no corresponde al tipo_esperado del recuadro
caf.no_certificate422No hay certificado cargado — sube el certificado antes del CAF
caf.duplicate409Ese rango de folios ya está registrado
caf.not_found404El CAF no existe para la org
caf.has_accepted_dtes409No se puede eliminar: tiene DTEs aceptados por el SII (retención legal de 6 años)
caf.has_ceded_dtes409No se puede eliminar: un DTE del CAF fue cedido vía RPETC
caf.request_in_flight409Ya hay una solicitud reciente de folios para este tipo — espera unos minutos antes de reintentar
caf.request_failed502La solicitud de folios al SII falló — message trae el motivo
caf.grant_rejected502El SII no concedió el folio (rechazo del timbraje) — message/data traen el detalle
caf.cert_not_authorized502El certificado activo no está autorizado a solicitar folios (timbrar) para esta empresa en el SII — revisa los usuarios autorizados en Mi SII o cambia el certificado activo
caf.type_not_prod_certified409Tu organización no tiene ese tipo de documento autorizado para producción, así que no se pueden solicitar folios de producción para él
caf.auto_requires_production409El timbraje automático (PUT /caf/auto) solo se activa con la empresa autorizada en producción; en certificación el proceso lo conduce la Puesta en Marcha

Situación tributaria de la organización y solicitud de acceso

Estos códigos no vienen de un documento: vienen del RUT de la organización, o de quién puede operarla. Ninguno se arregla reintentando.

CódigoHTTPDescripciónnext_action
situacion_tributaria.bloqueada409El SII confirmó que este RUT todavía no puede emitir: no existe en el SII, o no registra inicio de actividades. El campo estado del cuerpo dice cuál de los dos (rut_inexistente | sin_inicio_actividades). Lo responden seis entradas, y no todas significan lo mismo: POST /organizations lo responde ANTES de crear nada — el RUT de una organización es inmutable, así que no queda ninguna fila que arreglar, hay que volver a mandar el POST con el RUT corregido. Las otras cinco (POST /certificates, POST /onboarding/target-types, cualquier mutación bajo /onboarding/*, POST /organizations/me/sii-system y POST /organizations/me/onboarding-mode) actúan sobre una organización que YA existe y sólo quedó bloqueada — ahí sí hay que resolverlo en el SII y revalidar (POST /organizations/me/revalidar-situacion). Subir otro certificado o reintentar no cambia nada en ninguno de los dos casos
situacion_tributaria.rate_limit429Demasiadas consultas de situación tributaria seguidas para el mismo usuario. El freno tiene dos ejes: volumen por minuto y una sola consulta en vuelo a la vez — el SII frena por conexiones, no por requests. retry_after_seconds trae la espera mínima
invalid_rut400El RUT enviado a POST /organizations/solicitar-acceso no pasa el dígito verificador. Es el único endpoint con código propio para esto; en el resto un RUT inválido responde validation_failed
access_request.rate_limit429Alcanzaste el máximo de solicitudes de acceso por hora. Se cuenta por solicitante, no por organización: pedir acceso a RUTs distintos consume el mismo cupo
access_request.not_found404El token del enlace de decisión no corresponde a ninguna solicitud. Lo emite POST /organizations/solicitudes-acceso/decidir, un endpoint público y sin credenciales —el dueño abre el enlace del correo donde sea, el token es la única prueba—, así que este 404 también responde a un token inventado
access_request.expired410La solicitud de acceso caducó (7 días sin decidirse) o la organización destino ya no existe. No se puede revivir: hay que volver a pedir acceso

Notas de crédito y débito (compliance)

CódigoHTTPDescripciónnext_action
dte.nota_credito.fuera_plazo422NC fuera del plazo legal de 6 meses (Ley 21.398)consult_lawyer_or_use_correction_path
nc.over_cedida.blocked422Factura referenciada cedida vía RPETC con aceptación irrevocable ≥8 días (Corte Suprema 2025)confirm_override_and_retry
dte.nota_credito.cedida_override_required409Mismo bloqueo de cesión, detectado por el chequeo de emisión: requiere override explícitoconfirm_override_and_retry
dte.nota_credito.invalid_cod_ref422cod_ref inválido para NC — usa 1 (anula), 2 (corrige texto), 3 (corrige montos)fix_cod_ref_and_retry
dte.nota_credito.reference_tipo_unsupported422NC 61 solo referencia Factura 33, Factura Exenta 34 o Factura de Compra 46use_factura_33_or_34_reference
dte.nota_credito.plazo_anulacion_override_required409La NC que ANULA debía emitirse en el mismo período tributario o el siguiente (Res. Ex. SII 45/2003). Fuera de plazo el SII la acepta igual, pero el IVA se recupera por petición administrativa: reintenta con override_plazo_anulacionconfirm_override_and_retry
dte.nota_credito.invalid_reason422nc_reason fuera del catálogo, o incoherente con el cod_ref de la referenciafix_reason_and_retry
dte.nota_debito.interese_moratorio_legal422Intereses moratorios legales no se documentan con DTE (Oficio SII 2011/2020)consult_oficio_2011_2020
dte.nota_debito.invalid_reason422nd_reason fuera del enum válido (detail.validReasons)fix_reason_and_retry
dte.nota_debito.reference_tipo_unsupported422ND 56 solo referencia Factura 33, Factura Exenta 34, Factura de Compra 46 o Nota de Crédito 61use_supported_reference_tipo
dte.reference.single_required422Anular (cod_ref 1) o corregir texto (cod_ref 2) admite un único documento de referencia (Formato DTE v2.5 §E); solo corregir montos (3) admite variossplit_into_one_note_per_document
dte.reference.cross_env422El folio referenciado no existe en el ambiente de emisión, pero sí en el otro: certificación y producción llevan folios independientesemit_in_the_same_sii_env

Guía de despacho (52)

CódigoHTTPDescripciónnext_action
dte.52.dispatch_required400DTE 52 requiere el bloque despachoinclude_despacho_and_retry
dte.52.invalid_traslado422ind_traslado fuera de los valores soportados (detail.supported)fix_ind_traslado_and_retry
dte.52.transportista_required422Falta el transportista (detail.field) — Res. 154/2025
dte.52.conductor_required422Falta el conductor (detail.field) — Res. 154/2025
dte.52.vehiculo_required422Falta el vehículo (detail.field) — Res. 154/2025
dte.52.parent_invalid500Inconsistencia interna con el documento padre referenciado
guia.tipo_invalido409El documento no es una Guía (detail.tipoDte ≠ 52): esta vía anula solo el tipo 52 — para una factura, emite una Nota de Crédito (61)use_nota_credito_para_factura
guia.ya_facturada409La Guía (detail.folio) ya fue facturada: una Factura la referencia, así que no se puede anular solaanula_la_factura_via_nota_credito

SII y errores transitorios

CódigoHTTPDescripciónnext_action
sii_rejection422El SII rechazó el DTE — detail.code y detail.glosa traen el veredicto
sii_unavailable503SII caído transitorio (detail.retryAfterSec)
sii_invalid_track_id502El SII no reconoce el track id del envío
dte.tipo_cambio_unavailable503Solo exportación (110/112): la fuente de tipo de cambio no respondió. El documento no se emitió y no se usó folio, así que reintentar con la misma Idempotency-Key es seguro. Si la moneda no tiene fuente, el rechazo llega antes como validation_failedretry_later
rate_limit429Límite de tasa superado (detail.retryAfterSec) — ver Límites de tasa
internal_error500Error no mapeado; el detalle queda en los logs del servidor
empresa.not_found404No hay una empresa con ese id en el padrón de tu organización (GET /sii/empresas/{id})
empresa.conflict409Ya existe una empresa con ese RUT en tu padrón — edítala en vez de crearla
shopify.cert_not_found404La instalación de la app de Shopify referencia un certificate_id que no existe en esa organización
shopify.cert_rut_mismatch422El rut_emisor de la instalación de Shopify no coincide con el titular del certificado

Recepción de compras — acuses y reclamos (/recepcion)

Registrar un acuse o un reclamo va ante el SII por SOAP y le avisa a tu proveedor: sus rechazos son del SII, no de Notta.

CódigoHTTPDescripciónnext_action
recepcion.query_invalid400page y pageSize deben ser enteros, con pageSize entre 1 y 500
recepcion.no_encontrado404No hay un documento recibido con ese id en tu organización
recepcion.evento_invalid_input400accion no es una de las que el registro admite — el message lista las válidas
recepcion.rar_tipo_no_soportado422El SII no registra acuses ni reclamos para ese tipo de documento (tipoDte dice cuál era)
recepcion.rar_evento_rechazado422El SII rechazó el registro. codResp es su código y detail su glosa — el veredicto es del SII
recepcion.rar_cedible_warning409El documento es cedible y el reclamo puede afectar una cesión: el SII advierte antes de registrarlo. Trae requiresOverride: true; para seguir, el pedido debe volver con la confirmación explícita
mcp_rate_limited429Se superó el tope de acciones que una conexión MCP puede registrar en el SII. retry_after_seconds y el header Retry-After dicen cuánto esperar

Boletas

CódigoHTTPDescripciónnext_action
boleta.credentials.not_found404Falta la clave tributaria SII de la orgconfigure_sii_credentials
boleta.rest.unavailable502Endpoint REST de boletas del SII no disponible (detail.retryAfterSec)retry_with_backoff
boleta.rest.token_expired401Token del pipeline REST expiradoreauthenticate
boleta.rest.pipeline_error500El pipeline REST falló en una fase interna (detail.phase)operator_review
boleta.xml.not_yet_available404La boleta todavía no tiene XML — el pipeline sigue corriendopoll_self
validation_error422Un campo del body no pasa el schema. En POST /dtes/{id}/resend-delivery es el correo_receptor; en el batch de boletas, el payload — ahí el detalle viene en errors[]
boleta.batch.exceeds_max422El batch supera el máximo (detail.max)split_batch
boleta.batch.idempotency_conflict409Posición del batch ya ocupada (detail.batchId)poll_existing_or_change_key
boleta.batch.not_found404El batchId no existe para la orgverify_batch_id

BHE

CódigoHTTPDescripciónnext_action
bhe.credential.invalid401Clave tributaria BHE no encontrada o rechazada (detail.reason)configure_bhe_credential
bhe.plazo_anulacion.expired422BHE fuera del plazo de 3 meses para anularconsult_sii_or_manual_rectification
bhe.retencion.mismatch422La retención enviada no concuerda con la tasa legal del año (detail.expected)use_calculated_retencion
bhe.receptor.tipo_invalid422Tipo de receptor inválido para BHEfix_receptor_and_retry
bhe.rest.unavailable502Endpoint BHE del SII (loa.sii.cl) no disponible (detail.retryAfterSec)retry_with_backoff
bhe.rest.token_expired401Token del pipeline BHE expiradoreauthenticate
bhe.rest.pipeline_error500El pipeline BHE falló en una fase interna (detail.phase)operator_review
bhe.not_found404La BHE no existe para la org

Billing

CódigoHTTPDescripciónnext_action
billing.free_tier_exceeded402Cuota mensual del plan alcanzada (detail.usedThisMonth / detail.limit) — suscríbete a un plan superior o espera al próximo períodoupgrade_plan

Infraestructura (transitorios del servidor)

Reintenta con backoff; si persisten, contacta soporte con el request_id.

CódigoHTTPDescripciónnext_action
db.unavailable503La base de datos no está disponible
db.pool_exhausted503El pool de conexiones a la base de datos está saturado — reintenta
cert.kms_unavailable503El KMS que cifra certificados no está disponible
caf.kms_unavailable503El KMS que cifra CAFs no está disponible
cert.insert_failed500Falló la persistencia del certificado
caf.insert_failed500Falló la persistencia del CAF

RCV y RCOF

CódigoHTTPDescripciónnext_action
rcv.sync.failed502El sync del RCV falló contra el SII (detail.periodo)retry_sync
rcv.unauthorized401Certificado digital rechazado por el SII para RCVconfigure_sii_credential
rcv.period.invalid422Periodo inválido — debe ser YYYY-MM y no estar en el futuro (detail.reason)fix_period_and_retry
rcof.no_data422Sin boletas 39/41 emitidas en esa fecha — no se genera RCOF (detail.fecha)verify_fecha
rcof.aggregation_error500Falló la agregación de boletas del día para el RCOF
rcof.build_error500Falló la construcción del XML del RCOF
rcof.upload_error502El envío del RCOF al SII falló (detail.phase)retry_resend
rcv.not_found404No hay snapshot del RCV para ese período — hay que sincronizarlo primero con POST /rcvsync_first
rcv.query.invalid422Falta o es inválido `?type=issuedreceived (issuedson tus ventas;received`, tus compras)
rcv.cursor.invalid422El cursor no decodifica — usa el next_cursor de la respuesta anterior sin modificarlofix_query
rcv.input.invalid422El cuerpo de POST /rcv no valida — debe ser {rut, periodo, type?, force?}fix_input

Webhooks salientes (/webhooks)

CódigoHTTPDescripciónnext_action
invalid_webhook_url422La URL de callback no es https pública: apunta a localhost, a un rango privado, al servicio de metadata de la nube o trae un puerto propio. Se revisa al registrar y en cada entrega, así que un dominio que empiece a resolver a una IP privada deja de recibiruse_public_https_url
webhook_endpoint_disabled409POST /webhooks/{id}/test sobre una suscripción apagada (active: false): no se encola nada. El porqué viaja en detail.disabled_reason. Una suscripción apagada no se reactiva: bórrala y regístrala de nuevo, y despliega el secret nuevo en tu verificadorrecreate_webhook_endpoint

Los demás códigos de /webhooks son los comunes: idempotency_key_missing y invalid_json (400), unauthorized (401), forbidden si a la API key le falta webhook:read o webhook:write (403), validation_failed si la url o los events no cumplen el schema (422), y not_found si el id no existe, es de otra empresa o es del otro ambiente (404).

Ejemplo: factura cedida

{
  "code": "nc.over_cedida.blocked",
  "message": "NC inoponible al cesionario: la factura referenciada (folio 1234) tiene una cesión RPETC aceptada irrevocablemente (Doctrina Corte Suprema 2025).",
  "hint": "Factura cedida via RPETC con cesión irrevocable (≥8d). Set override_cedida=true en el body con respaldo operacional firmado.",
  "next_action": "confirm_override_and_retry",
  "detail": { "folioRef": 1234 },
  "request_id": "9f3b2c61-7d4a-4e0b-9c2d-1a5e8f3b7c90"
}

Rechazos del SII (al subir el DTE)

Cuando el SII recibe el EnvioDTE corre una cascada de validadores: XSD → schema → firma → carátula → timbre. Cada validador enmascara al siguiente, así que corregir un rechazo suele destapar el próximo en ese mismo orden.

El getEstUp del SII devuelve solo el código de cabecera (RFR/RCT/EPR); el sub-código accionable (p. ej. CRT-3-19, TED-2-510) llega por email del SII. Notta lo expone en sii_glosa.

RechazoSignificadoAcción
RFRRechazado por Error en Firma (canonicalización c14n / xmlns heredado / charset de la firma)Re-firmar el DTE con la canonicalización y el encoding correctos
CHR-00002Línea muy larga o encoding latin1 — aflora como RFR/RSCCorregir el encoding/largo de línea y re-firmar
RCTRechazado por Error en CarátulaCorregir la Carátula y reenviar
CRT-3-19Sub-código de RCT: Fecha/Número de Resolución inválido (FchResol/NroResol)Corregir la resolución y reenviar
TED-2-510Reparo: Firma del Timbre Electrónico (TED/FRMT) incorrecta — el DTE queda en EPR con reparoRe-firmar el timbre sobre el DD canónico y reenviar

RFR — Rechazado por Error en Firma

La firma se rompió porque los bytes canónicos que el SII verifica no son los que se firmaron: un xmlns heredado que el c14n omite al firmar e incluye al verificar, o un XML re-serializado después de firmar.

Cómo verificarlo: ejecuta un roundtrip local de firma + verificación sobre los bytes exactos que van al wire. Si el elemento firmado (SetDTE/Documento) solo hereda el namespace sin declararlo, el self-verify falla — redeclara xmlns explícito y firma con c14n-20010315 (el c14n exclusivo lo rechaza el XSD del SII).

CHR-00002 — línea muy larga o charset inválido

El validador de charset cortó el envío porque alguna línea del XML supera los 4090 caracteres o los bytes no van en ISO-8859-1.

Cómo verificarlo: mide el largo de línea máximo del XML enviado y confirma que el wire va como latin1, con saltos de línea entre las secciones mayores (Encabezado, Detalle, TED).

RCT — Rechazado por Error en Carátula

La carátula declara datos que el SII no tiene registrados para el emisor: RutEmisor/RutEnvia que no corresponden, o una resolución (FchResol/NroResol) inválida.

Cómo verificarlo: el sub-código del email del SII apunta al campo exacto (p. ej. CRT-3-19 es la resolución). Compara cada campo de la carátula contra lo registrado para la empresa en el SII antes de reenviar.

CRT-3-19 — Fecha/Número de Resolución inválido

La FchResol no es la fecha que el SII tiene registrada para tu empresa: en certificación el comodín 2014-08-22 no sirve para empresas recién postuladas — el SII espera la fecha real de postulación.

Cómo verificarlo: reenvía con FchResol = fecha en que la empresa postuló como emisor electrónico en maullin y NroResol = 0 (válido para todo emisor en certificación). Si el envío pasa, el comodín era el problema.

TED-2-510 — firma del timbre incorrecta

La firma FRMT se calculó sobre los bytes literales del DD, pero el SII la verifica sobre el DD canónico con el whitespace entre elementos colapsado.

Cómo verificarlo: verifica la FRMT localmente contra la clave pública del CAF usando dd.replace(/>\s+</g, "><") como input. Si verifica con la forma canónica y falla con la literal, re-firma el timbre sobre la canónica (preservando los bytes literales del CAF en el wire).

Corrección automática

Notta corrige la mayoría de estos rechazos automáticamente; este catálogo te ayuda a entender el sii_glosa cuando un DTE queda en un estado de rechazo.

Cómo manejar errores en código

const res = await fetch("https://app.notta.cl/api/v1/dtes", { /* ... */ });
if (!res.ok) {
  const err = await res.json();
  switch (err.next_action) {
    case "retry_with_backoff": return retryLater(err.detail?.retryAfterSec);
    case "regenerate_api_key": return notifyOps(err);
    case "confirm_override_and_retry": return promptUser(err);
    default: throw new Error(`${err.code} (request_id: ${err.request_id})`);
  }
}

Buenas prácticas

  • Decide por error.code (y next_action cuando viene), nunca por string-matching del message: el texto puede cambiar, los códigos no.
  • next_action es enumerable y estable — los valores existentes no cambian de semántica; solo se agregan nuevos.
  • No reintentes los 4xx salvo 429: un 400/422 va a fallar igual hasta que corrijas el payload. Los 502/503 transitorios sí se reintentan con backoff.
  • Loguea el request_id de cada respuesta en tu sistema —del header X-Request-Id, que viene también en los 200—: acelera cualquier diagnóstico con soporte, y es lo único que permite investigar una respuesta exitosa pero sospechosa.

Próximos pasos

  • Límites de tasa — el detalle del 429 y el patrón de backoff para errores transitorios.
  • Referencia API de DTEs — los endpoints que emiten estos códigos.
  • Nota de crédito (61) — el flujo completo detrás de nc.over_cedida.blocked y el plazo de 6 meses.
  • Webhooks — el push firmado que te avisa de un rechazo sin que preguntes.

Última actualización