Documentación de la API

Integra Numbay por API REST: consulta tu saldo, renta números, lee mensajes, inicia recargas y recibe eventos en tiempo real con webhooks firmados.

Crear llave de API

Las llaves de API y los webhooks se gestionan en Ajustes.

API pública

Base: https://numbay.io/api/v1 · Autenticación: Authorization: Bearer nb_live_... (o header x-api-key). Las llaves se crean en Settings → API con scope de solo lectura (read) o lectura y escritura (read+write).

Dinero siempre en centavos enteros (amountCents). Errores en JSON: { "error": "CODE", "message"? }. Rate limit por llave: 120/min lectura, 20/min escritura → 429 con header Retry-After.

GET /wallet (read)

Saldo y últimos movimientos. Query: ledger_limit (1–100, default 20).

{ "data": { "balanceCents": 5000, "currency": "USD", "ledger": [ { "id": "...", "amountCents": 1000, "type": "TOPUP", "refId": "...", "note": "...", "balanceAfter": 5000, "createdAt": "..." } ] } }

GET /numbers (read)

Números del usuario. Query: status (ACTIVE|SUSPENDED|RELEASED).

GET /numbers/available (read)

Inventario rentable. Query: country (ej. US), limit (1–20, default 10).

POST /numbers (write)

Renta un número. Compra inventario real: usa Idempotency-Key en los reintentos.

POST /api/v1/numbers
Idempotency-Key: <uuid-propio>
{ "country": "US", "e164": "+14155551008" }

201 { "data": { "id", "e164" } } · Errores: 400 INVALID_INPUT, 402 INSUFFICIENT_BALANCE, 403 KYC_REQUIRED, 409 NO_INVENTORY.

GET /messages (read)

Mensajes, más recientes primero. Query: numberId, direction (INBOUND|OUTBOUND), since (ISO 8601), limit (1–100), cursor (id devuelto en nextCursor).

{ "data": [ { "id": "...", "numberId": "...", "conversationId": "...", "direction": "INBOUND", "from": "+1...", "to": "+1...", "body": "...", "segments": 1, "status": "RECEIVED", "costCents": 0, "createdAt": "..." } ], "nextCursor": "..." }

POST /topups (write)

Inicia una recarga. Acepta Idempotency-Key.

{ "gateway": "QVAPAY" | "TRONDEALER", "amountCents": 100..100000 }

201 { "data": { "id", "gateway", "url"?, "address"?, "amountCents" } } — QvaPay devuelve url de pago; TronDealer devuelve address (USDT/USDC en BSC). La acreditación llega por webhook interno/reconciliación: consulta el estado o suscríbete al evento payment.completed.

GET /topups/{id} (read)

{ "data": { "id", "status": "PENDING|PAID|EXPIRED|FAILED", "gateway", "amountCents", "asset", "url"?, "address"?, "createdAt", "paidAt" } }

Webhooks salientes

Configura endpoints en Settings → Webhooks (URL https pública). Eventos:

  • message.received — SMS entrante en uno de tus números. data = mismo formato que GET /messages.
  • payment.completed — recarga acreditada. data = { id, gateway, amountCents, asset, paidAt }.

Cuerpo de cada entrega:

{ "id": "<deliveryId>", "event": "message.received", "createdAt": "...", "data": { } }

Firma

Headers: x-numbay-event, x-numbay-timestamp (epoch segundos), x-numbay-signature. La firma es HMAC-SHA256 en hex de ${timestamp}.${rawBody} con el secreto del endpoint. Verificación (Node):

import { createHmac, timingSafeEqual } from "node:crypto";

function verify(req, rawBody, secret) {
  const ts = req.headers["x-numbay-timestamp"];
  const expected = createHmac("sha256", secret).update(`${ts}.${rawBody}`).digest("hex");
  const got = req.headers["x-numbay-signature"] ?? "";
  return got.length === expected.length &&
    timingSafeEqual(Buffer.from(got), Buffer.from(expected)) &&
    Math.abs(Date.now() / 1000 - Number(ts)) < 300; // tolerancia 5 min
}

Entrega y reintentos

Responde 2xx en menos de 5 s (procesa async si necesitas más). Semántica at-least-once: deduplica por id. Reintentos con backoff: 1m, 5m, 15m, 1h, 4h, 12h, 24h (8 intentos en total) y luego FAILED definitivo. Desde el panel puedes ver las últimas entregas, reenviarlas manualmente y rotar el secreto.

¿Integras con un agente de IA? Toda esta referencia está disponible en texto plano en /llms-full.txt