Mis documentos: el detalle con sus archivos y el listado¶
Dos endpoints de sólo lectura sobre los e-CF que usted ya emitió. No disparan nada contra la DGII —no consultan su estado en vivo, para eso está consultas-dgii.md— sólo leen lo que ya tenemos registrado y lo que ya se generó.
| Endpoint | Para qué |
|---|---|
GET /api/v1/documents/{identificador} |
El detalle de un e-CF y, cuando ya tiene validez fiscal, su XML firmado y su PDF en base64 |
GET /api/v1/documents |
Listado paginado de sus e-CF emitidos, con filtros |
Los dos exigen el permiso documents:read (ver tokens-y-permisos.md), lo incluyen los
perfiles integration y read-only, y no hay nada más que pedir para bajar los archivos: no existen recursos
aparte para el XML ni el PDF, ni URL firmadas, ni un segundo paso. Quién es la empresa se declara distinto en cada uno:
- el detalle se pide por el identificador del documento (el RNC del
emisor seguido del e-NCF) y la empresa es su prefijo: no lleva
emisor_rnc; - el listado no habla de un documento concreto y lleva el parámetro
emisor_rnccon el RNC de la empresa cuyos documentos se quieren ver.
En los dos, su token tiene que estar habilitado para esa empresa.
Pensados para un portal de sólo consulta o para una pantalla de "Mis documentos" dentro de su propio sistema: sirve el mismo token con el que ya emite, no necesita uno nuevo.
El listado y el detalle son de la empresa: incluyen todos sus e-CF, los haya enviado su sistema u otro que también la atienda.
El detalle de un e-CF, con sus archivos¶
curl -s "https://endpoint-test.emision.do/api/v1/documents/133802074E310000000001" \
-H "Authorization: Bearer <su-token>" \
-H "Accept: application/json"
Es la misma respuesta que da el seguimiento del documento (estado, TrackID,
errores, entrega al receptor, correo al comprador…) más el identificador y la lista archivos:
{
"status": "success",
"message": "Consulta exitosa.",
"document": {
"document_id": 1234,
"ecf_type_code": "31",
"e_ncf": "E310000000001",
"identificador": "133802074E310000000001",
"track_id": "a1b2c3d4-...",
"status_code": 4,
"status": "accepted_dgii",
"status_description": "La DGII aceptó el e-CF: tiene validez fiscal.",
"is_valid_fiscally": true,
"…": "…",
"archivos": [
{
"tipo": "pdf",
"nombre": "133802074E310000000001.pdf",
"mime": "application/pdf",
"contenido_base64": "JVBERi0xLjQK"
},
{
"tipo": "xml",
"nombre": "133802074E310000000001.xml",
"mime": "application/xml",
"contenido_base64": "PD94bWwgdmVyc2lvbj0iMS4wIj8+"
},
{
"tipo": "respuesta_dgii",
"nombre": "133802074E310000000001-respuesta-dgii.json",
"mime": "application/json",
"contenido_base64": "eyJjb2RpZ28iOjEsImVzdGFkbyI6IkFjZXB0YWRvIn0="
}
]
}
}
(Los contenido_base64 del ejemplo están abreviados, y "…": "…" resume el resto del detalle, que es el de
siempre.)
El track_id sigue las mismas reglas que en toda la API: null mientras el documento no se haya
transmitido, el asignado al transmitir —el documento entra en in_process_dgii con él— y conservado
en los estados finales; en la factura de consumo (32) por debajo de RD$250.000 es null para siempre,
porque el resumen no tiene TrackID (estados.md).
Qué archivos puede traer¶
Cada elemento de archivos tiene los mismos cuatro campos: tipo, nombre (el nombre con que guardarlo),
mime y contenido_base64 (el contenido del archivo, codificado en base64). Salen siempre en este orden,
y sólo los que existan:
tipo |
Qué es | nombre |
mime |
Cuándo está |
|---|---|---|---|---|
pdf |
La representación impresa del e-CF, con su código QR. | {identificador}.pdf |
application/pdf |
Se genera unos segundos después de la aceptación (ver abajo). |
xml |
El e-CF completo, firmado: lo que usted está obligado a conservar. | {identificador}.xml |
application/xml |
Siempre, en un documento válido. |
resumen_rfce |
El resumen que se transmitió a la DGII. Sólo en la factura de consumo (32) por debajo de RD$250.000. | {identificador}-resumen.xml |
application/xml |
Siempre, en ese caso. |
respuesta_dgii |
La respuesta final de la DGII, tal cual (su codigo, estado y mensajes): la de la consulta de resultado o, en el resumen de la factura de consumo (que la DGII resuelve en la misma llamada), la que contestó al recibirlo. |
{identificador}-respuesta-dgii.json |
application/json |
Siempre que conste la respuesta de la DGII, que en un documento aceptado es lo normal. |
Los nombres siguen el estándar de la DGII para los adjuntos de un e-CF (RNC del emisor + e-NCF), que es
justo el identificador: puede guardar cada archivo con el nombre
que viene y queda donde la DGII lo espera.
Para la factura de consumo (32) por debajo del umbral, el xml es el e-CF completo (con el detalle de
ítems) que usted debe conservar, y el resumen_rfce es lo que de verdad viajó a la DGII — vea
ecf-32.md.
Cuándo aparecen: sólo con validez fiscal¶
archivos sólo se llena cuando el e-CF tiene validez fiscal: accepted_dgii o
conditionally_accepted_dgii (estados.md). En cualquier otro estado —recibido, en proceso,
rechazado, sin resolución…— la lista va vacía ("archivos": []), aunque el XML ya exista internamente: un
comprobante que la DGII no aceptó no es un e-CF que usted deba conservar ni entregar. Un documento rechazado
nunca trae archivos; si le hace falta ver por qué, están sus errors.
Eso hace barata la consulta mientras espera: sondee el detalle hasta que el estado sea final y, en cuanto sea válido, recoja los archivos de esa misma respuesta.
El PDF llega segundos después de la aceptación¶
La representación impresa se genera tras la aceptación, en un segundo paso. Consultado justo cuando el
documento pasa a accepted_dgii, el detalle puede traer el xml y todavía no el pdf. No es un error: no
se marca como faltante. Vuelva a consultar a los pocos segundos. Si la generación falla de forma pasajera, se
reintenta sola.
Si un archivo no se pudo leer: archivos_no_disponibles¶
Los archivos se guardan en nuestro almacenamiento. Si uno que debería estar no se puede leer en ese
momento —el almacenamiento no respondió, o el archivo no está—, la consulta no falla: el detalle es lo
importante, y se devuelve sin ese archivo, nombrándolo en archivos_no_disponibles:
{
"status": "success",
"message": "Consulta exitosa.",
"document": {
"…": "…",
"archivos": [
{ "tipo": "pdf", "nombre": "133802074E310000000001.pdf", "mime": "application/pdf", "contenido_base64": "JVBERi0xLjQK" }
],
"archivos_no_disponibles": ["xml"]
}
}
- La clave sólo aparece cuando falta algo: sin ella, no falta nada de lo que debía estar (el
pdfque todavía no se generó no cuenta como falta, ver arriba). - Es un fallo pasajero del lado nuestro: repita la consulta. Si persiste, contáctenos.
- El
xmly elresumen_rfcetienen que estar en un documento válido, y por eso se nombran si faltan. Elpdfque todavía no se generó no se nombra (ver arriba); sí uno que consta como generado y no se pudo leer. Larespuesta_dgii, que es opcional, tampoco se nombra si no consta.
Guardar los archivos¶
El contenido viene en base64 estándar. Para volcarlo a un archivo con curl y jq:
curl -s "https://endpoint-test.emision.do/api/v1/documents/133802074E310000000001" \
-H "Authorization: Bearer <su-token>" -H "Accept: application/json" \
| jq -r '.document.archivos[] | select(.tipo == "xml") | .contenido_base64' \
| base64 -d > 133802074E310000000001.xml
En código, es decodificar contenido_base64 y escribir los bytes con el nombre indicado. Conserve el
xml: es el comprobante fiscal firmado.
Tamaño de la respuesta¶
El base64 pesa un tercio más que el archivo, y la respuesta lleva, además del detalle, de dos a cuatro archivos: un e-CF con muchas líneas pesa más, y su PDF también. No hay paginación ni forma de pedir un solo archivo, así que:
- No sondee un documento ya aceptado. Mientras no es válido la respuesta es pequeña; cuando lo es, trae los archivos. Guárdelos y deje de preguntar.
- Si sólo necesita el estado de muchos documentos, use el listado (abajo): no lleva archivos.
Errores¶
| Código | Cuándo |
|---|---|
404 |
El identificador no corresponde a un e-CF de esa empresa: «No se encontró el documento.», con el texto largo en data.detalle. Se responde igual si no existe y si es de otra empresa: nunca se distingue entre "no existe" y "no es suyo". Un identificador mal formado también es un 404, pero de ruta: «La ruta solicitada no existe.» |
403 |
El token no tiene documents:read (el cuerpo dice qué permiso falta), o la empresa del prefijo del identificador no está habilitada para él. |
422 |
Además del identificador viene emisor_rnc (o emisor.rnc) y es otra empresa: «La petición declara más de una empresa.» |
Todos llevan el mismo envelope que el resto de la API, con la explicación en data.detalle
(cómo leerlo):
{
"status": "error",
"message": "No se encontró el documento.",
"data": {
"detalle": "No se encontró un e-CF con el identificador [133802074E310000009999] para este emisor."
}
}
Listado¶
GET /api/v1/documents?status=accepted_dgii&from=2026-08-01&to=2026-08-31&per_page=25&emisor_rnc=133802074
Authorization: Bearer <su-token>
{
"status": "success",
"message": "Consulta exitosa.",
"data": [
{
"document_id": 1234,
"ecf_type_code": "31",
"e_ncf": "E310000000001",
"identificador": "133802074E310000000001",
"track_id": "a1b2c3d4-...",
"status_code": 4,
"status": "accepted_dgii",
"status_description": "La DGII aceptó el e-CF: tiene validez fiscal.",
"issued_at": "27-07-2026",
"total_amount": "1180.00",
"total_itbis": "180.00",
"buyer_name": "CLIENTE SRL"
}
],
"meta": {
"current_page": 1,
"per_page": 25,
"total": 137,
"last_page": 6
}
}
track_id va null mientras el documento no se haya transmitido y siempre en las facturas de consumo
(32) por debajo de RD$250.000: se transmiten como resumen, que no tiene TrackID
(ecf-32.md). En los demás tipos llega relleno al
transmitirse (in_process_dgii) y los estados finales lo conservan
(estados.md). Para ver juntos todos los recursos de consulta, vaya a
«Consultar qué pasó con un documento».
Es el detalle justo para un listado, sin archivos: cada elemento trae su identificador, que es lo que se
usa para pedir el documento puntual con GET /api/v1/documents/{identificador} (código de seguridad, entrega al
receptor, mensajes de error y, si es válido, el XML y el PDF).
Filtros (todos opcionales, combinables)¶
| Parámetro | Formato | Filtra por |
|---|---|---|
from |
aaaa-MM-dd |
issued_at desde esta fecha (inclusive) |
to |
aaaa-MM-dd |
issued_at hasta esta fecha (inclusive) |
status |
slug de estados.md (p. ej. accepted_dgii) |
estado interno exacto |
type |
31…47 |
tipo de e-CF exacto |
buyer |
texto libre | coincide contra la razón social o el RNC/Cédula del comprador |
Un valor inválido (una fecha que no cumple el formato, un status o type que no existe en nuestros
catálogos) responde 422 en el mismo envelope que el resto de la API (vea
README.md), nunca un error de servidor. Como un filtro no habla de ningún
e-CF, no lleva document: lleva data, con detalle (el primer error y cuántos más hay) y errores, los
mensajes por campo:
{
"status": "error",
"message": "Los datos enviados no son válidos.",
"data": {
"detalle": "El campo fecha desde no corresponde al formato Y-m-d. (y 1 error más: vea «errores».)",
"errores": {
"from": ["El campo fecha desde no corresponde al formato Y-m-d."],
"per_page": ["El campo elementos por página debe ser un número entero."]
}
}
}
A diferencia de la validación de emitir un e-CF (que responde con
document.errors, porque ese comprobante tiene un estado fiscal:rejected_validation), aquí no hay documento de por medio. Un cliente que ya lee el envelope de la API sólo necesita saber dónde mirar:data.detallepara el texto ydata.errorespara el campo.
Paginación¶
page y per_page (por defecto 25, tope 100); pedir más de 100 también responde 422. El
orden es siempre por issued_at descendente: lo más reciente primero.
meta.total y meta.last_page son del total filtrado, no de todos sus documentos.