SEE del Contribuyente · sin OSE
Factura electrónica peruana, hablada directo con SUNAT.
Una API para emitir facturas, boletas, notas y guías de remisión bajo el Sistema de Emisión Electrónica del Contribuyente — cada emisor firma con su propio certificado y sus propias credenciales SOL. Sin intermediarios, sin costo por comprobante.
Base https://vendacoresunat.com/api/v1Autenticación
Un solo header, dos formas de credencial.
Cada request lleva la credencial en Authorization: Bearer <clave>. Hay dos tipos, y el formato de la clave indica cuál es:
X-Issuer-RUC para indicar sobre cuál se opera.X-Issuer-RUC ni falta hacerlo. Es la credencial recomendada para entregar a un sistema externo: no puede tocar ningún otro RUC del tenant aunque lo intente._test_ resuelve automáticamente contra el ambiente beta de SUNAT; _live_ contra producción. El ambiente no se elige por parámetro — lo decide la clave.
Modelo asíncrono
Cada envío responde de inmediato; el resultado de SUNAT llega después.
Un POST a cualquier endpoint de emisión devuelve 202 con status: "queued" en segundos — es un acuse de recibo de nuestro lado, no el veredicto de SUNAT. El envío real ocurre en una cola con reintentos automáticos ante caídas del servicio de SUNAT.
Para saber en qué quedó, dos caminos:
- Consultar —
GET /documents/{id}hasta questatusdeje de serqueuedoretrying. En producción normal, resuelve en 5–15 segundos. - Webhook — configura una URL de callback por tenant; avisamos con
document.acceptedodocument.rejected, firmado con HMAC (X-Signature) usando elwebhook_secretdel tenant.
| status | Significa |
|---|---|
| queued | Encolado, todavía no se intentó enviar. |
| retrying | SUNAT tuvo una falla de sistema (código 100–999); reintentando con backoff. |
| accepted | SUNAT lo aceptó — con o sin observaciones (código <2000 o ≥4000). |
| rejected | Rechazo definitivo o el comprobante quedó anulado por SUNAT (código 1000–3999). No se reintenta solo. |
| voided | Un documento antes aceptado, dado de baja después (RA o RC de anulación). |
Códigos de respuesta SUNAT
El campo codigo_sunat en cada respuesta cae en uno de estos cinco rangos.
| Rango | Significa | Acción |
|---|---|---|
| 0 | Aceptado sin novedad | Listo. |
| 100–999 | Excepción del sistema SUNAT | Se reintenta solo, con backoff. |
| 1000–1999 | Excepción del contribuyente | Corregir el payload; no se reintenta solo. |
| 2000–3999 | El comprobante queda anulado | No reintentar — hay que reemitir con datos correctos. |
| 4000+ | Observación | Aceptado igual — mostrar la nota, no es un error. |
Un caso real, para que quede claro por qué esto importa: un comprobante con código 3275 (dentro de 2000–3999) puede llegar con la llamada HTTP en apariencia "exitosa" — el error está adentro del CDR, no en el transporte. Un integrador que solo revisa si el request no falló, sin mirar codigo_sunat, puede terminar tratando como válido un comprobante que SUNAT ya anuló.
Idempotencia
Reenviar nunca duplica ni quema un correlativo.
Todo POST de emisión acepta Idempotency-Key. Usa un identificador único y estable por comprobante del lado de tu sistema — el id del documento en tu base, no el de la venta completa si una venta puede generar más de un comprobante. Reenviar la misma clave devuelve el mismo documento ya creado, sin pedir un correlativo nuevo.
Idempotency-Key: a1e4c9f0-6b3d-4e21-9a7f-2d8801c5f3aa
Emisores
Alta en 3 pasos antes de poder emitir nada.
Un emisor no queda listo con un solo request: RUC y datos fiscales primero, certificado digital después, credenciales SOL al final. Los tres pasos son independientes porque en la práctica llegan en momentos distintos — el certificado puede tardar en llegar del lado del cliente.
1. Alta del emisor
beta (default) o production.2. Certificado digital
Archivo .pfx/.p12 propio del emisor + su contraseña. Se valida que no esté vencido y, si el certificado trae el RUC en el asunto, que coincida con el del emisor.
Logo para el PDF (opcional)
logo{id}Imagen PNG o JPG (hasta 1 MB) que aparece en el encabezado del PDF de los comprobantes de ese emisor. Se reduce y se vuelve a guardar como PNG; el archivo original no se conserva. También se puede cargar desde el panel. Sin logo, el PDF sale solo con el nombre de la empresa.
POST /logo es para un sistema (por ejemplo un POS) que solo conoce el token de su emisor: cambia únicamente el logo de ese RUC.
3. Credenciales SOL
{
"sol_user": "ASELVA06",
"sol_password": "••••••••"
}
Tiene que ser un usuario secundario propio, nunca el maestro. El maestro (representante legal) puede loguearse sin problema en el portal SOL, pero SUNAT rechaza el servicio web con código 102 si se usa para el envío automatizado. Crea un usuario secundario en SUNAT con el permiso «SEE - Del Contribuyente y Envío de Documentos» explícitamente asignado.
Token del emisor
Genera un isk_live_… / isk_test_… scopeado a ese único RUC. Se muestra una sola vez — no queda forma de recuperarlo después, solo de rotarlo (invalida el anterior de inmediato).
Comprobantes
Un contrato: JSON adentro, {document_id, status} afuera. Nunca se manipula XML, ZIP ni SOAP del lado del cliente.
Factura / boleta
01 factura, 03 boleta.valor_unitario, valor_venta, base_igv, igv, total_impuestos, precio_unitario.items[] — el valor de cada ítem debe reflejar ya el descuento, no el precio de lista. La suma de los ítems tiene que cuadrar con totales.valor_venta.
curl -X POST https://vendacoresunat.com/api/v1/invoices \ -H "Authorization: Bearer isk_live_..." \ -H "Content-Type: application/json" \ -H "Idempotency-Key: doc-00931" \ -d '{ "tipo_documento": "03", "moneda": "PEN", "cliente": { "tipo_documento": "1", "numero_documento": "45678912", "razon_social": "Willy Fernandez" }, "items": [{ "descripcion": "Camisa talla M", "unidad": "NIU", "cantidad": 1, "valor_unitario": 8.47, "valor_venta": 8.47, "base_igv": 8.47, "porcentaje_igv": 18, "igv": 1.53, "tipo_afectacion_igv": "10", "total_impuestos": 1.53, "precio_unitario": 10.00 }], "totales": { "gravadas": 8.47, "igv": 1.53, "total_impuestos": 1.53, "valor_venta": 8.47, "subtotal": 10.00, "importe_total": 10.00 } }'
{
"document_id": "01a0446b-a2cd-7072-89f5-14abb2f79143",
"tipo": "03", "serie": "B007", "numero": 1,
"nombre": "20612873381-03-B007-1",
"status": "queued",
"links": { "pdf": "…", "xml": "…", "cdr": "…" }
}
Notas de crédito / débito
Mismo cuerpo que una factura/boleta, más el documento que corrigen:
Resumen diario de boletas (RC)
Referencia boletas ya emitidas por document_id, agrupadas en dos listas:
Comunicación de baja (RA)
Anula factura, nota de crédito o de débito ya aceptada — boletas no, van por /summaries → anulaciones. Para liquidaciones de compra (04) el mismo endpoint genera el resumen de reversiones (RR); no se pueden mezclar con facturas o notas en una misma solicitud.
{
"fecha_generacion": "2026-08-27",
"documentos": [
{ "document_id": "…", "motivo": "Error en el monto" }
]
}
Guía de remisión (GRE 2.0)
Traslado de bienes por cuenta propia (remitente). A diferencia del resto, no usa el servicio web SOAP tradicional — GRE 2.0 firma localmente y envía por REST + OAuth2. Requiere credenciales GRE separadas en el emisor (POST /issuers/{id}/gre). Shape completo disponible aparte — es protocolo distinto, no cabe en esta página sin volverla ruidosa.
Consulta y descarga
Estado de un documento
Archivos
Webhooks
Alternativa a hacer polling — SUNAT avisa cuando resuelve.
{
"event": "document.accepted",
"document_id": "01a0446b-…",
"codigo_sunat": "0",
"mensaje": "La Boleta de Venta numero B007-1, ha sido aceptado"
}
Firmado con X-Signature (HMAC-SHA256 sobre el cuerpo crudo, con el webhook_secret del tenant) — verifica la firma antes de confiar en el contenido.
Flujo de punta a punta
- Alta del emisor → certificado → SOL → token. Una sola vez por RUC.
POST /invoicescon el token del emisor →202,document_id.- Poll
GET /documents/{id}cada 1–2s, o espera el webhook. status: "accepted"concodigo_sunat: "0"→ listo. Con observaciones (≥4000) → aceptado igual, mostrar la nota.- ¿Hay que corregir? Nota de crédito/débito si ya se envió al cliente; RA o anulación por RC si hay que anularlo del todo.
Ambiente de pruebas
Usa un token isk_test_… o sk_test_… — resuelve solo contra el ambiente beta de SUNAT, sin tocar ningún RUC real. El emisor de pruebas oficial de SUNAT:
| RUC | 20000000001 |
|---|---|
| Usuario SOL | MODDATOS |
| Clave SOL | moddatos |
1033 — registrado previamente ahí es normal: cambia el correlativo e intenta de nuevo, no es un bug de tu integración.