factadesarrolladores API 0.1.0-esqueleto

Referencia HTTP

Lo que vale para todas las rutas: cómo autentica, cómo se hace idempotente una operación que gasta un correlativo, qué límites tiene y qué significa cada uno de los 49 códigos de error.

URL base y ambiente

URL base https://hcnvknpsbadplnfcflxx.supabase.co/functions/v1/api-v1

Es la única URL pública de Facta. La llave selecciona el ambiente del Ministerio de Hacienda: facta_test_ transmite a apitest.dtes.mh.gob.sv y facta_live_ al servicio de producción. Las pruebas se hacen en esta misma URL con el certificado y la llave de pruebas; no se publica una segunda URL.

Todo cuerpo es application/json; charset=utf-8, y toda respuesta también, incluidos los errores. El cuerpo no puede pasar de 1 MB.

Autenticación

X-Facta-Key — la llave

facta_test_<id>.<secreto> en pruebas, facta_live_<id>.<secreto> en producción. La parte antes del punto es pública e identifica la fila; la de después se muestra UNA vez al acuñarla y no se guarda (solo su SHA-256 con pimienta).

El prefijo y el ambiente de la fila tienen que coincidir: una llave facta_test_ que apuntara a una fila de producción es 403, jamás una emisión.

Nunca en Authorization. El gateway lee esa cabecera por su cuenta.

Esta llave sola no firma. Abre nada: identifica y autentica. Para firmar hace falta además X-Facta-Sign-Key (ver los dos vaults del plan, §6).

Lista de IP autorizadas (opcional, se define al acuñar la llave): cada entrada es una dirección —190.53.1.2, 2001:db8::1— o un rango en notación CIDR —190.53.1.0/24, 2001:db8::/32—, IPv4 o IPv6. Un integrador detrás de un NAT con varias salidas pone su prefijo, no cada dirección. La lista vacía significa «desde cualquier parte». Si la llave tiene lista y tu IP no está en ninguna de sus entradas, la respuesta es ip_not_allowed (403) antes de cualquier trabajo. La IP que se compara es la del extremo que nos conecta, y una cabecera X-Forwarded-For puesta por el cliente no la cambia.

X-Facta-Sign-Key — el segundo factor

La contraseña que abre el vault de firma, donde vive el certificado del emisor. 256 bits generados por el navegador en la ceremonia de acuñado y mostrados UNA vez.

Por qué existe: en la web hay dos secretos independientes (la sesión y la llave del vault, que nunca viaja) y robar uno no basta. En una API hay un solo token, y si ese token bastara para firmar, un .env filtrado sería poder de emitir DTE reales con el certificado del cliente. Con esta separación, el token robado no firma.

Qué hacemos con ella: se convierte a bytes al entrar, se deriva la KEK por HKDF-SHA-256, se abre la envoltura y el arreglo se pone a cero. No se guarda, no se registra en ninguna bitácora y no hay hash de ella en nuestra base — el verificador es la propia etiqueta GCM de la envoltura. Lo que sí guardamos es el CONTEO de intentos fallidos: cinco seguidos en una hora suspenden la llave.

No es FACTA_UNLOCK_KEY. Esa otra (prefijo factauk_) abre sus credenciales de almacenamiento en SU servidor y no viaja nunca a Facta. Si la manda aquí, la respuesta se lo dice en vez de intentarlo.

Alcances

Cada ruta exige un alcance, y la llave los lleva escritos desde que se acuñó. Un alcance que falta es 403 forbidden_scope, y details dice cuál hacía falta y cuáles tiene.

AlcancePara quéRutas
issueEmitir, anular y registrar eventos de retorno/v1/dte, /v1/dte/prepare, /v1/dte/sign, …/invalidate, …/return
queryLeer lo emitidoGET /v1/dte, GET /v1/dte/{codigoGeneracion}
downloadÁrea de retención…/file, /v1/dte/holding
ningunoSalud y contrato/v1/status, /v1/openapi.json

Idempotencia

Identificador que el CLIENTE elige para esta operación. Obligatorio en toda ruta que no se pueda deshacer: las tres que gastan correlativo y la anulación. Sin él, un reintento por timeout quemaría un segundo número — o mandaría un segundo evento contra un documento que el primero ya anuló.

