Timbre

Errores

El formato de los errores y qué hacer con cada uno.

Todos los fallos tienen la misma forma:

Respuesta
{
  "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

HTTPQué pasóQué hacer
400El cuerpo o un parámetro no cumple el esquemaCorregir la petición; mirá issues
401Llave ausente, mal formada, inexistente o revocadaRevisar Authorization
404No existe, o no es tuyoNo reintentar
409Conflicto: duplicado, o un límite alcanzadoDepende del código
422El documento no se puede emitir como vieneCorregir los datos
429Superaste un límite —de peticiones o de documentosCon Retry-After, esperalo; sin él, es el tope mensual
503Una 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

Petición
curl https://api.timbre.cr/internal/v1/usage \
  -H "Authorization: Bearer timbre_sk_…"

que responde documentsThisMonth, monthlyDocumentLimitnull 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.

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.