Errores
El formato de los errores y qué hacer con cada uno.
Todos los fallos tienen la misma forma:
{
"error": "issuer_not_found",
"message": "The issuer profile does not exist"
}error es una clave estable; message es texto en inglés para quien
desarrolla y puede cambiar de redacción. Decidí sobre el código, no sobre el
mensaje, y traducilo vos: enganchar tu interfaz a esa prosa es enganchartela a
algo que va a cambiar.
En un fallo de validación se agrega issues con qué campo falló y por qué.
Por código HTTP
El código HTTP ya dice qué tipo de problema es y si tiene sentido reintentar:
| HTTP | Qué pasó | Qué hacer |
|---|---|---|
400 | El cuerpo o un parámetro no cumple el esquema | Corregir la petición; mirá issues |
401 | Llave ausente, mal formada, inexistente o revocada | Revisar Authorization |
404 | No existe, o no es tuyo | No reintentar |
409 | Conflicto: duplicado, o un límite alcanzado | Depende del código |
422 | El documento no se puede emitir como viene | Corregir los datos |
429 | Superaste un límite (de peticiones o de documentos) | Con Retry-After, esperalo; sin él, es el tope mensual |
503 | Una dependencia no respondió | Reintentar con espera creciente |
Los que vas a ver
invalid_payload y invalid_path_parameter son de forma: algo no cumple el
esquema.
issuer_not_found y issuer_id_required son de configuración: el emisor no
existe, o no dijiste cuál cuando hay más de uno.
issuer_limit_reached significa que la cuenta llegó a su tope de emisores. Es
un límite técnico del motor, no una licencia. El tope cuenta solo los activos,
así que desactivar uno libera su espacio, y reactivarlo
después puede devolver este mismo código, si ese espacio ya lo ocupa otro.
document_limit_reached es otro límite, y no es el de peticiones por
segundo: es cuántos documentos podés finalizar este mes en producción.
Sandbox no tiene tope. Se revisa solo al finalizar, nunca al crear un
borrador, y un reintento con la misma Idempotency-Key de un documento que
ya finalizaste nunca lo dispara, no es una emisión nueva. El mes se cuenta en
hora de Costa Rica. Podés ver cuánto llevás y cuál es tu tope con
curl https://api.timbrecr.com/core/v1/usage \
-H "Authorization: Bearer timbre_sk_…"que responde documentsThisMonth, monthlyDocumentLimit (null es sin
tope) y periodStart, el mismo número contra el que el motor te va a
rechazar si lo alcanzás.
Los certificate_* son del .p12 que cargaste y todos son 400:
certificate_password_invalid es un PIN que no lo abre,
certificate_expired y certificate_not_yet_valid son fechas, y
certificate_identification_mismatch es un certificado que no corresponde a la
cédula que declaraste.
duplicate_branch_code y duplicate_terminal_code son códigos ya registrados:
el de sucursal es único por contribuyente y el de terminal, único dentro de su
sucursal.
representation_not_enabled es un 403 al pedir el PDF de un documento que
se aceptó cuando el plan no incluía PDF. Esperar no lo genera; los XML firmados
siguen disponibles. Más en Artefactos.
tariff_ambiguous significa que Timbre no supo qué tarifa corresponde y
prefirió negarse a adivinar. Hoy pasa con el 8 %. Un motor fiscal que adivina
produce documentos que parecen correctos y no lo son.
cabys_unknown es un código CAByS que no está en la versión del catálogo
vigente en la fecha de la venta. No lo confundas con tariff_ambiguous: acá
el código ni siquiera existe, ahí existe pero su tarifa no tiene código legal.
tax_supplied_by_caller pasa si una línea trae campos de impuesto que le
corresponden a Timbre derivar. No se reconcilian ni se ignoran: se rechazan,
para que nunca circule un documento cuyo impuesto lo puso quien lo mandó y no
el catálogo.
regime_unsupported es un contribuyente en un régimen que la derivación
todavía no modela (hoy, zona_franca). El alta lo acepta como taxRegime,
pero emitir a su nombre se rechaza hasta que ese régimen esté implementado.
expected_total_mismatch solo aparece si mandaste expectedTotal: es una
aserción tuya sobre el total, y si no coincide con lo que Timbre calculó,
rechaza en vez de firmar un documento que ya sabe que no es el que
esperabas.
Los exoneration_* son de la exoneración del comprador
(exoneration.authorizationNumber), y cada uno distingue una causa distinta
para no convertir una duda en una línea a precio completo sin decírtelo:
exoneration_not_found es un número que Hacienda no reconoce,
exoneration_expired es uno real pero fuera de vigencia en la fecha del
documento, exoneration_cabys_not_covered es uno vigente que no cubre
ninguno de los CAByS del documento, y exoneration_unavailable es que no se
pudo verificar con Hacienda: Timbre prefiere rechazar antes que cobrarle IVA
de más a alguien exonerado.
rate_limit_exceeded viene con Retry-After.
service_unavailable es del lado de Timbre o de Hacienda. Reintentá; no
cambies el documento.
Reintentar, y cuándo no
429 y 503 se reintentan con espera creciente. 400, 401, 403, 404 y
422 no: el resultado va a ser el mismo hasta que cambie algo tuyo.
Un documento en permanent_error o rejected no se reintenta: se corrige el
problema y se emite uno nuevo. Reencolar solo aplica a un
documento que quedó en finalized sin llegar a la cola.
Reintentar una emisión siempre con la misma Idempotency-Key. Sin ella,
el reintento de una petición que no viste responder emite un segundo documento
y gasta otro consecutivo.