Reglas, todas verificables:

  • Alcance (llave, Idempotency-Key). Dos integradores pueden usar la misma cadena sin chocar, y ninguno puede leer la respuesta del otro.
  • Repetir la misma llave con el MISMO cuerpo devuelve la respuesta guardada, con Idempotency-Replayed: true. Eso incluye un rechazo del MH: el reintento devuelve el mismo 422, sin quemar otro número.
  • Repetir con OTRO cuerpo es 422 idempotency_key_reuse. Devolver la factura de ayer para la venta de hoy es peor que fallar. Si la primera petición ya terminó, details nombra su codigoGeneracion. Excepción: las altas de cliente y producto (POST /v1/customers, POST /v1/products). Ahí el otro cuerpo se aplica al registro que la llave creó (semántica de PATCH) y la respuesta es 200 con actualizado: true; solo si esa llave no guarda un registro vuelve el 422.
  • Mientras la primera está en vuelo, la segunda es 409 idempotency_in_flight — no se encola. El cliente reintenta.
  • TTL 24 h. Pasado eso la cadena queda libre: un reintento un día después es una venta nueva, no un reintento.

Límites de uso

Toda respuesta dice cuánto cupo queda. Los números describen la ventana que está más cerca de morder: con 5 llamadas libres en la hora y 1 en el día, lo que se anuncia es 1.

httpcabeceras de cualquier respuesta
RateLimit-Limit: 57
RateLimit-Remaining: 56
RateLimit-Reset: 3600
RateLimit-Policy: hour;q=60;w=3600, day;q=300;w=86400
CabeceraQué dice
RateLimit-LimitEl techo de la ventana más cercana a morder.
RateLimit-RemainingLo que queda de esa ventana, con esta petición ya descontada.
RateLimit-ResetSegundos hasta que esa ventana se libera del todo.
RateLimit-PolicyLas dos ventanas, hour;q=60;w=3600, day;q=300;w=86400.
Retry-AfterSegundos que conviene esperar. Solo en los errores donde esperar arregla algo.

Consultar /v1/status no gasta el cupo que reporta: se cuenta en una ventana propia y generosa, porque un monitor que pregunta cada minuto se comía el cupo de emitir.

Errores

Un error es siempre la misma forma: { "error": { "code", "message", "details" } }. El code es el contrato y no cambia; el message está escrito en español para la persona que lee un registro a las dos de la mañana, y puede reescribirse.

jsonrespuesta 422 · mh_rejected
{
  "error": {
    "code": "mh_rejected",
    "message": "[receptor.nit] NIT CONTRIBUYENTE NO EXISTE",
    "details": {
      "estado": "RECHAZADO",
      "descripcionMsg": "[receptor.nit] NIT CONTRIBUYENTE NO EXISTE",
      "observaciones": [],
      "codigoGeneracion": "E4311553-DADF-4168-829E-B6B7E50F1D41",
      "numeroControl": "DTE-03-M001P001-000000000000177"
    }
  }
}
Los 49 códigos del contrato
codeHTTPCuándo pasaQué hacer
unauthorized 401

No llegó la cabecera X-Facta-Key.

Mande la llave. Y nunca en Authorization — esa la lee el gateway.

invalid_api_key 401

La llave no existe, está mal escrita o el secreto no coincide.

Los tres casos tardan lo mismo y dicen lo mismo, a propósito. Revise que copió la llave entera, con el punto.

key_revoked 403

Alguien la revocó desde la app.

Una llave revocada no vuelve. Acuñe otra.

key_expired 403

Pasó la fecha de vencimiento que se le puso al acuñarla.

Acuñe otra desde Configuración → Llaves de la API.

key_inactive 403

La llave está desactivada.

Vuelva a activarla desde la app.

forbidden_scope 403

A la llave le falta el alcance que la ruta exige.

details.required dice cuál hacía falta y details.granted los que tiene. Los alcances de emitir y consultar se eligen al acuñar; el dueño concede catalog:read y catalog:write por llave desde Cuenta → API.

dte_type_not_allowed 403

La llave no tiene permitido ese tipoDte.

