Referencia
El contrato del binario y el modelo de entrada de fase 1. Las notas de los campos son del SDK, en inglés, y se citan del commit 59f651b.
Contrato del CLI
Una llamada, un JSON por la entrada estándar y un JSON por la salida estándar. Fuente: docs/cli.md.
| Comando | Entrada | Salida | Red |
|---|---|---|---|
build | Un comprobante (modelo de entrada). | {file_name, xml}, XML sin firmar. | No |
sign | {xml, signature_id?}. Certificado y clave por --cert y --key, o por SUNAT_CERT_PATH y SUNAT_KEY_PATH. | {xml, digest_value, signature_value}. | No |
zip | {name, xml | xml_base64}. | {file_name, zip_base64}. | No |
send | La misma entrada que zip. Endpoint por --endpoint o SUNAT_ENDPOINT; usuario y clave por --user, --password o sus variables de entorno. | {outcome: "cdr", cdr, cdr_zip_base64} o {outcome: "fault", code, message, class}. | Sí, HTTPS con verificación del certificado. |
status | {ruc, document_type, series, number}. Endpoint por SUNAT_STATUS_ENDPOINT; credenciales igual que en send. | {outcome: "status", status_code, status_message, cdr?}, o fault. | Sí, HTTPS con verificación del certificado. |
cdr | {cdr_base64}, ZIP o XML. | {reference_id, response_code, description, notes, class}. | No |
Errores. Si algo falla, el binario sale con código 1, no escribe nada en la salida estándar y escribe {"error": "..."} en el error estándar. Las credenciales y las claves no se imprimen nunca, ni en el error.
Clases de respuesta de la SUNAT
El SDK clasifica cada código de respuesta. La tabla sale de docs/architecture.md. Qué hacer con cada clase está en Paso a producción, sección 4.
| Código | Clase |
|---|---|
0 | Aceptada. |
100 a 1999 | Excepción: el servicio devolvió un error y no hay constancia. |
1033 | Ya registrado con otros datos. Se recupera la constancia; no se reenvía. |
1032 | Número anulado o rechazado. No se reutiliza. |
2000 a 3999 | Rechazada: no se registra. |
2323, 2324 | Códigos de la comunicación de baja. |
4000 y más | Observada: aceptada con advertencias. |
Modelo de entrada, fase 1
Un JSON por comprobante, con nombres de campo en inglés y en snake_case. Los importes van como texto. El SDK no calcula impuestos: los recibe y los valida. El texto de esta sección sale dedocs/input-model.md.
One JSON document per voucher, snake_case English field names. Decimal amounts are JSON strings
("118.00"), never floats. Amounts are given by the caller: the SDK renders and validates, it does not
compute taxes.
Phase 1 scope: invoice 01, receipt 03, credit note 07, debit note 08, UBL 2.1, IGV only
(affectations 10-16, 20, 21, 30-37, 40; 17 IVAP is out of phase 1). Out of phase 1, rejected with an
explicit error: ISC, ICBPER, IVAP, other taxes, global discounts or charges, advances, detraction,
perception, despatch references.
Document
| Field | Notes |
|---|---|
kind |
"invoice" (01, 03) or "note" (07, 08) |
ubl_version |
"2.1" |
operation_type |
catalog 51, invoice only ("0101") |
document_type |
"01", "03", "07", "08" |
series |
"F001", "B001" |
number |
string, no padding added |
issue_date |
ISO 8601 with offset, "2026-10-15T10:30:00-05:00" |
due_date |
optional, invoice only |
currency |
"PEN", "USD" |
payment_terms |
{ "type": "Contado" } or { "type": "Credito", "amount": "...", "currency": "PEN", "installments": [{ "amount": "...", "currency": "PEN", "due_date": "2026-11-15T00:00:00-05:00" }] } |
issuer |
see Issuer |
customer |
see Customer |
totals |
see Totals |
lines |
see Line |
legends |
[{ "code": "1000", "value": "..." }] |
note |
only for 07/08, see Note |
Issuer
ruc, legal_name, trade_name, email, telephone, address: ubigeo, country_code,
department, province, district, urbanization, street, establishment_code.
Customer
document_type (catalog 06), document_number, legal_name, optional address (same shape as the
issuer's), email, telephone.
Totals
taxed, unaffected, exempt, export, free, free_igv, igv, total_taxes, sale_value,
subtotal, rounding, payable. An absent total is not rendered.
Line
unit (catalog 03, "NIU", "ZZ"), quantity, product_code, sunat_product_code, description,
unit_value, igv_base, igv_rate ("18.00"), igv, affectation (catalog 07), total_taxes,
unit_price, line_value, free_unit_value (free lines).
Note (07, 08)
reason_code (catalog 09 or 10), reason, affected_document_type, affected_document_number
("F001-123").
Fase 2: resumen diario y baja
Contrato y vectores, sin build. Este modelo describe lo que vendrá. No se puede emitir todavía con él.
Out of phase 2, rejected with an explicit error: perception, ICBPER, ISC and IVAP in summary lines; reversal summaries (RR); despatch (GRE).
Summary (kind: "summary", RC)
| Field | Notes |
|---|---|
number |
correlative of the day, string ("1"); the identifier is RC-YYYYMMDD-<number> with the generation date |
generation_date |
date the summary is generated, "2026-10-15" |
reference_date |
date of issue of the documents it reports, "2026-10-14" |
currency |
"PEN", "USD" |
issuer |
see Issuer |
lines |
see Summary line |
Summary line: document_type ("03", "07", "08"), document_number ("B001-1"), status
(catalog 19: "1" add, "2" modify, "3" void), customer_document_type (catalog 06),
customer_document_number, optional reference_document ({ "document_type": "03", "document_number": "B001-1" }, for notes), and the amounts total, taxed, exempt, unaffected, export, free,
other_charges, igv. igv is always rendered (0.00 when absent); any amount given, even "0.00", is
rendered; an absent amount other than igv is not. Summary lines carry no tax percentage.
Void communication (kind: "voided", RA)
| Field | Notes |
|---|---|
number |
correlative of the day, string; the identifier is RA-YYYYMMDD-<number> with the communication date |
generation_date |
date of issue of the documents it voids |
communication_date |
date the communication is sent |
issuer |
see Issuer |
lines |
[{ "document_type": "01", "series": "F001", "number": "123", "reason": "..." }] |
Both are sent with sendSummary and return a ticket; the CDR comes from getStatus with that ticket.