Envía el comprobante por correo
curl https://api.boletura.com/v1/documents/0192f2c4-7d1e-7a3b-9c4d-5e6f7a8b9c0d/email \ --request POST \ --header 'Authorization: Bearer bol_test_TU_CLAVE' \ --header 'Content-Type: application/json' \ --data '{ "email": "compras@cliente.example"}'$ch = curl_init("https://api.boletura.com/v1/documents/0192f2c4-7d1e-7a3b-9c4d-5e6f7a8b9c0d/email");
curl_setopt($ch, CURLOPT_POST, true);curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer bol_test_TU_CLAVE', 'Content-Type: application/json']);curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([ 'email' => 'compras@cliente.example']));
curl_exec($ch);
curl_close($ch);import http.clientimport json
conn = http.client.HTTPSConnection("api.boletura.com")
payload = json.dumps({ "email": "compras@cliente.example"})headers = { "Authorization": "Bearer bol_test_TU_CLAVE", "Content-Type": "application/json"}
conn.request( "POST", "/v1/documents/0192f2c4-7d1e-7a3b-9c4d-5e6f7a8b9c0d/email", body=payload, headers=headers,)
response = conn.getresponse()print(response.read().decode())
conn.close()using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://api.boletura.com/v1/documents/0192f2c4-7d1e-7a3b-9c4d-5e6f7a8b9c0d/email");request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "bol_test_TU_CLAVE");request.Content = new StringContent("""{ "email": "compras@cliente.example"}""",System.Text.Encoding.UTF8, "application/json");
using var response = await client.SendAsync(request);java.io.ByteArrayOutputStream body = new java.io.ByteArrayOutputStream();body.write("{\n \"email\": \"compras@cliente.example\"\n}".getBytes(java.nio.charset.StandardCharsets.UTF_8));
HttpClient client = HttpClient.newHttpClient();HttpRequest request = HttpRequest.newBuilder() .uri(java.net.URI.create("https://api.boletura.com/v1/documents/0192f2c4-7d1e-7a3b-9c4d-5e6f7a8b9c0d/email")) .header("Authorization", "Bearer bol_test_TU_CLAVE") .header("Content-Type", "application/json") .method("POST", HttpRequest.BodyPublishers.ofByteArray(body.toByteArray())) .build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());System.out.println(response.body());fetch('https://api.boletura.com/v1/documents/0192f2c4-7d1e-7a3b-9c4d-5e6f7a8b9c0d/email', { method: 'POST', headers: { Authorization: 'Bearer bol_test_TU_CLAVE', 'Content-Type': 'application/json' }, body: JSON.stringify({ email: 'compras@cliente.example' })})Encola el envío del PDF A4 y el XML al correo del cliente del documento o al de email, a nombre de la empresa y con su correo como respuesta. Responde de inmediato: status queda en pending y el correo sale en segundos, o al día siguiente si la plataforma llegó a su tope diario de correos a clientes. Hasta 5 envíos por documento y día de Lima (429 document_email_limit_reached). No depende de la opción de envío automático de la empresa, pero sigue su regla: una factura o una nota sobre factura se envía cuando SUNAT la acepta, y antes responde 409 document_email_document_not_accepted; uno rechazado, fuera de plazo, anulado o en anulación responde 409 document_email_document_invalid, y si eso pasa mientras espera, queda en skipped. En test, el asunto empieza con [PRUEBA] y el envío cuenta para el tope de 20 correos diarios de la cuenta: pasado ese tope, queda en skipped.
Requiere el scope documents:write.
Autorización
Sección titulada «Autorización»Parámetros
Sección titulada «Parámetros»Parámetros de la ruta
Sección titulada «Parámetros de la ruta»UUID del documento que se envía. Uno que no existe en el tenant y ambiente de la clave, o que no es un UUID, responde 404 not_found.
Cuerpo de la solicitud
Sección titulada «Cuerpo de la solicitud»Envío del documento por correo. El cuerpo se puede omitir.
object
Correo de destino. Si se omite, va al correo del cliente del documento.
Ejemplos
A otro correo
{ "email": "compras@cliente.example"}Respuestas
Sección titulada «Respuestas»Envío encolado, con status = pending.
Un envío del documento por correo.
object
UUID del envío.
Correo de destino.
Quién originó el envío.
Estado del envío.
Motivo de failed o skipped; nulo en los demás estados.
Cuándo se pidió. Timestamp RFC 3339 en UTC, con seis decimales.
Cuándo lo recibió el servidor de correo; nulo mientras no se envía. Timestamp RFC 3339 en UTC, con seis decimales.
Ejemplos
Envío encolado
{ "id": "0192f1a2-9d14-7a61-b3c2-5e8f7a6b4c3d", "recipient": "compras@cliente.example", "origin": "manual", "status": "pending", "error": null, "created_at": "2026-10-09T16:20:41.507311Z", "sent_at": null}Cabeceras
Sección titulada «Cabeceras»El del cliente si tiene de 1 a 128 caracteres [A-Za-z0-9._-]; si no, uno generado. Es el request_id de los errores.
Solicitudes admitidas en la ventana de un minuto, de la clave o de la IP.
Solicitudes que quedan en la ventana.
Segundos hasta la ventana siguiente.
Solicitud mal formada. Códigos: invalid_json y bad_request. No se reintenta sin corregirla.
Formato único de los errores de la API.
object
El error principal.
object
Familia del error, como validation_error, conflict o unavailable.
Código estable, para programar contra él.
Mensaje en español, apto para mostrar a un operador. Puede cambiar: no se programa contra él.
Ruta del campo en el JSON, con índices entre corchetes, como items[2].unit_price. Solo cuando el error apunta a un campo.
Identificador de la solicitud, el mismo de X-Request-Id. Inclúyelo al escribir a soporte.
Documento con el que choca la solicitud. Solo en external_id_conflict y document_already_replaced.
Todos los problemas. Solo aparece cuando hay varios.
Uno de los problemas de la solicitud.
object
Código estable, para programar contra él.
Mensaje en español, apto para mostrar a un operador.
Ruta del campo en el JSON, si el error apunta a uno.
Ejemplos
invalid_json
{ "error": { "type": "bad_request", "code": "invalid_json", "message": "El cuerpo no es un JSON válido.", "request_id": "req_0192f1a27c3e7d4b9a8f2b6c1d0e4f5a" }}Cabeceras
Sección titulada «Cabeceras»El del cliente si tiene de 1 a 128 caracteres [A-Za-z0-9._-]; si no, uno generado. Es el request_id de los errores.
Solicitudes admitidas en la ventana de un minuto, de la clave o de la IP.
Solicitudes que quedan en la ventana.
Segundos hasta la ventana siguiente.
Sin una API key válida. Códigos: unauthorized, invalid_api_key, revoked_api_key, expired_api_key.
Formato único de los errores de la API.
object
El error principal.
object
Familia del error, como validation_error, conflict o unavailable.
Código estable, para programar contra él.
Mensaje en español, apto para mostrar a un operador. Puede cambiar: no se programa contra él.
Ruta del campo en el JSON, con índices entre corchetes, como items[2].unit_price. Solo cuando el error apunta a un campo.
Identificador de la solicitud, el mismo de X-Request-Id. Inclúyelo al escribir a soporte.
Documento con el que choca la solicitud. Solo en external_id_conflict y document_already_replaced.
Todos los problemas. Solo aparece cuando hay varios.
Uno de los problemas de la solicitud.
object
Código estable, para programar contra él.
Mensaje en español, apto para mostrar a un operador.
Ruta del campo en el JSON, si el error apunta a uno.
Ejemplos
invalid_api_key
{ "error": { "type": "unauthorized", "code": "invalid_api_key", "message": "La API key no es válida.", "request_id": "req_0192f1a27c3e7d4b9a8f2b6c1d0e4f5a" }}Cabeceras
Sección titulada «Cabeceras»El del cliente si tiene de 1 a 128 caracteres [A-Za-z0-9._-]; si no, uno generado. Es el request_id de los errores.
Solicitudes admitidas en la ventana de un minuto, de la clave o de la IP.
Solicitudes que quedan en la ventana.
Segundos hasta la ventana siguiente.
La clave no puede operar. Códigos: forbidden (no tiene el scope), tenant_suspended, plan_without_api (el plan de la cuenta no incluye la API), account_not_activated (emisión en live de una cuenta que Boletura todavía no activó).
Formato único de los errores de la API.
object
El error principal.
object
Familia del error, como validation_error, conflict o unavailable.
Código estable, para programar contra él.
Mensaje en español, apto para mostrar a un operador. Puede cambiar: no se programa contra él.
Ruta del campo en el JSON, con índices entre corchetes, como items[2].unit_price. Solo cuando el error apunta a un campo.
Identificador de la solicitud, el mismo de X-Request-Id. Inclúyelo al escribir a soporte.
Documento con el que choca la solicitud. Solo en external_id_conflict y document_already_replaced.
Todos los problemas. Solo aparece cuando hay varios.
Uno de los problemas de la solicitud.
object
Código estable, para programar contra él.
Mensaje en español, apto para mostrar a un operador.
Ruta del campo en el JSON, si el error apunta a uno.
Ejemplos
forbidden
{ "error": { "type": "forbidden", "code": "forbidden", "message": "La API key no tiene el scope documents:write que exige este endpoint.", "request_id": "req_0192f1a27c3e7d4b9a8f2b6c1d0e4f5a" }}Cabeceras
Sección titulada «Cabeceras»El del cliente si tiene de 1 a 128 caracteres [A-Za-z0-9._-]; si no, uno generado. Es el request_id de los errores.
Solicitudes admitidas en la ventana de un minuto, de la clave o de la IP.
Solicitudes que quedan en la ventana.
Segundos hasta la ventana siguiente.
not_found: no existe en el tenant y ambiente de la clave.
Formato único de los errores de la API.
object
El error principal.
object
Familia del error, como validation_error, conflict o unavailable.
Código estable, para programar contra él.
Mensaje en español, apto para mostrar a un operador. Puede cambiar: no se programa contra él.
Ruta del campo en el JSON, con índices entre corchetes, como items[2].unit_price. Solo cuando el error apunta a un campo.
Identificador de la solicitud, el mismo de X-Request-Id. Inclúyelo al escribir a soporte.
Documento con el que choca la solicitud. Solo en external_id_conflict y document_already_replaced.
Todos los problemas. Solo aparece cuando hay varios.
Uno de los problemas de la solicitud.
object
Código estable, para programar contra él.
Mensaje en español, apto para mostrar a un operador.
Ruta del campo en el JSON, si el error apunta a uno.
Ejemplos
not_found
{ "error": { "type": "not_found", "code": "not_found", "message": "El documento no existe.", "request_id": "req_0192f1a27c3e7d4b9a8f2b6c1d0e4f5a" }}Cabeceras
Sección titulada «Cabeceras»El del cliente si tiene de 1 a 128 caracteres [A-Za-z0-9._-]; si no, uno generado. Es el request_id de los errores.
Solicitudes admitidas en la ventana de un minuto, de la clave o de la IP.
Solicitudes que quedan en la ventana.
Segundos hasta la ventana siguiente.
Conflicto con el estado del documento. document_email_document_not_accepted: SUNAT todavía no aceptó la factura o la nota sobre factura; se reintenta cuando sunat_status sea accepted o accepted_with_observations. document_email_document_invalid: SUNAT lo rechazó, venció su plazo de envío, o está anulado o en anulación; no se reintenta mientras ese estado no cambie.
Formato único de los errores de la API.
object
El error principal.
object
Familia del error, como validation_error, conflict o unavailable.
Código estable, para programar contra él.
Mensaje en español, apto para mostrar a un operador. Puede cambiar: no se programa contra él.
Ruta del campo en el JSON, con índices entre corchetes, como items[2].unit_price. Solo cuando el error apunta a un campo.
Identificador de la solicitud, el mismo de X-Request-Id. Inclúyelo al escribir a soporte.
Documento con el que choca la solicitud. Solo en external_id_conflict y document_already_replaced.
Todos los problemas. Solo aparece cuando hay varios.
Uno de los problemas de la solicitud.
object
Código estable, para programar contra él.
Mensaje en español, apto para mostrar a un operador.
Ruta del campo en el JSON, si el error apunta a uno.
Ejemplos
document_email_document_not_accepted
{ "error": { "type": "conflict", "code": "document_email_document_not_accepted", "message": "SUNAT todavía no aceptó el comprobante: una factura o una nota sobre factura se envía por correo cuando SUNAT la acepta.", "request_id": "req_0192f1a27c3e7d4b9a8f2b6c1d0e4f5a" }}Cabeceras
Sección titulada «Cabeceras»El del cliente si tiene de 1 a 128 caracteres [A-Za-z0-9._-]; si no, uno generado. Es el request_id de los errores.
Solicitudes admitidas en la ventana de un minuto, de la clave o de la IP.
Solicitudes que quedan en la ventana.
Segundos hasta la ventana siguiente.
payload_too_large: el cuerpo supera 256 KiB.
Formato único de los errores de la API.
object
El error principal.
object
Familia del error, como validation_error, conflict o unavailable.
Código estable, para programar contra él.
Mensaje en español, apto para mostrar a un operador. Puede cambiar: no se programa contra él.
Ruta del campo en el JSON, con índices entre corchetes, como items[2].unit_price. Solo cuando el error apunta a un campo.
Identificador de la solicitud, el mismo de X-Request-Id. Inclúyelo al escribir a soporte.
Documento con el que choca la solicitud. Solo en external_id_conflict y document_already_replaced.
Todos los problemas. Solo aparece cuando hay varios.
Uno de los problemas de la solicitud.
object
Código estable, para programar contra él.
Mensaje en español, apto para mostrar a un operador.
Ruta del campo en el JSON, si el error apunta a uno.
Ejemplos
payload_too_large
{ "error": { "type": "payload_too_large", "code": "payload_too_large", "message": "El cuerpo supera el tamaño máximo: 256 KiB, o 2 MiB al subir un certificado, un logo o un CDR.", "request_id": "req_0192f1a27c3e7d4b9a8f2b6c1d0e4f5a" }}Cabeceras
Sección titulada «Cabeceras»El del cliente si tiene de 1 a 128 caracteres [A-Za-z0-9._-]; si no, uno generado. Es el request_id de los errores.
Solicitudes admitidas en la ventana de un minuto, de la clave o de la IP.
Solicitudes que quedan en la ventana.
Segundos hasta la ventana siguiente.
unsupported_media_type: el cuerpo no llega con Content-Type: application/json.
Formato único de los errores de la API.
object
El error principal.
object
Familia del error, como validation_error, conflict o unavailable.
Código estable, para programar contra él.
Mensaje en español, apto para mostrar a un operador. Puede cambiar: no se programa contra él.
Ruta del campo en el JSON, con índices entre corchetes, como items[2].unit_price. Solo cuando el error apunta a un campo.
Identificador de la solicitud, el mismo de X-Request-Id. Inclúyelo al escribir a soporte.
Documento con el que choca la solicitud. Solo en external_id_conflict y document_already_replaced.
Todos los problemas. Solo aparece cuando hay varios.
Uno de los problemas de la solicitud.
object
Código estable, para programar contra él.
Mensaje en español, apto para mostrar a un operador.
Ruta del campo en el JSON, si el error apunta a uno.
Ejemplos
unsupported_media_type
{ "error": { "type": "unsupported_media_type", "code": "unsupported_media_type", "message": "El cuerpo debe enviarse con Content-Type application/json.", "request_id": "req_0192f1a27c3e7d4b9a8f2b6c1d0e4f5a" }}Cabeceras
Sección titulada «Cabeceras»El del cliente si tiene de 1 a 128 caracteres [A-Za-z0-9._-]; si no, uno generado. Es el request_id de los errores.
Solicitudes admitidas en la ventana de un minuto, de la clave o de la IP.
Solicitudes que quedan en la ventana.
Segundos hasta la ventana siguiente.
Datos no válidos: error.param señala el campo. Códigos: document_email_recipient_required, cuando el documento no tiene correo del cliente y no se envía email; document_email_customer_email_invalid, cuando no se envía email y el correo del cliente del documento está mal escrito, y los de un email mal escrito.
Formato único de los errores de la API.
object
El error principal.
object
Familia del error, como validation_error, conflict o unavailable.
Código estable, para programar contra él.
Mensaje en español, apto para mostrar a un operador. Puede cambiar: no se programa contra él.
Ruta del campo en el JSON, con índices entre corchetes, como items[2].unit_price. Solo cuando el error apunta a un campo.
Identificador de la solicitud, el mismo de X-Request-Id. Inclúyelo al escribir a soporte.
Documento con el que choca la solicitud. Solo en external_id_conflict y document_already_replaced.
Todos los problemas. Solo aparece cuando hay varios.
Uno de los problemas de la solicitud.
object
Código estable, para programar contra él.
Mensaje en español, apto para mostrar a un operador.
Ruta del campo en el JSON, si el error apunta a uno.
Ejemplos
document_email_recipient_required
{ "error": { "type": "validation_error", "code": "document_email_recipient_required", "message": "El comprobante no tiene correo del cliente: indica a qué correo enviarlo.", "param": "email", "request_id": "req_0192f1a27c3e7d4b9a8f2b6c1d0e4f5a" }}Cabeceras
Sección titulada «Cabeceras»El del cliente si tiene de 1 a 128 caracteres [A-Za-z0-9._-]; si no, uno generado. Es el request_id de los errores.
Solicitudes admitidas en la ventana de un minuto, de la clave o de la IP.
Solicitudes que quedan en la ventana.
Segundos hasta la ventana siguiente.
document_email_limit_reached: el documento ya se envió 5 veces hoy a pedido; se reintenta al día siguiente de Lima. También rate_limited, si se superó el límite de solicitudes, que se reintenta después de Retry-After.
Formato único de los errores de la API.
object
El error principal.
object
Familia del error, como validation_error, conflict o unavailable.
Código estable, para programar contra él.
Mensaje en español, apto para mostrar a un operador. Puede cambiar: no se programa contra él.
Ruta del campo en el JSON, con índices entre corchetes, como items[2].unit_price. Solo cuando el error apunta a un campo.
Identificador de la solicitud, el mismo de X-Request-Id. Inclúyelo al escribir a soporte.
Documento con el que choca la solicitud. Solo en external_id_conflict y document_already_replaced.
Todos los problemas. Solo aparece cuando hay varios.
Uno de los problemas de la solicitud.
object
Código estable, para programar contra él.
Mensaje en español, apto para mostrar a un operador.
Ruta del campo en el JSON, si el error apunta a uno.
Ejemplos
document_email_limit_reached
{ "error": { "type": "rate_limited", "code": "document_email_limit_reached", "message": "El comprobante ya se envió 5 veces hoy a pedido: vuelve a intentarlo mañana.", "request_id": "req_0192f1a27c3e7d4b9a8f2b6c1d0e4f5a" }}Cabeceras
Sección titulada «Cabeceras»El del cliente si tiene de 1 a 128 caracteres [A-Za-z0-9._-]; si no, uno generado. Es el request_id de los errores.
Solicitudes admitidas en la ventana de un minuto, de la clave o de la IP.
Solicitudes que quedan en la ventana.
Segundos hasta la ventana siguiente.
Segundos que conviene esperar antes de reintentar.
Falla transitoria, como database_unavailable. Se reintenta con una espera creciente; un reintento después de una respuesta perdida puede enviar el correo otra vez.
Formato único de los errores de la API.
object
El error principal.
object
Familia del error, como validation_error, conflict o unavailable.
Código estable, para programar contra él.
Mensaje en español, apto para mostrar a un operador. Puede cambiar: no se programa contra él.
Ruta del campo en el JSON, con índices entre corchetes, como items[2].unit_price. Solo cuando el error apunta a un campo.
Identificador de la solicitud, el mismo de X-Request-Id. Inclúyelo al escribir a soporte.
Documento con el que choca la solicitud. Solo en external_id_conflict y document_already_replaced.
Todos los problemas. Solo aparece cuando hay varios.
Uno de los problemas de la solicitud.
object
Código estable, para programar contra él.
Mensaje en español, apto para mostrar a un operador.
Ruta del campo en el JSON, si el error apunta a uno.
Ejemplos
database_unavailable
{ "error": { "type": "unavailable", "code": "database_unavailable", "message": "La base de datos no está disponible. Reintente en unos segundos.", "request_id": "req_0192f1a27c3e7d4b9a8f2b6c1d0e4f5a" }}Cabeceras
Sección titulada «Cabeceras»El del cliente si tiene de 1 a 128 caracteres [A-Za-z0-9._-]; si no, uno generado. Es el request_id de los errores.
Solicitudes admitidas en la ventana de un minuto, de la clave o de la IP.
Solicitudes que quedan en la ventana.
Segundos hasta la ventana siguiente.