details.allowed trae la lista. Acuñe una llave que incluya el tipo.

ip_not_allowed 403

La llave tiene lista de direcciones y la suya no está en ninguna entrada.

Añada su dirección o su rango CIDR al acuñar. La dirección que se compara es la del extremo que conecta; una cabecera puesta por el cliente no la cambia.

environment_not_allowed 403

El prefijo de la llave y su fila no coinciden, o el ambiente de la llave no es el de su empresa.

Si su empresa ya pasó a producción, acuñe una llave de producción; una de pruebas no puede emitir documentos reales.

sign_key_required 401

Falta X-Facta-Sign-Key en una ruta que firma — o llegó la de apertura de almacenamiento (factauk_), que no es esta.

Mande la contraseña del vault de firma, la que empieza con factask_ y se mostró una vez al acuñar.

sign_key_invalid 401

Esa contraseña no abre el vault de firma.

details.intentosRestantes dice cuántos quedan antes de que la llave se suspenda sola.

sign_vault_locked 403

Cinco intentos fallidos en una hora. La llave queda suspendida.

Esperar no lo arregla. Hay que reactivarla desde la app.

sign_vault_missing 409

La llave no está provisionada para firmar, o el certificado que guarda es de otro NIT que el de la empresa.

Reintentar no la arregla. Acuñe la llave de nuevo, que es lo que provisiona su vault de firma.

invalid_request 400

Cuerpo ausente, que no es JSON, de más de 1 MB, un campo con la forma equivocada, o un código de generación que no tiene forma de UUID.

details.field señala el campo cuando lo hay. No gasta correlativo.

validation_failed 422

El documento no cumple el esquema oficial del Ministerio de Hacienda.

details.issues lista cada campo. No gasta correlativo: la validación corre antes de reservar.

retention_mixed_class_unsupported 422

Se pide aplicarReteRenta en un documento con alguna línea exenta o no_sujeta (items[].tipoVenta).

No se firmó nada ni se gastó correlativo. Emita las líneas exentas o no sujetas en otro documento, o no aplique la retención: la base de la retención de renta para esa mezcla todavía no está confirmada.

not_found 404

No existe el documento, o el customerId, o la ruta.

Un documento de otra empresa contesta exactamente lo mismo que uno que no existe. Es a propósito.

drive_not_configured 503

Este servicio no tiene configurado el cliente OAuth de Google Drive.

No lo arregla el cliente. Escriba a soporte; mientras tanto, las copias a Drive esperan a que alguien abra Facta.

drive_reauthorization_required 409

Google retiró el permiso de la llave larga de ese Drive (revocado, vencido o cambio de contraseña).

Una persona debe abrir Facta y pulsar «Confirmar en Google» en ese destino de Google Drive. Reintentar no lo arregla.

drive_token_failed 502

Google no contestó o rechazó la renovación por una razón distinta a un permiso retirado.

Reintente en unos segundos. No se reenvía el texto de Google.

drive_not_available_for_api 403

Se llamó a la ruta de renovación de Google Drive. Drive está desactivado para la API (decisión de seguridad del 17-oct-2026) y no cuenta como almacenamiento de la API.

No reintente. Conecte Almacenamiento Facta u otro destino compatible (S3 y compatibles, Supabase Storage, FTP/SFTP). Una empresa cuyo único destino es Drive se trata como una empresa sin destino.

method_not_allowed 405

La ruta existe, pero no con ese método.

El mensaje dice cuál es el bueno.

idempotency_key_required 400

Falta Idempotency-Key en una ruta que no se puede deshacer (emitir, anular o devolver).

Elija una cadena por operación. Sin ella, un reintento por timeout quemaría un segundo número.

idempotency_key_reuse 422

Ya usó esa Idempotency-Key con un cuerpo distinto (o, con X-Facta-Request-Fingerprint, con otra huella). Si la primera petición terminó, details trae codigoGeneracion. Ya no se devuelve en POST /v1/customers ni POST /v1/products cuando la llave creó un registro: ahí se actualiza ese registro (200, actualizado: true).

Use una nueva. Para cambiar un registro de catálogo, use PATCH con el id. Devolver la factura de ayer para la venta de hoy sería peor que fallar.

