Saltar al contenido

    Errores de la API

    Todos los errores de Pabilo llegan con la misma forma y un código estable contra el que puedes programar.

    Formato de la respuesta

    Sin importar el endpoint, un error siempre devuelve exactamente dos claves. El status HTTP acompaña al código.

    Ejemplo de error
    {
      "message": "bad request: client phone or email is required: error building subscription",
      "error": "BAD_REQUEST"
    }
    CampoPara qué sirve
    errorEl código estable. Es lo único contra lo que debes programar. No cambia entre versiones.
    messageTexto humano para depurar. Arrastra todo el contexto del intento, separado por :, del más general al más específico. Puede cambiar sin aviso.

    Errores no controlados

    Si algo falla fuera de los casos previstos, el status es 500 y el código es internal_server_error — en minúscula, a diferencia del resto que va en mayúsculas. Trátalo como un fallo inesperado y repórtalo con el message completo.

    json
    {
      "message": "unexpected EOF",
      "error": "internal_server_error"
    }

    Catálogo de códigos

    Autenticación y permisos

    CódigoHTTPSignificadoQué hacer
    UNAUTHORIZED401No enviaste credenciales o el token/API key no es válido.Revisa el header Authorization: Bearer o appKey. Si el JWT expiró, refréscalo.
    FORBIDDEN403Estás autenticado pero el recurso no es tuyo, o tu empleado no tiene el permiso.No reintentes. Verifica que el recurso pertenezca a tu cuenta.
    USER_IS_NOT_ACTIVE400La cuenta está deshabilitada.Contacta a soporte.
    CLIENT_IS_NOT_ACTIVE400El cliente con el que operas está deshabilitado.Reactívalo con PUT /v1/clients/:clientId/toggle-disabled.

    Plan y créditos

    CódigoHTTPSignificadoQué hacer
    PLAN_IS_NOT_ACTIVE400El plan venció o nunca se activó.Renueva el plan. No reintentes hasta entonces.
    PLAN_IS_ALREADY_ACTIVE400Intentaste activar un plan que ya está vigente.Ignorable: el estado deseado ya se cumple.
    NOT_ENOUGH_CREDITS400No hay créditos para cobrar la verificación.Recarga créditos. No reintentes: cada intento fallaría igual.
    REQUEST_LIMIT_REACHED400Se agotó la cuota de peticiones del plan.Espera al siguiente período o sube de plan.
    BANK_ACCOUNT_LIMIT_REACHED400Llegaste al máximo de cuentas bancarias del plan.Elimina una cuenta o sube de plan.
    RENOVATION_IS_ALREADY_PAID400La renovación ya estaba pagada.Ignorable.

    Verificación de pagos

    CódigoHTTPSignificadoQué hacer
    PAYMENT_NOT_FOUND400El banco no tiene ningún movimiento con esa referencia y monto.Muéstraselo al pagador: la referencia está mal o el pago aún no se refleja.
    PAYMENT_AMOUNT_NOT_VALID400La referencia existe pero el monto no coincide con el esperado.Muéstraselo al pagador. Revisa status_detail para el detalle.
    PAYMENT_ALREADY_EXISTS400Esa transferencia ya se usó para confirmar otro pago.No reintentes. Pide una transferencia nueva.
    IS_NOT_POSITIVE_PAYMENT400El movimiento encontrado es un débito, no un crédito.La referencia corresponde a un egreso; pide la correcta.
    IS_NOT_POSITIVE_AMOUNT400El monto enviado es cero o negativo.Corrige el monto antes de reintentar.
    INVALID_MOVEMENT_TYPE400El tipo de movimiento no aplica a esa cuenta bancaria.Consulta los tipos válidos del banco.
    MOVEMENT_TYPE_REQUIRED400Ese banco exige indicar el tipo de movimiento y no lo enviaste.Agrega movement_type al body.

    Banco y conexión

    CódigoHTTPSignificadoQué hacer
    BANK_NOT_AVAILABLE400El banco no respondió o devolvió un error de plataforma.Reintentable con backoff.
    BANK_TEMPORARILY_INACTIVE503El banco está marcado como caído del lado de Pabilo.Reintentable con backoff largo.
    BANK_TOO_MANY_REQUESTS429El banco está limitando la tasa de peticiones.Reintentable: espera antes del siguiente intento.
    USER_BANCK_NOT_FOUND400La cuenta bancaria no existe o no es tuya.Revisa el user_bank_id.
    USER_BANCK_BAD_PASSWORD400Las credenciales guardadas ya no sirven.Actualiza la contraseña de la cuenta bancaria.
    USER_BANCK_PASSWORD_EXPIRED400El banco pide cambiar la contraseña.Cámbiala en el portal del banco y actualízala en Pabilo.
    USER_BANCK_BLOCKED400El banco bloqueó el usuario.Desbloquéalo con el banco. Pabilo envía un correo cuando esto pasa.
    USER_BANCK_BAD_API_KEY400La API key del banco (Binance, Mercantil empresa) no es válida.Regenera la credencial en el banco.
    USER_BANK_ALREADY_EXISTS400Ya registraste esa cuenta bancaria.Usa la existente.
    USER_BANK_IS_DISABLED400La cuenta bancaria está deshabilitada.Reactívala antes de verificar pagos con ella.
    INVALID_BANK_PROVIDER500El proveedor bancario guardado no existe en el sistema.Reporta a soporte: es un dato inconsistente.
    SESSION_ALREADY_ACTIVE400Ya hay una sesión abierta contra el banco para esa cuenta.Reintentable: espera a que la sesión anterior termine.
    PROXY_ERROR400Falló la salida a internet asignada a esa cuenta.Reintentable con backoff.

    Cuenta y registro

    CódigoHTTPSignificadoQué hacer
    THIS_EMAIL_ALREADY_EXISTS400Ese correo ya está registrado.Usa otro correo.
    THIS_USERNAME_ALREADY_EXISTS400El usuario o nickname ya está tomado por un usuario, empleado o cliente.Elige otro. Con clientes puedes usar generate_nick para que Pabilo elija.
    INVALID_PHONE400El teléfono no tiene un formato reconocible.Usa +584121234567, 04121234567, 4121234567 o 584121234567.
    TEMPORAL_CODE_NOT_AVAILABLE400El código expiró, ya se usó o se agotaron los intentos.Pide un código nuevo.

    Secretos bancarios

    CódigoHTTPSignificadoQué hacer
    COORDINATE_KEY_EXTRACTION_FAILED422No se pudo leer la tarjeta de coordenadas de la imagen.Sube una foto más nítida y completa.
    COORDINATE_KEY_NOT_VALID422La imagen no es una tarjeta de coordenadas.Sube la imagen correcta.

    Notificaciones de dispositivo

    CódigoHTTPSignificadoQué hacer
    NOTIFICATION_IGNORED400Es una notificación de sistema conocida, no un pago. Ruido esperado.Ignorable: no reintentes ni la reenvíes.
    NOTIFICATION_PARSE_FAILED400Ningún patrón conocido reconoció el texto de la notificación.Se guarda para escribir el patrón faltante. No reintentes.

    Genéricos

    CódigoHTTPSignificadoQué hacer
    BAD_REQUEST400Validación genérica. El detalle va en message.Lee message: dice exactamente qué campo falta o está mal.
    NOT_FOUND404El recurso no existe.Revisa el id.
    PAGE_NOT_FOUND404La ruta no existe en la API.Revisa la URL y el método.
    METHOD_NOT_SUPPORT405El método HTTP no aplica a esa ruta.Revisa el método en la documentación del endpoint.
    NOT_IMPLEMENTED404La operación no está disponible para ese proveedor bancario.No todos los bancos soportan todas las operaciones.
    INTERNAL_ERROR500Error interno controlado.Reintentable una vez; si persiste, reporta a soporte con el message.
    MISSING_CONFIG400Falta configuración en la cuenta para completar la operación.Lee message para saber qué falta.
    GET_CURRENCY_RATE_FAILED500No se pudo obtener la tasa de cambio.Reintentable con backoff.

    Errores de verificación de pago

    Cuando falla el cobro de un link de pago hay una segunda fuente de información además del código: el campo payment_link.status_detail, que viene en español y está pensado para mostrárselo al pagador. Lo recibes en el webhook y al consultar el link de pago.

    Situaciónstatus_detail
    Monto no coincideMonto del pago no coincide con el esperado
    Banco no disponibleBanco temporalmente no disponible: <detalle>
    Pago no encontradoPago no encontrado en el banco con la referencia proporcionada
    Pago ya usadoEl pago ya fue procesado anteriormente
    OtroEl mensaje del banco, o «Error al procesar el pago»

    Manejo recomendado

    En la práctica los errores caen en tres grupos que se tratan distinto: los que conviene reintentar, los que requieren una acción del dueño de la cuenta, y los que hay que mostrarle al pagador.

    Manejo por familia
    // Reintentar con backoff: son transitorios.
    const REINTENTABLES = new Set([
      "BANK_NOT_AVAILABLE",
      "BANK_TEMPORARILY_INACTIVE",
      "BANK_TOO_MANY_REQUESTS",
      "PROXY_ERROR",
      "SESSION_ALREADY_ACTIVE",
    ]);
    
    // Requieren que el dueño de la cuenta haga algo. Reintentar no sirve.
    const REQUIEREN_ACCION = new Set([
      "NOT_ENOUGH_CREDITS",
      "PLAN_IS_NOT_ACTIVE",
      "REQUEST_LIMIT_REACHED",
      "USER_BANCK_BAD_PASSWORD",
      "USER_BANCK_BLOCKED",
      "USER_BANK_IS_DISABLED",
    ]);
    
    // Se le muestran al pagador: el problema está en la transferencia.
    const DEL_PAGADOR = new Set([
      "PAYMENT_NOT_FOUND",
      "PAYMENT_AMOUNT_NOT_VALID",
      "PAYMENT_ALREADY_EXISTS",
      "IS_NOT_POSITIVE_PAYMENT",
    ]);
    
    async function pagar(linkId, body, intento = 0) {
      const res = await fetch(`https://api.pabilo.app/v1/paymentlink/${linkId}/pay`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(body),
      });
    
      if (res.ok) return res.json();
    
      const { error, message } = await res.json();
    
      if (REINTENTABLES.has(error) && intento < 3) {
        await new Promise((r) => setTimeout(r, 2 ** intento * 1000));
        return pagar(linkId, body, intento + 1);
      }
    
      if (REQUIEREN_ACCION.has(error)) {
        // Avisa al dueño de la cuenta; no reintentes.
        notificarAlComercio(error, message);
        throw new Error(error);
      }
    
      if (DEL_PAGADOR.has(error)) {
        // Muestra el motivo en el checkout.
        mostrarAlPagador(error);
        throw new Error(error);
      }
    
      // Cualquier otro: loguea el message completo, es lo que sirve para depurar.
      console.error("pabilo:", error, message);
      throw new Error(error);
    }