Registrar un evento de retorno
Cuando un cliente devuelve mercadería, se le reembolsa un cobro o se reduce una exportación, el documento original no se toca: se registra un evento de retorno. POST /v1/dte/{codigoGeneracion}/return produce un evento aparte, con su propio documento firmado, su propio código de generación y su propio sello del Ministerio de Hacienda. No gasta correlativo y, una vez sellado, no se deshace.
Se aplica a una factura (01), una factura de exportación (11) o una factura de sujeto excluido (14) con sello, que esta API haya emitido. Para otros tipos de documento la corrección es una nota de crédito.
Varios eventos sobre el mismo documento
Un documento admite tantos eventos de retorno como hagan falta, hasta sumar lo que se vendió. Hacienda lleva esa suma y rechaza el exceso; Facta la lleva también, línea por línea, y se la dice antes de firmar nada. Para saber cuánto queda, consulte el documento:
curl "$FACTA_API_BASE_URL/v1/dte/7875BC7A-9580-441D-94E4-FA455E9D8BD0" \
-H "X-Facta-Key: $FACTA_API_KEY"La respuesta trae disponible (por cada línea: lo vendida, lo devuelta y lo disponible) y retornos, la lista de eventos ya registrados. Si el documento se emitió desde la app, disponible es null: esta API solo puede registrar eventos de retorno sobre los documentos que ella misma emitió.
El cuerpo: líneas desde 1
curl -X POST "$FACTA_API_BASE_URL/v1/dte/7875BC7A-9580-441D-94E4-FA455E9D8BD0/return" \
-H "X-Facta-Key: $FACTA_API_KEY" \
-H "X-Facta-Sign-Key: $FACTA_SIGN_KEY" \
-H "Idempotency-Key: retorno-venta-00417-a" \
-H "Content-Type: application/json" \
-d '{ "items": [ { "linea": 1, "cantidad": 1 } ] }'Las líneas se cuentan desde 1, como en la factura impresa: linea: 1 es la primera. Cada item lleva una de dos formas, nunca las dos:
| Campo | Qué es |
|---|---|
cantidad | Unidades que regresan de esa línea. El precio, la descripción y los tributos se copian de la original; si hubo descuento, se prorratea por la parte devuelta. |
noGravado | Un cargo (positivo) o un abono (negativo) que no afecta la base imponible. Solo mueve el total a pagar, no puede pasar de lo que traía la línea ni cambiar de signo, y una factura de sujeto excluido no lo admite. |
fechaEvento es opcional (AAAA-MM-DD, por defecto hoy en El Salvador). El plazo es de tres meses desde que el documento se generó o se selló, el que venza antes, o de dos años para facturas de ciertas actividades económicas.
Lo que contesta
| Estado | Qué significa |
|---|---|
200 | Sellado. Trae selloRecibido, totales del evento, jws, archivoJson y el disponible que queda ya contando este evento. |
202 | Hacienda no contestó. Vea la sección siguiente. |
409 | idempotency_in_flight o sign_vault_missing: la misma petición sigue en curso, o la llave no está provisionada para firmar. |
422 | No se pudo registrar. El code dice por qué. |
Los códigos de la familia 422:
return_exceeds_available: se pide más de lo que queda.details.lineastrae, por cada línea, losolicitadoy lodisponible.return_window_closed:fechaEventoestá fuera del plazo.return_type_not_allowed: el documento no es 01, 11 ni 14.validation_failed: el documento no tiene sello, está anulado, o el evento no cumple el esquema oficial.mh_rejected: Hacienda leyó el evento y lo negó. Sus unidades vuelven al saldo.
Cuando Hacienda no contesta
El evento quedó firmado y registrado, y sus unidades ya cuentan como devueltas: se le debe a Hacienda, como se le debe un DTE en contingencia. La respuesta es 202 con estado: "firmado" y el jws que se enviará.
Repita la misma llamada: la misma Idempotency-Key y el mismo cuerpo. Antes de reenviar, la API le pregunta a Hacienda qué guarda de ese evento: si ya lo selló, asienta el sello; si lo rechazó, asienta el rechazo. Solo reenvía el mismo evento firmado cuando Hacienda no tiene registro de él; nunca arma un segundo evento. Con otra Idempotency-Key, en cambio, sería una devolución nueva sobre las mismas unidades, y la API la rechazaría por exceso.
Un documento con eventos de retorno no se anula
Hacienda lo rechaza (Anexo V 44.4), y Facta lo detiene antes: POST …/invalidate contesta 409 has_return_events y no manda nada. Si el documento tiene un error de fondo, corríjalo con más eventos de retorno o con una nota de crédito.
Guardar el archivo
archivoJson son los bytes exactos del JSON del evento: guárdelos tal cual, como guarda el del DTE. El evento también se descarga con su propio código en GET /v1/dte/{codigoGeneracion}/file?kind=json, mientras el área de retención de la API (una hora) o el almacenamiento administrado lo tengan.
La hoja carta del evento viene en la respuesta, en representacionGrafica (PDF en base64), y se descarga con GET …/file?kind=pdf por el código del evento. Pasado el área de retención, Facta rearma el JSON y la hoja de la firma que guarda con cada evento sellado; solo un evento sin firma guardada contesta 404 return_pdf_unavailable, y un evento de retorno no tiene ticket. Para contarlo en sus destinos propios, escríbalo con el código del evento y avíselo en POST /v1/storage/copies/{codigoGeneracion}/byos, igual que un documento.
En código
El SDK de TypeScript lo expone como registerReturn. En los demás lenguajes hay un ejemplo completo de cada uno en sdks/<lenguaje>/examples/retorno.*, con la misma estructura que el de emitir: leer lo que queda, registrar el evento, distinguir 200, 202, 409 y 422, y guardar el JSON.
Otras guías
Emitir un DTE
Cómo llamar a POST /v1/dte, qué llega firmado por dentro y las tres respuestas que puede recibir.
Leer →Preparar y firmar por separado
Por qué separar la reserva del correlativo de la firma, el prepareToken y sus 15 minutos, y qué pasa si el documento cambia entre las dos llamadas.
Leer →