Inbound · Vos la llamás

REST API Reference

Tu POS llama a BipBip para aceptar, rechazar, avanzar el estado y consultar órdenes. Esta es la contraparte del Webhook Spec: una vez que BipBip te entrega una orden, usás esta API para gestionar su ciclo de vida.

Base URL

Todos los endpoints son relativos a https://api.bipbip.com. Las rutas llevan prefijo /api/v1/ con versionado en el path.

Autenticación

Cada request debe incluir el header X-Bipbip-Api-Key con el valor entregado por el equipo BipBip durante el onboarding. No usamos Bearer tokens ni OAuth para esta API. Para obtener tus API Keys, contactá al equipo BipBip en [email protected].

# Cada llamada debe llevar este header
curl -H "X-Bipbip-Api-Key: <tu-api-key>" \
     https://api.bipbip.com/api/v1/Orders/<orderKey>

Protegé tu API Key

No la expongas en el frontend, en repositorios públicos ni en bundles del cliente. Guardala en variables de entorno del servidor y nunca la commitees.

Paths case-sensitive

El servidor no normaliza casing. El segmento /Orders/ va con O mayúscula literal — /orders/ devuelve 404. Respetá el casing exacto de cada path documentado acá.

Idempotency-Key

Todas las mutaciones (/accept, /reject, /status) requieren el header Idempotency-Key. El valor debe ser un UUID v4 único por intento lógico.

  • Misma key + mismo body dentro de las 24h → BipBip devuelve la respuesta en caché sin reejecutar.
  • Misma key + body diferente → HTTP 409 Conflict.
  • Key nueva → request procesado normalmente.
  • Sin header en mutaciones → HTTP 422 Unprocessable Entity.
{
  "type": "https://errors.bipbip.com/idempotency-conflict",
  "title": "Idempotency conflict",
  "detail": "Idempotency-Key reused with a different request body.",
  "status": 409,
  "traceId": "00-abc...",
  "meta": null
}

Rate limits

Los límites se aplican por API Key y están activos en todos los endpoints. Si los superás recibís HTTP 429 — implementá exponential backoff con jitter en tu cliente.

Ventana Límite Estrategia
Ventana fija 100 requests / min Reset cada 60s
Ventana deslizante 1.000 requests / hora Evaluada continuamente

Errores

La API sigue el formato RFC 7807 Problem Details. Todos los errores devuelven un body JSON con los campos type, title, detail, status, traceId y meta. Para errores de validación (422), meta incluye un mapa de campo → lista de mensajes.

Código Significado
401 Unauthorized API Key ausente o inválida
404 Not Found orderKey no existe o no pertenece al cliente autenticado
409 Conflict Idempotency-Key reutilizada con body diferente, orden en estado incorrecto para la transición, o transición inválida
422 Unprocessable Entity Datos inválidos en el body o falta el header Idempotency-Key. El campo meta.errors detalla los campos problemáticos.
429 Too Many Requests Rate limit superado — implementá backoff exponencial con jitter
5xx Error del servidor — reintentá con backoff

Error genérico (401, 404, 409, 429)

{
  "type": "https://errors.bipbip.com/not-found",
  "title": "Order not found",
  "detail": "The order ord_xxx does not exist or does not belong to the authenticated client.",
  "status": 404,
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
  "meta": null
}

Error de validación (422)

{
  "type": "https://errors.bipbip.com/validation",
  "title": "Validation failed",
  "detail": "One or more validation errors occurred.",
  "status": 422,
  "traceId": "00-abc...",
  "meta": {
    "errors": {
      "reasonCode": ["The ReasonCode field is required."]
    }
  }
}

Orders

Gestioná el ciclo de vida de las órdenes que BipBip te entregó por webhook. Los endpoints de mutación requieren X-Bipbip-Api-Key e Idempotency-Key.

POST /api/v1/Orders/{orderKey}/accept

Transiciona la orden de pending a accepted. Cancela el job de timeout de aceptación y publica el evento order.accepted_by_external_merchant.v1. Body: { remoteOrderId?, estimatedPrepTimeMinutes? }. Idempotente: misma Idempotency-Key + mismo body → respuesta cacheada. Responde 202.

