Introducción
El API de Probability permite que tu software (ERP, POS, sistema de bodega o desarrollo propio) cree órdenes, sincronice catálogo e inventario, consulte bodegas y reciba por webhook firmado el resultado de cada operación y los cambios de estado de sus órdenes.
Cómo empezar
- En Probability entra a Integraciones → Crear integración → API de integración → API de Probability.
- Ponle el nombre de tu software y, si ya la tienes, la URL donde recibirás webhooks.
- Al crearla verás una sola vez el
client_id(pk_…), elclient_secret(sk_…), el secreto de webhook (whsec_…) y el identificador de tu negocio (biz_…). Guárdalos en un lugar seguro. - Pide un token (abajo, en Autenticación) y prueba cualquier endpoint desde su consola.
Forma de las respuestas
Toda respuesta trae request_id, también en la cabecera X-Request-Id. Es el dato que debes compartir con soporte.
| Caso | Forma |
|---|---|
| Lectura | { request_id, data } |
| Lista | { request_id, data, total, page, page_size, total_pages } · page_size máximo 100 |
| Escritura aceptada | 202 { request_id, transaction_id, status, kind, replayed, status_url } |
| Error | { request_id, error: { code, message, details } } |
Autenticación
El API usa OAuth 2.0 client_credentials. Cambias tu client_id y client_secret por un token de acceso que dura 1 hora y lo envías en cada llamada:
Authorization: Bearer pat_…
El endpoint de token acepta el cuerpo como formulario (application/x-www-form-urlencoded), como JSON, o las credenciales por HTTP Basic. Cuando el token vence, pide otro. Regenerar las llaves en Probability revoca los tokens activos.
Obtener un token para esta página
El secreto solo vive en la memoria de esta pestaña. El token se guarda en sessionStorage y se borra al cerrar la pestaña; nunca en localStorage.
Permisos
| Permiso | Habilita |
|---|---|
orders:read | GET /orders, GET /orders/{id} |
orders:write | POST /orders, POST /orders/{id}/status |
products:read / products:write | GET /products / POST /products |
inventory:read / inventory:write | GET /inventory / PUT /inventory |
warehouses:read | GET /warehouses |
Los permisos se encienden o apagan desde la integración en Probability. Un permiso apagado responde 403 insufficient_scope.
Transacciones y webhooks
Las lecturas responden en el momento. Las escrituras (crear órdenes, cambiar estados, catálogo e inventario) son asíncronas:
- Validación temprana. Lo que se puede saber sin procesar responde
400 validation_errorcondetailscampo por campo: orden sin ítems, bodega de otro negocio, SKU repetido en el lote, estado desconocido… - Aceptada. Si pasa, respondemos
202contransaction_id. La operación se encola y la ejecuta el módulo de Probability que corresponde. - Resultado por webhook. Cuando termina, enviamos el evento a tu URL. Si no te llega, consulta
GET /transactions/{id}. Una transacción que no termina en 30 minutos pasa afailedcon códigotimeouty emitetransaction.failed.
Idempotencia
Envía la cabecera Idempotency-Key en toda escritura. La misma llave con el mismo cuerpo devuelve la misma transacción (replayed: true y cabecera Idempotent-Replayed: true). La misma llave con otro cuerpo responde 409 idempotency_key_reused. Un external_id de orden ya usado con otra llave responde 409 duplicate_external_id.
Formato del webhook
| Evento | Cuándo llega |
|---|---|
order.created / order.rejected | Resultado de POST /orders. |
order.status_applied / order.status_rejected | Resultado de POST /orders/{id}/status. |
order.status_changed | Cualquier cambio de estado de una orden de tu integración, venga de donde venga (operación, transportadora, otra persona). |
products.upserted | Resultado de POST /products, por SKU. |
inventory.updated | Resultado de PUT /inventory; los SKU desconocidos se reportan. |
transaction.failed | La escritura no se pudo completar. |
webhook.ping | Prueba enviada desde Probability o con POST /webhooks/test. |
Verificar la firma
Cada webhook trae X-Probability-Signature: t=<unix>,v1=<hex>, donde hex = HMAC-SHA256(whsec, "<t>." + cuerpo crudo). Verifica sobre el cuerpo sin parsear, rechaza si t tiene más de 5 minutos y compara en tiempo constante. También llegan X-Probability-Event y X-Probability-Event-Id; usa este último para descartar duplicados.
// Node.js
const crypto = require('crypto');
function verify(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(',').map(p => p.split('=')));
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
const expected = crypto.createHmac('sha256', secret).update(parts.t + '.' + rawBody).digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1 || ''));
}
# Python
import hmac, hashlib, time
def verify(raw_body: bytes, header: str, secret: str) -> bool:
parts = dict(p.split('=', 1) for p in header.split(','))
if abs(time.time() - int(parts['t'])) > 300:
return False
expected = hmac.new(secret.encode(), parts['t'].encode() + b'.' + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get('v1', ''))
Reintentos
Si tu URL no responde 2xx, reintentamos a los 10 s, 60 s, 300 s y 300 s (5 intentos en total). Después la entrega queda como fallida y la ves en Uso del API → Entregas de webhook. Responde 2xx rápido y procesa después.
Errores y límites
| HTTP | code | Qué significa |
|---|---|---|
| 400 | validation_error | La solicitud tiene errores; mira details. |
| 400 | invalid_json | El cuerpo no es un JSON válido para el endpoint. |
| 400 | unsupported_grant_type / invalid_request | Token: solo grant_type=client_credentials, con client_id y client_secret. |
| 401 | invalid_client | client_id o client_secret inválidos. |
| 401 | invalid_token | Token ausente, inválido o vencido. |
| 403 | insufficient_scope | La credencial no tiene permiso para la operación. |
| 403 | integration_inactive | La integración está desactivada en Probability. |
| 404 | not_found | El recurso no existe o no es de tu negocio. |
| 409 | duplicate_external_id | Ya existe una orden con ese external_id en tu integración. |
| 409 | idempotency_key_reused | La Idempotency-Key ya se usó con otro cuerpo. |
| 413 | payload_too_large | El cuerpo supera 2 MB. |
| 429 | rate_limited / slow_down | Demasiadas solicitudes; respeta Retry-After. |
| 503 | queue_unavailable | No pudimos encolar la escritura; reintenta con la misma Idempotency-Key. |
| 500 | internal_error | Error nuestro; comparte el request_id. |
Límites
- 20 solicitudes por segundo por integración, con ráfagas de hasta 60.
/oauth/token: 1 por segundo, ráfagas de hasta 10.- Cuerpo máximo de 2 MB. Lotes de hasta 100 productos o 500 filas de inventario; órdenes de hasta 200 ítems.
- Paginación:
pageypage_size(por defecto 10, máximo 100).