idempotency_in_flight 409

La primera petición con esa llave todavía se está procesando.

Reintente en unos segundos. No se encola a propósito: la primera puede estar gastando un correlativo ahora mismo.

prepare_token_invalid 422

El prepareToken venció, es de otra llave, o el documento cambió aunque sea un centavo.

Vuelva a llamar a /v1/dte/prepare. Lo que se firma tiene que ser lo que obtuvo el número.

return_exceeds_available 422

Se pide devolver más de lo que queda de una línea del documento, contando los eventos de retorno anteriores.

details.lineas trae, por cada línea (desde 1), lo solicitado y lo disponible. Pida como máximo lo disponible; no se firmó nada.

return_window_closed 422

fechaEvento es anterior al documento, está en el futuro o cae después del plazo de retorno (tres meses; dos años en ciertas actividades).

Registre el evento de retorno dentro del plazo. Pasado, el documento ya no admite el evento y la salida es otra operación fiscal.

return_type_not_allowed 422

El documento no es una factura (01), una factura de exportación (11) ni una de sujeto excluido (14).

Los demás tipos se corrigen con una nota de crédito, no con un evento de retorno.

return_pdf_unavailable 404

Se pidió el PDF de un evento de retorno y ya no tenemos su JSON firmado para dibujarlo, o se pidió su ticket (no existe).

Use el PDF de la respuesta original (representacionGrafica) o el que guardó en su almacenamiento. La hoja de un evento de retorno es solo carta.

has_return_events 409

Se intenta anular un documento que ya tiene eventos de retorno registrados.

Hacienda no permite anularlo (Anexo V 44.4). No se mandó nada; corrija con los eventos de retorno y, si hace falta, una nota de crédito.

include_limit_exceeded 400

Se pidió GET /v1/dte?include=dte con limit mayor que 25.

Pida limit=25 o menos y siga con siguiente. details.maximo dice el tope. Sin include la página sigue siendo de hasta 100.

not_sealed 409

Se descarga el JSON (kind=json, sin raw=true) de un documento que todavía no tiene sello, o sea, en contingencia. El Archivo DTE necesita selloRecibido.

Espere a que la contingencia se retransmita y el documento se selle, o descargue lo guardado con raw=true. details.estado dice en qué estado está.

rate_limited 429

La llave alcanzó su techo por hora o por día.

details.window, details.remaining y details.retryAfterSeconds dicen cuál y cuánto esperar.

amount_limit 429

El documento pasa del monto máximo por documento de la llave.

Se comprueba antes de reservar, así que no gasta correlativo. Suba el techo de la llave o divida la operación.

mh_rejected 422

Hacienda leyó el documento y lo negó.

Sí gastó correlativo, y details nombra codigoGeneracion y numeroControl para que la corrección reuse ESE número (§167). No es 502 porque es un veredicto sobre los datos, no una caída.

mh_unreachable 502

Reservado: la versión actual nunca lo devuelve.

Todo fallo de transporte sale como 202 contingencia, porque para entonces el documento ya está firmado y se le debe a Hacienda.

correlative_unavailable 503

No se pudo reservar el correlativo.

Reintente con la MISMA Idempotency-Key. No se gastó número.

no_storage_destination 422

Un destino propio (BYOS) reportado no está entre los destinos publicados de la llave. La emisión ya NO devuelve este error por falta de almacenamiento (desde el 6-oct-2026).

Sincronice los destinos desde la app. Para emitir sin almacenamiento disponible no hace falta nada: el documento se emite y la respuesta trae advertencias.

storage_unsupported 501

El servidor todavía no publica la capacidad o ruta de almacenamiento administrado.

El SDK conserva compatibilidad y marca la capacidad como desconocida; actualice el servidor antes de depender de ella.

storage_unavailable 503

La copia administrada no se pudo consultar o reparar temporalmente. También lo devuelve la emisión, con Retry-After, cuando el interruptor de emergencia está abierto (la copia temporal viene fallando para esa empresa y no hay otro almacenamiento): no se gastó correlativo.

Reintente solo la operación de almacenamiento. No vuelva a emitir el DTE. Si fue una emisión rechazada por el interruptor, espere Retry-After y reintente con la MISMA Idempotency-Key.