Example Response
{
  "message": "Order accepted",
  "data": {
    "orderKey": "ord_7h3nXpQa2KvLmN9F",
    "status": "accepted",
    "acceptedAt": "2026-04-11T15:44:12Z",
    "remoteOrderId": "POS-2026-04-11-00142"
  }
}
POST /api/v1/Orders/{orderKey}/reject

Transiciona la orden de pending a rejected (terminal). Requiere un reasonCode (int) obligatorio. Body: { reasonCode*, reason?, notes? }. Publica el evento order.rejected_by_external_merchant.v1. Idempotente. Responde 202.

Example Response
{
  "message": "Order rejected",
  "data": {
    "orderKey": "ord_7h3nXpQa2KvLmN9F",
    "status": "rejected",
    "rejectedAt": "2026-04-11T15:44:30Z",
    "reason": "Producto agotado"
  }
}
POST /api/v1/Orders/{orderKey}/status

Cambia el estado de una orden ya aceptada. Transiciones válidas: accepted → preparing → ready → handedOver. También permite cancelar (→ cancelled). Body: { newStatus* (MerchantStatus: pending/accepted/rejected/preparing/ready/handedOver/cancelled/driverAssigned), estimatedReadyAt? }. Publica order.status_changed_by_external_merchant.v1. Responde 202.

Example Response
{
  "message": "Status updated",
  "data": {
    "orderKey": "ord_7h3nXpQa2KvLmN9F",
    "status": "preparing",
    "changedAt": "2026-04-11T15:47:00Z"
  }
}
GET /api/v1/Orders/{orderKey}

Devuelve el snapshot completo de la orden: estado actual, timestamps de cada transición (receivedAt, acceptedAt?, rejectedAt?, preparingAt?, readyAt?, driverAssignedAt?, handedOverAt?, cancelledAt?) e historial completo de cambios. Solo retorna órdenes del cliente autenticado. No requiere Idempotency-Key. Responde 200.

Example Response
{
  "message": "Ok",
  "data": {
    "orderKey": "ord_7h3nXpQa2KvLmN9F",
    "storeId": 42,
    "brandId": 7,
    "status": "preparing",
    "merchantStatusReason": null,
    "remoteOrderId": "POS-2026-04-11-00142",
    "receivedAt": "2026-04-11T15:42:00Z",
    "acceptedAt": "2026-04-11T15:44:12Z",
    "rejectedAt": null,
    "preparingAt": "2026-04-11T15:47:00Z",
    "readyAt": null,
    "driverAssignedAt": null,
    "handedOverAt": null,
    "cancelledAt": null,
    "history": [
      {
        "fromStatus": null,
        "toStatus": "pending",
        "changedAt": "2026-04-11T15:42:00Z",
        "actorType": "system",
        "actorId": null,
        "reason": null
      }
    ]
  }
}
GET /api/v1/Orders

Lista órdenes del cliente autenticado con paginación por cursor opaco. Query params: Cursor?, PageSize? (máx 100), Status?, FromDate?, ToDate?. Ordenadas por fecha de creación descendente. Pasa nextCursor de la respuesta anterior para obtener la siguiente página. Responde 200.

Example Response
{
  "message": "Ok",
  "data": {
    "items": [
      {
        "orderKey": "ord_7h3nXpQa2KvLmN9F",
        "storeId": 42,
        "status": "preparing",
        "receivedAt": "2026-04-11T15:42:00Z",
        "acceptedAt": "2026-04-11T15:44:12Z",
        "remoteOrderId": "POS-2026-04-11-00142"
      }
    ],
    "nextCursor": "eyJsYXN0SWQiOjE3fQ",
    "hasMore": true
  }
}

Máquina de estados

Las transiciones válidas desde la REST API son: pendingaccepted / rejected, acceptedpreparing, preparingreadyhandedOver. El estado driverAssigned lo setea BipBip automáticamente entre ready y handedOver (el comercio no transiciona a este estado, solo lo lee). Ver el glosario para detalles. Nota: los valores de status en la REST API son camelCase lowercase (ej: handedOver, driverAssigned), distinto de los webhooks que usan PascalCase.