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

El código HTTP ya dice qué tipo de problema es y si tiene sentido reintentar:

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 documentos)Con 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.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.