entrega_token_invalido 401

El token de entrega no corresponde a este documento y canal, está mal escrito, o el documento no tiene token (estaba en contingencia, o ningún canal se podía entregar).

Use el token exacto que devolvió la emisión (entrega.token). Si no lo tiene, el estado se lee con GET /v1/dte/{codigoGeneracion}/entrega.

entrega_vencida 410

Pasaron más de 5 minutos desde la emisión y el canal todavía no se había entregado.

No se reintenta con ese token. El documento ya está emitido; entréguelo por sus propios medios o desde la app.

canal_no_marcado 409

El documento no marcó ese canal en entrega al emitirse.

Los canales se marcan al emitir. Revise con GET /v1/dte/{codigoGeneracion}/entrega cuáles quedaron marcados.

catalog_write_disabled 403

Una ruta de escritura del catálogo (POST, PATCH o DELETE en /v1/customers y /v1/products) y el dueño de la cuenta no ha permitido administrar clientes y productos desde el API.

El dueño lo activa en Cuenta → API → «Permitir administrar clientes y productos desde el API» (eso guarda el catálogo sin cifrar en Facta DTE) y concede catalog:write a la llave. Se comprueba antes que el alcance y no cambia nada.

catalog_encrypted 409

Una ruta del catálogo y los clientes y productos de la empresa están cifrados (o una ficha quedó cifrada a medias de una conversión).

El servidor no puede leer un catálogo cifrado. El dueño lo guarda sin cifrar en Cuenta → API; hasta entonces, customerId y productId se resuelven con el SDK y el vault abierto.

catalog_duplicate 409

Ya existe un cliente con ese documento, o un producto con ese código.

details.id dice cuál y details.activo si está desactivado. Edítelo con PATCH, o reactívelo con {"activo": true}.

service_unavailable 503

Algo nuestro no contestó — la base, el registro de peticiones, el de idempotencia.

Reintente con la misma Idempotency-Key.

internal_error 500

Un fallo que no estaba previsto.

El mensaje nunca describe nuestras entrañas. Si se repite, escriba a soporte con la hora y el keyId.

El SDK de TypeScript añade un código más que el servidor nunca manda, network_error: la petición no llegó a recibir respuesta.

Las diez rutas

RutaQué haceAlcanceFirma
GET /v1/status Salud, ambiente y techos restantes ninguno no
POST /v1/dte Emitir un DTE en una sola llamada issue sí
GET /v1/dte Listar lo emitido, por rango de fechas query no
POST /v1/dte/prepare Reservar el correlativo y obtener el documento canónico issue no
POST /v1/dte/sign Firmar y transmitir un documento preparado issue sí
GET /v1/dte/{codigoGeneracion} Consultar un documento por su código de generación query no
POST /v1/dte/{codigoGeneracion}/invalidate Anular un documento que Hacienda ya selló issue sí
POST /v1/dte/{codigoGeneracion}/return Registrar el evento de retorno de una factura que Hacienda ya selló issue sí
GET /v1/dte/{codigoGeneracion}/file Descargar JSON/PDF o regenerar un ticket de un DTE sellado download no
GET /v1/dte/holding Qué hay en tu área de retención ahora mismo download no
GET /v1/storage/status Consultar capacidad administrada y destino BYOS verificado download no
GET /v1/storage/copies Consultar recibos de copias JSON/PDF/evento de invalidación download no
POST /v1/storage/copies/{codigoGeneracion}/repair Reintentar JSON/PDF de un DTE ya sellado download no
GET /v1/openapi.json El contrato de esta API, servido por ella misma ninguno no

La ruta decide, y el método se comprueba después. Una ruta que existe con el método equivocado responde 405 method_not_allowed; una que no existe, 404 not_found.

Esquemas

Los 52 objetos del contrato. Cada campo, con su tipo y si es obligatorio, está en la página de la operación que lo usa y en el contrato descargable.

EsquemaQué describe
Error

Entrega

Qué canales entregan el documento una vez emitido. Solo marca: la entrega se pide después, con el token de la respuesta, y emitir nunca espera a ella. Sin este objeto no se entrega nada (las preferencias de correo de la empresa en la app no aplican a la API).

