VerifikaDocs
Guías

Errores

El catálogo completo, qué se reintenta y qué no, y para qué sirve el requestId.

Todos los errores tienen la misma forma:

{
  "error": {
    "code": "SCOPE_REQUIRED",
    "message": "Esta API key no tiene permiso para esta operación.",
    "details": { "scope": "expenses.read" },
    "requestId": "req_b0b926d7fbe34e53be33d869981cc9b9",
    "docUrl": "https://docs.verifika.tech/docs/guias/errores"
  }
}

Ramifica por code, nunca por message. Los códigos son parte del contrato; los mensajes están en español para humanos y pueden cambiar de redacción. Lo mismo con details: usa los campos que esta página nombra y no dependas de los demás.

HTTPcodeQué pasó¿Reintentar?
400BAD_REQUESTCuerpo o parámetros inválidos. details trae el detalle. También sale si tu llave entra a varios negocios y no mandaste companyId.No, hasta corregir
401UNAUTHORIZEDFalta la llave, o es inválida, revocada, vencida, o viene de una IP no permitida.No
403SCOPE_REQUIREDLa llave no tiene ese permiso. details.scope dice cuál.No
403PLAN_FEATURE_REQUIREDEl plan no incluye la función. details.feature dice cuál (public_api, webhooks_outbound, expenses_module…). Qué incluye tu plan lo dice GET /v1/plan.No
403PLAN_LIMIT_REACHEDSe agotó un límite del plan, casi siempre las verificaciones del mes. details.feature, details.limit y details.current.No, hasta el mes siguiente o un cambio de plan
403ACCOUNT_TRIAL_ENDED · ACCOUNT_CANCELED · ACCOUNT_READ_ONLYLa cuenta está bloqueada: terminó la prueba, se canceló o tiene un pago pendiente. Los tres traen details.readOnly: true y details.reason. Cierra también las lecturas.No, hasta que el dueño la regularice
403TEST_MODE_UNAVAILABLELlave de prueba (vk_test_) en un endpoint o una escritura sin modo prueba. Ver Modo de prueba.No, usa una llave real
403FORBIDDENEl companyId no es de esta llave, la llave no sirve para esta API, o —en POST /v1/verifications— el phone es de alguien del equipo cuyo rol no puede registrar pagos.No
404NOT_FOUNDEl recurso no existe o no es tuyo.No
404PHONE_NOT_REGISTEREDEn POST /v1/verifications: el phone no está en el equipo del negocio.No, hasta agregarlo en Equipo
409IDEMPOTENCY_IN_PROGRESSEsa Idempotency-Key se está procesando.Sí, en unos segundos
409IDEMPOTENCY_KEY_REUSEDMisma llave con cuerpo distinto.No, usa una llave nueva
409INCOME_NOT_EDITABLEEse ingreso no se edita ni se borra por API: no lo declaró nadie a mano (nació de un comprobante o del banco), o es un abono de cuentas por cobrar, que se anula desde el cuaderno.No
422RECEIVABLE_OVERPAYMENTUn abono o una condonación por más de lo que debe el cliente. details.balance es el saldo real.No, hasta corregir el monto
422RECEIVABLE_ALREADY_PAIDLa cuenta ya está en cero: no se salda otra vez ni se mueve de etapa.No
422RECEIVABLE_STAGE_REQUIRES_PAYMENTPediste la etapa PAID con saldo pendiente. Se salda con POST …/settle.No
422RECEIVABLE_NEGATIVE_BALANCEAnular ese fiado dejaría al cliente con saldo a favor. Anula primero el abono.No
429RATE_LIMITEDTe pasaste del límite.Sí, respetando Retry-After
429TEST_MODE_LIMIT_REACHEDTu llave de prueba llegó a 200 verificaciones en 24 horas.Sí, más tarde
503AI_UNAVAILABLELa lectura con IA (de un comprobante o de una factura) falló.Según details.retryable
500INTERNAL_ERRORNos falló algo.Sí, con espera creciente

404 y 403 no se distinguen a propósito

Pedir un ingreso de otro negocio devuelve 404, no 403. Si respondiéramos "no tienes permiso", estaríamos confirmando que ese id existe — y eso convierte el endpoint en una forma de descubrir datos ajenos probando ids.

Los de verificaciones, conciliación y gastos con IA

Cada uno tiene su tabla en su guía:

  • Verificar un comprobante —IMAGE_REQUIRED, INVALID_IMAGE_URL, BLOCKED_IMAGE_URL, IMAGE_TOO_LARGE, IMAGE_TYPE_NOT_ALLOWED, IMAGE_FETCH_FAILED, IMAGE_FETCH_TIMEOUT, INVALID_IMAGE_BASE64, PHONE_REQUIRED—: Verifica desde tu propio bot. Ojo: un comprobante duplicado o que no es un comprobante no es un error: es un 200 con status: DUPLICATE o REJECTED.
  • Conciliación —STATEMENT_*, LINE_*, BULK_TOO_LARGE, SERVICE_BUSY—: Conciliación por API.
  • Gastos con IA —EXPENSE_DUPLICATE, EXPENSE_SCAN_*, FILE_*—: Gastos con IA.

details.retryable y details.customerMessage

Muchos errores traen details.retryable: true quiere decir que repetir lo mismo (con la misma Idempotency-Key) puede funcionar —una URL que no respondió, la IA que falló un momento—; false, que va a fallar igual hasta que cambies algo. Cuando lo traen, créele más que a la regla general de abajo.

Los errores de cupo agotado (PLAN_LIMIT_REACHED) o cuenta bloqueada (ACCOUNT_*) de una verificación traen además details.customerMessage: la frase para la persona que pagó («En este momento no podemos verificar pagos para este negocio. Comunícate directamente con el negocio.»), que no le cuenta lo que pasa con la cuenta del negocio.

Qué reintentar

Regla corta: 429 y 5xx se reintentan con espera creciente; el resto, no. Un 403 no se arregla insistiendo, y un 400 tampoco.

const REINTENTABLE = (status) => status === 429 || status >= 500;

El requestId

Va en el cuerpo de todo error y en la cabecera X-Request-Id de toda respuesta, incluidas las exitosas.

Guárdalo en tus logs. Con ese id, soporte encuentra tu petición exacta; sin él, un reporte de "a veces me da error" no se puede investigar.

Si tu sistema ya genera ids de traza, mándalo tú en X-Request-Id y lo respetamos, así tu traza no se parte al entrar a Verifika.

En esta página