Los dos canales vienen incluidos en toda llave que emite (issue). WhatsApp además necesita saldo en la billetera, y no se envía si la petición dice consentimiento: false; en esos casos la emisión sale igual y el canal lo reporta. Un entrega mal formado es 400 invalid_request y no gasta correlativo.

EstadoDeCanal

pendiente marcado y esperando su petición de entrega · en_proceso el proveedor aún no contesta · enviado el proveedor aceptó el mensaje · fallido no se pudo (mire motivo) · sin_credito la billetera de WhatsApp no tiene saldo · sin_consentimiento falta la atestación · no_permitido la llave no tiene el alcance del canal · vencido pasaron los 5 minutos sin entregarse · esperando_sello el documento estaba en contingencia al emitirse (no hay token; la entrega de estos queda para más adelante).

CanalDeEntrega

EntregaMarcada

Lo que la emisión devuelve cuando marcó canales. Sin token ni venceEn cuando no hay nada entregable (documento en contingencia, o ningún canal con alcance y consentimiento).

SolicitudEntrega

EntregaDeCanal

EstadoDeEntrega

Direccion

Códigos del catálogo oficial del MH (CAT-012/013), no nombres.

Receptor

Un cliente guardado (customerId) se resuelve de dos maneras según la llave. Con «Catálogo legible por la API» el servidor lo lee del catálogo que la app publicó para esa llave (sin clave de desbloqueo). Con el catálogo cifrado, el SDK de TypeScript lo resuelve desde el catálogo cifrado local y envía los campos completos; una llamada HTTP directa debe enviar el receptor completo, porque el servidor no puede descifrar las filas canónicas. Los campos inline se usan para este DTE y no crean ni actualizan una ficha del catálogo de clientes. Cuando se manda el receptor resuelto, los campos explícitos ganan sobre los datos guardados.

Un CCF (03) exige numDocumento (NIT), nrc, nombre, codActividad, direccion y correo. Una FE (01) admite el receptor entero, parcial o ninguno — sin receptor es el consumidor final anónimo, que es legal.

DocumentoRelacionado

El documento sellado que una nota ajusta. Dos formas:

  • Por código — { "codigoGeneracion": "…" } y nada más. El servidor lee el tipo y la fecha del índice de su empresa. Es la forma que usa quien emitió el original por esta misma API.
  • Entera — tipoDocumento, numeroDocumento y fechaEmision, para ajustar algo que no está en nuestro índice.

Un código que no está en su índice es 404; uno que está pero sin sello es 400 — Hacienda no selló nada que ajustar.

ReceptorExportacion

Quien compra desde fuera del país. No tiene NRC ni dirección con códigos del catálogo: tiene país, una dirección en texto y si es persona o empresa. Su descActividad es texto libre — el registro del Ministerio solo cubre a contribuyentes salvadoreños.

Exportacion

Lo que hace de una venta una exportación. La tasa es cero, y tanto la línea como el resumen llevan el tributo C3 con valor 0 — un null ahí lo rechaza Hacienda con «DEBE PROPORCIONAR UN VALOR».

ReceptorSujetoExcluido

El VENDEDOR, no el comprador — el 14 es el único documento del catálogo que corre al revés: lo emite quien compra, para documentar una compra a alguien que no está en el registro de IVA. Por eso no tiene NRC (si lo manda, se ignora: el esquema no tiene dónde ponerlo) y por eso no hay IVA en ninguna parte del documento.

Item

Una línea se describe entera (descripcion y precioUni) o nombra un producto del catálogo con productId.

productId lo resuelve el servidor solo en una llave con «Catálogo legible por la API» (llave.catalogMode: readable en /v1/status) o en cualquier llave de una empresa con el catálogo sin cifrar: la descripción, el precio, el código, el tipo y la unidad del producto completan lo que la línea omite, y los campos explícitos ganan. Un producto que no está en el catálogo publicado, o está inactivo, es 404; si el precio del catálogo tiene una base de IVA distinta a la del documento (vat_included), es 400 con reason: catalog_vat_basis — envíe precioUni en la base del documento. En una llave con catálogo cifrado la API no puede resolverlo: el SDK de TypeScript lo hace en local, y una llamada directa debe enviar descripcion y precioUni.

SolicitudDte

Un solo cuerpo para los seis tipos, y cada tipo exige lo suyo. La tabla de quién pide qué:

010305 / 061114
precio de la líneacon IVAsin IVAsin IVAsin IVA (tasa cero)el precio, tal cual
receptoropcionalinscritoinscritoextranjeroel VENDEDOR
campos propiosaplicarRetencionIvaaplicarRetencionIva, aplicarPercepcionIvadocumentosRelacionados, y numPagoElectronico solo en 06exportacionaplicarReteRenta

Mandar el campo propio de un tipo en otro es 400, no un campo ignorado en silencio: si alguien manda aplicarReteRenta en una factura, cree que está reteniendo y no está reteniendo nada.

FilaApendice

Representacion

Datos solo de la hoja (la representación gráfica). Nunca entran al JSON firmado ni viajan a Hacienda: existen únicamente para que una plantilla los dibuje (mesa y mesero de un restaurante, la placa de un taller, los puntos de un programa de lealtad…). Un documento emitido con y sin representacion produce exactamente los mismos bytes firmados (una prueba lo fija), así que no hay cifra fiscal que se pueda colar por aquí.

Se guardan con el índice del documento para que la hoja que se vuelve a descargar o a dibujar salga igual. Una plantilla imprime solo los bloques cuyos datos llegaron; las plantillas que no declaran una clave la ignoran, así que mandarla es seguro y no cambia la hoja. Solo la API los acepta: desde la aplicación esos bloques no se imprimen.

Todo texto va en una sola línea, sin caracteres de control. Hasta 20 valores en total entre las claves conocidas y las filas. En prepare este objeto se rechaza con 400: se manda en /v1/dte/sign junto al prepareToken.

SolicitudFirma

PersonaDeLaAnulacion

SolicitudAnulacion

DteAnulado

SolicitudRetorno

ItemRetorno

Lleva cantidad o noGravado, una sola.

Disponible

Lo que queda por devolver de una línea del documento.

RetornoSellado

RetornoPendiente

El evento está firmado y registrado, pero Hacienda no contestó. Es la misma estructura que RetornoSellado, sin sello.

RetornoResumen

Un evento de retorno registrado sobre un documento, en retornos de GET /v1/dte/{codigoGeneracion}.

Totales

Los calcula dte-core en el servidor. El cliente los transporta y no los recalcula nunca.

totalIva es el IVA de verdad, venga de donde venga: una FE lo lleva en resumen.totalIva y un CCF lo desglosa en resumen.tributos código 20 dejando aquel campo vacío. Leer un solo sitio acierta la mitad de las veces, y la mitad en la que falla es cada crédito fiscal.

DteSellado

DteEnContingencia

Firmado y sin respuesta del MH. Lleva los mismos campos que el sellado salvo los que solo existen con sello: no hay selloRecibido, no hay fhProcesamiento y no hay representacionGrafica — la hoja necesita el sello y el QR, así que todavía no se puede dibujar.

DtePreparado

DteConsultado

PaginaDeDocumentos

DteEnLista

Lo que hace falta para reconciliar: qué número, qué estado, cuánto y a quién. El documento entero se pide por su código con GET /v1/dte/{codigoGeneracion} o …/file.

DocumentoRetenido

Una fila del área de retención — evidencia de estado, nunca una ruta de bucket ni contenido.

ManagedStorageArtifactReceipt

ManagedInvalidationReceipt

El recibo de un archivo único: el evento de invalidación sellado, guardado bajo el código del documento ORIGINAL y junto a su json.

ManagedStorageReceipt

PlantillasDisponibles

ManagedStorageStatus

ManagedDocumentCopy

ManagedDocumentCopies

Status

Ventana

SyncRevision

Cliente

ClienteEntrada

ClienteCambio

Los mismos campos que ClienteEntrada, todos opcionales, más activo.

Producto

ProductoEntrada

ProductoCambio

Los mismos campos que ProductoEntrada, todos opcionales, más activo.

Escriba para buscar Por ejemplo: idempotencia, contingencia, 422, emitir.

↑ ↓ moverse Enter abrir Esc cerrar