Provibet API de Casino
OpenAPI

API de Casino · v1

Te damos la bienvenida a la API de Casino de Provibet

Una sola integración servidor a servidor para sumar a tu plataforma un catálogo de más de 3.400 juegos de 27 estudios, con jugadores propios, fichas prepagas y apertura de juegos embebible en tu sitio.

Cómo funciona

Provibet funciona con un modelo prepago de transferencia. Provibet asigna fichas (CRD) a la billetera de tu cuenta; tu backend las entrega a cada jugador antes de jugar y las recupera cuando termina. Mientras juega, el saldo del jugador cambia dentro de la plataforma de juego: no hay notificaciones por apuesta ni por premio.

  1. Consultás tu billetera prepaga para saber cuántas fichas tenés disponibles.
  2. Registrás al jugador con un identificador estable de tu sistema.
  3. Le entregás fichas con una clave idempotente propia de esa orden.
  4. Elegís un juego del catálogo y pedís su apertura.
  5. Al terminar, descontás el saldo del jugador para devolverlo a tu billetera.
  6. Cada movimiento de fichas tiene una operación con estado consultable.

Endpoints de la API v1

Todas las rutas cuelgan de la base https://api.provibet.com/api/b2b/v1 y requieren firma HMAC.

Además, las portadas del catálogo se sirven públicamente desde https://api.provibet.com/media/{mediaId} y la página de apertura vive en https://api.provibet.com/play/. Ninguna de las dos requiere firma.

Convenciones

  • Transporte: HTTPS y JSON en UTF-8. Los bodies no pueden superar 64 KiB.
  • Sólo backend: la API se llama desde tu servidor. El secreto HMAC nunca viaja al navegador ni a una app.
  • Importes: strings decimales con hasta dos posiciones ("50", "50.25"); las respuestas siempre usan dos ("50.00"). Ver Moneda.
  • Fechas: ISO 8601 en UTC, por ejemplo 2026-09-24T12:00:00.000Z.
  • Identificadores: los IDs de juegos, medios y operaciones son opacos. Usalos tal como los recibís; no los construyas.
  • Campos: los bodies aceptan sólo los campos documentados (un campo extra responde 400 invalid_request). Tu lector de respuestas debe tolerar campos nuevos.
  • Correlación: cada respuesta incluye el encabezado X-Request-Id. Guardalo junto con tu referencia.

Qué no incluye

  • No hay callbacks ni webhooks por apuesta o premio: el saldo se consulta con GET /players/{id}/balance.
  • No procesa pagos en dinero ni retiros bancarios: mueve fichas entre tu billetera y tus jugadores.
  • No incluye bonos ni promociones.

Soporte e incidentes

Usá el canal que acordaste con Provibet al habilitar tu cuenta. Para que podamos correlacionar el caso, incluí la hora UTC, el endpoint, el código HTTP, error.code, el X-Request-Id de la respuesta y el operation.id si existe.

Nunca compartas tu secreto HMAC, encabezados firmados completos, URLs de apertura de juegos ni datos personales innecesarios.


Primeros pasos

Inicio rápido

Este recorrido te lleva de las credenciales al primer juego abierto. Cada paso muestra la llamada en cURL, Node.js y PHP usando los helpers descargables.

Antes de empezar

Necesitás un backend con salida por IP fija, un gestor de secretos y el reloj sincronizado por NTP. Provibet da de alta tu integración y te entrega:

  • Clave pública (publicKey): empieza con b2b_. Va en el encabezado X-B2B-Key.
  • Secreto HMAC (secret): se muestra una sola vez al crearlo. Guardalo en tu gestor de secretos.
  • Permisos (scopes) y la lista de IP o redes autorizadas desde las que vas a llamar.

Paso a paso

  1. Configurá el entorno

    Descargá node.mjs, request.php o sign-for-curl.mjs y definí estas variables en tu gestor de secretos:

    Variables de entorno
    PROVIBET_PUBLIC_KEY=b2b_TU_CLAVE_PUBLICA
    PROVIBET_SECRET=TU_SECRETO            # nunca en el repositorio ni en logs
    PROVIBET_ORIGIN=https://api.provibet.com

    Para cURL, esta función firma cada solicitud con sign-for-curl.mjs sin pasar el secreto como argumento. La usamos en todos los ejemplos:

    bash · función provibet
    # Requiere Node.js 20+, cURL, sign-for-curl.mjs y node.mjs en la carpeta actual.
    provibet() { # uso: provibet METODO RUTA [BODY_JSON] [IDEMPOTENCY_KEY]
      local work rc url="https://api.provibet.com$2"
      work="$(mktemp -d)" || return 1
      printf '%s' "${3:-}" > "$work/body"
      node sign-for-curl.mjs "$1" "$url" "$work/body" "$work/headers" ${4:+"$4"} &&
        curl -sS -X "$1" "$url" --header "@$work/headers" \
          ${3:+--data-binary "@$work/body"} -w '\nHTTP %{http_code}\n'
      rc=$?; rm -rf "$work"; return $rc
    }
  2. Consultá tu billetera

    Es una lectura sin efectos: ideal para validar clave, secreto, reloj e IP.

    cURL
    provibet GET /api/b2b/v1/wallet
    Node.js
    import { provibetRequest } from './node.mjs';
    
    const { status, requestId, data } = await provibetRequest('GET', '/api/b2b/v1/wallet');
    if (status !== 200) throw new Error(`Consulta fallida: ${status} · ${requestId}`);
    console.log(data.wallet.availableCredits); // "9500.00"
    PHP
    require __DIR__ . '/request.php';
    
    $r = provibetRequest('GET', '/api/b2b/v1/wallet');
    if ($r['status'] !== 200) throw new RuntimeException('Consulta fallida: ' . $r['status']);
    echo $r['data']['wallet']['availableCredits']; // "9500.00"
  3. Registrá un jugador

    Usá un externalPlayerId estable de tu sistema. Si ya existe, la respuesta es la misma.

    cURL
    provibet POST /api/b2b/v1/players '{"externalPlayerId":"jugador-123"}' player-jugador-123-0001
    Node.js
    const body = JSON.stringify({ externalPlayerId: 'jugador-123' });
    const { status } = await provibetRequest('POST', '/api/b2b/v1/players', body, 'player-jugador-123-0001');
    // 201 → { "player": { "externalPlayerId": "jugador-123" } }
    PHP
    $body = json_encode(['externalPlayerId' => 'jugador-123'], JSON_THROW_ON_ERROR);
    $r = provibetRequest('POST', '/api/b2b/v1/players', $body, 'player-jugador-123-0001');
    // 201 → { "player": { "externalPlayerId": "jugador-123" } }
  4. Entregale fichas

    Generá la clave idempotente de la orden y guardala antes de enviar. Si la conexión se corta, reintentás con la misma clave y el mismo importe.

    cURL
    provibet POST /api/b2b/v1/players/jugador-123/credits '{"amountCredits":"50","currency":"CRD"}' credit-order-5001
    Node.js
    const body = JSON.stringify({ amountCredits: '50', currency: 'CRD' });
    // Persistí body y clave en tu base ANTES de enviar.
    const { status, data } = await provibetRequest('POST', '/api/b2b/v1/players/jugador-123/credits', body, 'credit-order-5001');
    // 200 COMPLETED · 202 pendiente (consultá la operación) · 422 REJECTED
    PHP
    $body = json_encode(['amountCredits' => '50', 'currency' => 'CRD'], JSON_THROW_ON_ERROR);
    // Persistí $body y la clave en tu base ANTES de enviar.
    $r = provibetRequest('POST', '/api/b2b/v1/players/jugador-123/credits', $body, 'credit-order-5001');
    // 200 COMPLETED · 202 pendiente (consultá la operación) · 422 REJECTED
  5. Elegí un juego

    cURL
    provibet GET '/api/b2b/v1/games?category=slots&limit=20'
    Node.js
    const { data } = await provibetRequest('GET', '/api/b2b/v1/games?category=slots&limit=20');
    const gameId = data.games[0].id; // ID opaco: guardalo tal cual
    PHP
    $r = provibetRequest('GET', '/api/b2b/v1/games?category=slots&limit=20');
    $gameId = $r['data']['games'][0]['id']; // ID opaco: guardalo tal cual
  6. Abrí el juego

    La respuesta trae una URL temporal de un solo uso. Entregala únicamente al jugador correspondiente; podés abrirla en una pestaña o dentro de un iframe de tu sitio.

    cURL
    provibet POST "/api/b2b/v1/games/$GAME_ID/launch" '{"externalPlayerId":"jugador-123","locale":"es-AR"}' launch-7f3a9c21
    Node.js
    const body = JSON.stringify({ externalPlayerId: 'jugador-123', locale: 'es-AR' });
    const { data } = await provibetRequest('POST', `/api/b2b/v1/games/${gameId}/launch`, body, `launch-${crypto.randomUUID()}`);
    // data.launch.url → entregala al navegador del jugador antes de data.launch.expiresAt
    PHP
    $body = json_encode(['externalPlayerId' => 'jugador-123', 'locale' => 'es-AR'], JSON_THROW_ON_ERROR);
    $r = provibetRequest('POST', "/api/b2b/v1/games/{$gameId}/launch", $body, 'launch-' . bin2hex(random_bytes(8)));
    // $r['data']['launch']['url'] → entregala al navegador del jugador
  7. Recuperá el saldo al terminar

    Cuando el jugador sale, consultá su saldo y descontalo hacia tu billetera con otra clave idempotente. Ver Descontar fichas.

Checklist antes de habilitar tráfico

  • Clave, secreto, IP de salida y permisos confirmados con Provibet.
  • Secreto sólo en backend: fuera del frontend, del repositorio y de los logs.
  • Reloj sincronizado, nonce nuevo por intento y cadena canónica de nueve líneas verificada.
  • Claves idempotentes de créditos y débitos persistidas antes del envío y reutilizadas tras un timeout.
  • Importes como strings decimales, sin pasar por números de punto flotante.
  • Manejo de 202, 409, 422, 429 y fallas de transporte probado.
  • Operaciones UNKNOWN seguidas por GET /operations/{id}, sin liberar ni repetir por tiempo.
  • operation.id, referencia propia y X-Request-Id registrados y correlacionados.
  • Apertura probada en móvil y escritorio; dominio del iframe registrado por Provibet.

Primeros pasos

Panel de gestión

El back office de Provibet, en admin.provibet.com, es donde se administra cada cuenta de integración: credenciales, permisos, fichas asignadas y seguimiento de operaciones.

Acceso administrado por Provibet. Hoy el panel lo opera el equipo de Provibet, con usuario, contraseña y código de verificación de dos pasos. Tu equipo consulta su saldo y sus operaciones por API; cualquier cambio de configuración se pide por el canal acordado.

Integraciones y credenciales

  • Alta de integración: nombre del cliente, IP o redes autorizadas (notación CIDR, por ejemplo 203.0.113.10/32) y permisos.
  • Entrega de credenciales: al crear la integración se generan la clave pública y el secreto HMAC. El secreto se muestra una única vez y se borra de la vista al cerrarla.
  • Revocación: una clave revocada deja de autenticar de inmediato (401 invalid_client_credentials). Para reemplazar credenciales, coordiná el cambio con Provibet.

Asignación de fichas

Las fichas de tu billetera prepaga se cargan desde el panel. Cada asignación registra la cantidad en CRD, una referencia única y el motivo o constancia. Repetir una asignación con la misma referencia no duplica fichas.

Operaciones y conciliación

  • Listado de operaciones de crédito y débito, filtrable por estado.
  • Conciliación de operaciones UNKNOWN: consulta el resultado real y, si se confirma, la operación pasa a COMPLETED. La conciliación nunca reenvía una transferencia.
  • Revocación de sesiones de juego abiertas desde una URL de apertura.

Disponibilidad del servicio

Provibet puede pausar la creación de jugadores, los movimientos de fichas o la apertura de juegos. Mientras una función está pausada, la API responde 503 con un código que lo indica (por ejemplo financial_operations_disabled). El catálogo se actualiza periódicamente desde el panel.


Primeros pasos

Autenticación y firma

Cada solicitud a /api/b2b/v1, incluidas las consultas GET, se firma con HMAC-SHA256. El secreto nunca se transmite: sólo se usa para calcular la firma de la solicitud exacta.

Encabezados

EncabezadoRegla
X-B2B-Key obligatorioTu clave pública: b2b_ seguido de 43 caracteres A-Z a-z 0-9 _ -.
X-B2B-Timestamp obligatorioHora Unix UTC en segundos, exactamente 10 dígitos. Debe estar dentro de ±300 segundos de la hora del servidor.
X-B2B-Nonce obligatorioValor aleatorio de 16 a 128 caracteres A-Z a-z 0-9 _ -. De un solo uso por credencial: generá uno nuevo en cada intento, también en los reintentos.
X-B2B-Signature obligatorioHMAC-SHA256 en hexadecimal minúscula (64 caracteres). Se acepta el prefijo sha256=.
Idempotency-Key en todo POST8 a 128 caracteres: empieza con letra o dígito y sigue con A-Z a-z 0-9 . _ : -. Forma parte de la firma. Ver Idempotencia.
Content-Type en todo POSTapplication/json. Con otro tipo el body no se lee y la firma no coincide.
X-Request-Id opcionalPara tus propios registros. La API no lo reutiliza: cada respuesta trae su propio X-Request-Id (UUID).

Cada encabezado X-B2B-* e Idempotency-Key se envía una sola vez; si se repite, la respuesta es 400 duplicate_authentication_header.

Cadena canónica

La firma se calcula sobre nueve líneas unidas por salto de línea \n (LF), sin salto final:

Formato · orden obligatorio
B2B-HMAC-SHA256
<publicKey>
<timestamp>
<nonce>
<MÉTODO en mayúsculas>
<path exacto enviado, incluido /api/b2b/v1>
<query canonicalizado, o vacío>
<Idempotency-Key, o vacío>
<SHA-256 hexadecimal del body exacto>

La firma es hex(HMAC-SHA256(secret, cadenaCanónica)).

Reglas de cada línea

  • Path: la parte de la URL anterior a ?, exactamente como la enviás, con el prefijo /api/b2b/v1 y los escapes %XX que transmitas. No puede contener espacios, caracteres de control ni #.
  • Query: decodificalo como application/x-www-form-urlencoded (igual que URLSearchParams: + equivale a espacio), ordená los pares por nombre y, a igualdad, por valor (comparación de unidades UTF-16), volvé a codificar cada componente según RFC 3986 y unilos con &. Los parámetros repetidos se conservan.
  • Líneas vacías: sin query o sin clave idempotente, la línea queda vacía. Nunca elimines una línea.
  • Body: SHA-256 en hexadecimal minúscula de los bytes exactos enviados. Un GET sin body usa el hash de cero bytes: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.
  • JSON: serializalo una sola vez y enviá esos mismos bytes. Cambiar espacios u orden después de firmar invalida la firma.

Ejemplo

Para GET /api/b2b/v1/games?limit=20&category=slots el query se reordena alfabéticamente:

Cadena canónica ilustrativa
B2B-HMAC-SHA256
b2b_CLAVE_PUBLICA_DE_EJEMPLO_000000000000000000
1790251200
nonce-unico-de-ejemplo-0001
GET
/api/b2b/v1/games
category=slots&limit=20

e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

Calcular la firma

Node.js
import { createHash, createHmac, randomBytes } from 'node:crypto';

const rfc3986 = v => encodeURIComponent(v).replace(/[!'()*]/g, c => '%' + c.charCodeAt(0).toString(16).toUpperCase());
const canonicalQuery = params => [...params.entries()]
  .sort(([ak, av], [bk, bv]) => ak !== bk ? (ak < bk ? -1 : 1) : av === bv ? 0 : av < bv ? -1 : 1)
  .map(([k, v]) => `${rfc3986(k)}=${rfc3986(v)}`).join('&');

export function sign({ method, url, body = '', idempotencyKey = '', key, secret }) {
  const u = new URL(url);
  const timestamp = String(Math.floor(Date.now() / 1000));
  const nonce = randomBytes(24).toString('base64url');
  const canonical = ['B2B-HMAC-SHA256', key, timestamp, nonce, method.toUpperCase(), u.pathname,
    canonicalQuery(u.searchParams), idempotencyKey, createHash('sha256').update(body).digest('hex')].join('\n');
  return {
    'X-B2B-Key': key, 'X-B2B-Timestamp': timestamp, 'X-B2B-Nonce': nonce,
    'X-B2B-Signature': createHmac('sha256', secret).update(canonical).digest('hex'),
    ...(idempotencyKey ? { 'Idempotency-Key': idempotencyKey, 'Content-Type': 'application/json' } : {}),
  };
}
PHP
// Canonicalización del query: ver provibetCanonicalQuery() en request.php.
function provibetSign(string $method, string $target, string $body, string $idemKey, string $key, string $secret): array {
    [$path, $query] = array_pad(explode('?', $target, 2), 2, '');
    $timestamp = (string) time();
    $nonce = rtrim(strtr(base64_encode(random_bytes(24)), '+/', '-_'), '=');
    $canonical = implode("\n", ['B2B-HMAC-SHA256', $key, $timestamp, $nonce, strtoupper($method), $path,
        provibetCanonicalQuery($query), $idemKey, hash('sha256', $body)]);
    return ['X-B2B-Key: ' . $key, 'X-B2B-Timestamp: ' . $timestamp, 'X-B2B-Nonce: ' . $nonce,
        'X-B2B-Signature: ' . hash_hmac('sha256', $canonical, $secret)];
}
cURL
# sign-for-curl.mjs escribe un archivo de encabezados nuevo (modo 0600) por intento.
# El secreto se lee de PROVIBET_SECRET: nunca aparece en la línea de comandos.
umask 077
work="$(mktemp -d)"; trap 'rm -rf "$work"' EXIT
: > "$work/body"
url='https://api.provibet.com/api/b2b/v1/wallet'
node sign-for-curl.mjs GET "$url" "$work/body" "$work/headers"
curl --fail-with-body -sS "$url" --header "@$work/headers"

Los helpers completos están en Ejemplos.

Orden de validación

El servidor valida en este orden; el primer fallo determina la respuesta:

#VerificaciónSi falla
1Encabezados de autenticación no repetidos400 duplicate_authentication_header
2Formato de clave, timestamp, nonce y firma401 invalid_authentication_headers
3Timestamp dentro de ±300 s401 request_timestamp_outside_window
4Límite de 120 solicitudes por minuto por clave429 rate_limit_exceeded
5Clave activa401 invalid_client_credentials
6IP de origen autorizada403 source_ip_not_allowed
7Idempotency-Key válida en POST400 idempotency_key_required
8Firma401 invalid_request_signature
9Nonce no usado409 nonce_replayed_or_invalid
10Permiso del endpoint403 insufficient_scope

Permisos (scopes)

Pedí sólo los permisos que usa tu integración.

ScopeEndpoints
wallet:readGET /wallet
players:writePOST /players
players:readGET /players/{id}/balance
credits:writePOST /players/{id}/credits y POST /players/{id}/debits
games:readGET /games y GET /studios
games:launchPOST /games/{id}/launch
operations:readGET /operations/{id}

IP de salida

Cada integración tiene una lista de IP o redes autorizadas. Llamá desde direcciones fijas y avisá a Provibet antes de cambiarlas: una IP no registrada recibe 403 source_ip_not_allowed.

Si la firma no coincide

  1. Confirmá que usás la clave y el secreto activos, y segundos Unix (no milisegundos).
  2. Compará tu cadena canónica línea por línea: nueve líneas, path con prefijo /api/b2b/v1, query ordenado, clave idempotente exacta y hash de los bytes enviados.
  3. Verificá que ningún proxy o cliente HTTP de tu lado reescriba la ruta, el query o el body después de firmar.
  4. Generá timestamp, nonce y firma nuevos antes de volver a enviar.

Nunca registres el secreto ni la cadena canónica junto con el secreto. Para depurar, loguéa la cadena canónica sin el secreto y el X-Request-Id de la respuesta.


Primeros pasos

Idempotencia

Un intento puede repetirse; una entrega de fichas, no. La Idempotency-Key identifica la intención de negocio y el nonce identifica cada intento de transporte.

La regla

  • Idempotency-Key es obligatoria en todo POST y forma parte de la firma. Sin una clave válida la respuesta es 400 idempotency_key_required.
  • El nonce se renueva en cada intento. La clave idempotente se conserva mientras la intención sea la misma.
  • Generá la clave a partir de tu orden (por ejemplo credit-order-5001) y guardala antes de enviar.

Comportamiento por endpoint

EndpointQué hace la clave
POST /players/{id}/credits
POST /players/{id}/debits
Deduplica. La misma clave con la misma intención devuelve la misma operación con "replayed": true y su estado actual (200, 202 o 422), sin volver a mover fichas. La misma clave con otra intención responde 409 idempotency_conflict.
POST /playersSólo se exige y se firma. El alta ya es idempotente por externalPlayerId: repetirla devuelve el mismo jugador.
POST /games/{id}/launchSólo se exige y se firma. Cada llamada emite una URL de apertura nueva; usá una clave distinta por solicitud.

Qué se compara en créditos y débitos

La intención se identifica por tipo (crédito o débito), jugador e importe. "50" y "50.00" son el mismo importe, y omitir currency equivale a enviar "CRD". Aun así, conservá y reenviá los mismos bytes: simplifica la auditoría.

  • Las claves son únicas por cuenta: una clave usada en un crédito no sirve para un débito, ni para otro jugador u otro importe.
  • Las claves no vencen: quedan asociadas a su operación.
  • Si la solicitud se rechazó antes de crear la operación (por ejemplo 409 insufficient_credits o un error de firma), la clave no queda consumida y podés reintentar la misma intención.
  • Un reintento con una clave ya registrada responde aunque los movimientos nuevos estén pausados o el jugador ya no esté activo.

Cuándo y cómo reintentar

SituaciónAcción correcta
Timeout, conexión cortada o respuesta no JSONEl resultado es incierto. Reintentá con la misma clave e importe y timestamp, nonce y firma nuevos.
202 con operationGuardá operation.id y consultá GET /operations/{id} con backoff. No crees otra intención.
409 idempotency_conflictCompará con la intención guardada. No cambies la clave para forzar el movimiento.
409 nonce_replayed_or_invalidVolvé a firmar con un nonce nuevo; conservá la clave.
422 con state: REJECTEDRechazo definitivo. Una nueva intención necesita una decisión de negocio y otra clave.
429 o 503Esperá con backoff exponencial y jitter; reintentá con la misma clave.

Nunca reintentes con una clave nueva una entrega cuyo resultado no conocés. Si lo hacés, podés entregar las fichas dos veces. Consultá la operación o repetí la solicitud con la clave original.

Backoff sugerido

Para consultar una operación pendiente: 1, 2, 5 y 10 segundos; después cada 30 segundos con jitter, hasta el límite de tiempo de tu aplicación. Llegar a ese límite no cambia el estado financiero: la orden sigue pendiente.


Primeros pasos

Entornos y URLs

Direcciones públicas de Provibet y requisitos de transporte.

URLs

UsoURLAutenticación
API v1 (base)https://api.provibet.com/api/b2b/v1Firma HMAC
Página de aperturahttps://api.provibet.com/play/Ticket de un solo uso en la URL
Portadas del catálogohttps://api.provibet.com/media/{mediaId}Pública
Panel de gestiónhttps://admin.provibet.com/Equipo de Provibet
Documentaciónhttps://provibet.com/docs/Pública
Contrato OpenAPIhttps://provibet.com/docs/openapi.jsonPública

Pruebas y producción

No existe un entorno de pruebas público ni un alta automática. Provibet confirma el entorno, las credenciales, los límites y las condiciones de tu cuenta antes de que envíes tráfico con fichas. Separá credenciales, registros y referencias de prueba de los productivos.

Todos los IDs, claves, importes y URLs de esta documentación son ilustrativos. No sirven para autenticar ni para abrir juegos.

Transporte

  • Sólo HTTPS, contra el host exacto api.provibet.com. Otro host responde 421 unexpected_host.
  • Bodies JSON de hasta 64 KiB; más grandes responden 413 request_too_large.
  • Reloj sincronizado por NTP: la firma admite un desvío máximo de 300 segundos.
  • Las respuestas de la API llevan Cache-Control: no-store. Las portadas se pueden cachear (public, max-age=86400, immutable).
  • No sigas redirecciones automáticamente en llamadas firmadas.

Integración

Billetera prepaga

Tu cuenta tiene una billetera de fichas CRD. De ahí salen las fichas que entregás a tus jugadores y ahí vuelven las que descontás.

Consultar la billetera

GET/api/b2b/v1/wallet
Scope wallet:readSin bodySin Idempotency-Key
cURL
provibet GET /api/b2b/v1/wallet
Node.js
const { status, data } = await provibetRequest('GET', '/api/b2b/v1/wallet');
PHP
$r = provibetRequest('GET', '/api/b2b/v1/wallet');
200 · Respuesta
{
  "wallet": {
    "currency": "CRD",
    "availableCredits": "9500.00",
    "reservedCredits": "500.00",
    "version": "14"
  }
}
CampoDescripción
currencystringSiempre CRD.
availableCreditsstring decimalFichas disponibles para nuevas entregas.
reservedCreditsstring decimalFichas apartadas por entregas pendientes o inciertas. No están disponibles.
versionstring enteroAumenta con cada movimiento de la billetera. Sirve para detectar cambios.

Cómo se mueve el saldo

EventoDisponiblesReservadas
Asignación de fichas desde el panel+ importe—
Entrega a un jugador: se crea la operación (RESERVED)− importe+ importe
Entrega COMPLETED—− importe (sale hacia el jugador)
Entrega REJECTED+ importe (se libera)− importe
Entrega UNKNOWN—Sigue reservada hasta la conciliación
Descuento COMPLETED+ importe (vuelve del jugador)—
Descuento RESERVED, UNKNOWN o REJECTEDSin cambiosSin cambios

Saldo insuficiente

Si availableCredits no alcanza para una entrega, la respuesta es 409 insufficient_credits y no se crea ninguna operación. La clave idempotente no queda consumida: cuando Provibet cargue fichas, podés reintentar la misma intención con la misma clave.

No hay un endpoint de compra de fichas: las cargas las registra Provibet en el panel, con una referencia única por asignación.


Integración

Jugadores

Cada jugador se identifica con un externalPlayerId de tu sistema. Provibet no entrega usuarios ni contraseñas de jugadores.

Crear o recuperar un jugador

POST/api/b2b/v1/players
Scope players:writeIdempotency-Key obligatoria
CampoRegla
externalPlayerId obligatoriostring3 a 128 caracteres; empieza con letra o dígito y admite A-Z a-z 0-9 . _ : @ -. Tiene que ser estable: es la identidad del jugador en tu cuenta.
displayName opcionalstringHasta 100 caracteres. Se acepta pero hoy no se guarda ni se devuelve.
cURL
provibet POST /api/b2b/v1/players '{"externalPlayerId":"jugador-123"}' player-jugador-123-0001
Node.js
const body = JSON.stringify({ externalPlayerId: 'jugador-123' });
const { status, data } = await provibetRequest('POST', '/api/b2b/v1/players', body, 'player-jugador-123-0001');
PHP
$body = json_encode(['externalPlayerId' => 'jugador-123'], JSON_THROW_ON_ERROR);
$r = provibetRequest('POST', '/api/b2b/v1/players', $body, 'player-jugador-123-0001');
201 · Respuesta (nuevo o existente)
{
  "player": {
    "externalPlayerId": "jugador-123"
  }
}

La respuesta es 201 tanto para un jugador nuevo como para uno ya registrado. No cambies el externalPlayerId al repetir la solicitud.

RespuestaSignificado
400 invalid_requestID con formato inválido o campos no documentados.
409 player_initialization_pendingEl alta de ese jugador está en curso. Reintentá en unos segundos.
409 player_not_ready · 503 player_initialization_reviewEl alta necesita revisión de Provibet. Contactá a soporte con el X-Request-Id.
503 registration_unavailableEl registro de jugadores está pausado.

Consultar el saldo

GET/api/b2b/v1/players/{externalPlayerId}/balance
Scope players:readSin body
cURL
provibet GET /api/b2b/v1/players/jugador-123/balance
Node.js
const id = encodeURIComponent('jugador-123');
const { data } = await provibetRequest('GET', `/api/b2b/v1/players/${id}/balance`);
PHP
$id = rawurlencode('jugador-123');
$r = provibetRequest('GET', "/api/b2b/v1/players/{$id}/balance");
200 · Respuesta
{
  "player": {
    "externalPlayerId": "jugador-123",
    "balanceCredits": "250.00",
    "currency": "CRD"
  }
}
  • El saldo se lee en el momento: incluye las partidas jugadas y puede cambiar mientras el jugador está en un juego.
  • Codificá el ID como segmento de URL y firmá ese mismo path codificado.
  • Un jugador inexistente, inactivo o de otra cuenta responde 404 player_not_found.

Integración

Fichas: créditos y débitos

Las fichas pasan de tu billetera al jugador con un crédito y vuelven con un débito. Cada movimiento es una operación idempotente con un estado explícito.

Modelo de transferencia

Provibet trabaja en modo transferencia: movés fichas antes y después de jugar, y durante la partida el saldo del jugador cambia dentro de la plataforma de juego. No hay callbacks por apuesta ni por premio.

Estados de una operación

Máquina de estados de una operación RESERVED pasa a COMPLETED, REJECTED o UNKNOWN. UNKNOWN pasa a COMPLETED por conciliación. COMPLETED y REJECTED son terminales. 200 · confirmada 422 · rechazada 202 · resultado incierto conciliación RESERVEDintención registrada COMPLETEDterminal REJECTEDterminal UNKNOWNrequiere seguimiento
UNKNOWN nunca se libera automáticamente: sólo pasa a COMPLETED cuando la conciliación confirma el movimiento.
EstadoHTTPQué significaQué hacés
COMPLETED200Movimiento confirmado.Cerrá la orden como exitosa. Terminal.
RESERVED202Intención registrada, procesamiento en curso (por ejemplo, un reintento mientras el original sigue en proceso).Consultá la operación con backoff.
UNKNOWN202No hay certeza del resultado. En un crédito, las fichas siguen reservadas.Mantené la orden pendiente y consultá la operación. No acredites ni liberes por tu cuenta.
REJECTED422Rechazo definitivo; no hubo movimiento (en un crédito, la reserva se liberó).Registrá el rechazo. Terminal.

Las respuestas 200, 202 y 422 traen el mismo formato: {"operation": {…}, "replayed": false}. El 422 no usa el formato de error: leé operation.state.

Entregar fichas (crédito)

POST/api/b2b/v1/players/{externalPlayerId}/credits
Scope credits:writeIdempotency-Key obligatoriaGuardar antes de enviar

Reserva el importe de tu billetera y lo transfiere al jugador, que tiene que estar registrado y activo.

CampoRegla
amountCredits obligatoriostringDecimal mayor que cero con hasta dos posiciones; máximo 10000000000.00. Ver Moneda.
currency opcionalstringSi se envía, tiene que ser CRD.
cURL
provibet POST /api/b2b/v1/players/jugador-123/credits '{"amountCredits":"50","currency":"CRD"}' credit-order-5001
Node.js
// 1) Persistí la intención: { orderId, playerId, body, key } ANTES de enviar.
const body = JSON.stringify({ amountCredits: '50', currency: 'CRD' });
const key = 'credit-order-5001';
// 2) Enviá. Ante un timeout, repetí con el MISMO body y la MISMA clave.
const { status, data } = await provibetRequest('POST', '/api/b2b/v1/players/jugador-123/credits', body, key);
// 3) Guardá data.operation.id y decidí según data.operation.state.
PHP
// 1) Persistí la intención ($orderId, $playerId, $body, $key) ANTES de enviar.
$body = json_encode(['amountCredits' => '50', 'currency' => 'CRD'], JSON_THROW_ON_ERROR);
$key = 'credit-order-5001';
// 2) Enviá. Ante un timeout, repetí con el MISMO body y la MISMA clave.
$r = provibetRequest('POST', '/api/b2b/v1/players/jugador-123/credits', $body, $key);
// 3) Guardá $r['data']['operation']['id'] y decidí según su 'state'.
200 · Entrega confirmada
{
  "operation": {
    "id": "00000000-0000-4000-8000-000000000001",
    "kind": "CREDIT",
    "state": "COMPLETED",
    "amountCredits": "50.00",
    "currency": "CRD",
    "createdAt": "2026-09-24T12:00:00.000Z",
    "updatedAt": "2026-09-24T12:00:01.000Z"
  },
  "replayed": false
}
HTTPInterpretación
200COMPLETED. Si es un reintento de la misma intención, replayed es true.
202RESERVED o UNKNOWN. Las fichas siguen reservadas. Consultá la operación; no abras otra intención.
422REJECTED: la reserva se liberó.
409insufficient_credits (sin operación creada), idempotency_conflict o nonce_replayed_or_invalid.
404player_not_found: jugador inexistente o inactivo.
503financial_operations_disabled o service_temporarily_unavailable: no se creó la operación.

Si la transferencia al jugador falla después de reservar, la operación queda UNKNOWN y la reserva se mantiene hasta que la conciliación confirme el resultado. Un corte de conexión no equivale a un rechazo.

Descontar fichas (débito)

POST/api/b2b/v1/players/{externalPlayerId}/debits
Scope credits:writeIdempotency-Key obligatoriaGuardar antes de enviar

Devuelve a tu billetera fichas del saldo del jugador, por ejemplo cuando cierra su sesión o retira. El body, los límites y las reglas de reintento son los del crédito; operation.kind vale DEBIT.

cURL
provibet POST /api/b2b/v1/players/jugador-123/debits '{"amountCredits":"25.50","currency":"CRD"}' debit-order-7001
Node.js
const body = JSON.stringify({ amountCredits: '25.50', currency: 'CRD' });
const { status, data } = await provibetRequest('POST', '/api/b2b/v1/players/jugador-123/debits', body, 'debit-order-7001');
PHP
$body = json_encode(['amountCredits' => '25.50', 'currency' => 'CRD'], JSON_THROW_ON_ERROR);
$r = provibetRequest('POST', '/api/b2b/v1/players/jugador-123/debits', $body, 'debit-order-7001');
422 · Descuento rechazado
{
  "operation": {
    "id": "00000000-0000-4000-8000-000000000003",
    "kind": "DEBIT",
    "state": "REJECTED",
    "amountCredits": "25.50",
    "currency": "CRD",
    "createdAt": "2026-09-24T12:00:00.000Z",
    "updatedAt": "2026-09-24T12:00:00.400Z"
  },
  "replayed": false
}
HTTPInterpretación
200COMPLETED: las fichas ya suman en availableCredits.
202RESERVED o UNKNOWN: tu billetera todavía no cambió. Consultá la operación; no abras otra intención.
422REJECTED: el saldo del jugador no alcanzaba o el descuento no llegó a enviarse. La billetera no cambió.
409idempotency_conflict (por ejemplo, una clave ya usada en un crédito) o nonce_replayed_or_invalid.

Sin reserva previa. El débito no aparta fichas: se acreditan en tu billetera recién al confirmarse. Si el jugador está en una partida su saldo puede cambiar; descontá cuando la partida terminó, por ejemplo al recibir el aviso de salida.

Resultado incierto: qué hacer

  1. Mantené la orden como pendiente en tu sistema. No acredites, no devuelvas y no muestres el movimiento como fallido.
  2. Si no recibiste respuesta, repetí la solicitud con la misma clave: vas a obtener la operación original.
  3. Con el operation.id, consultá GET /operations/{id} con backoff.
  4. Si sigue en UNKNOWN, contactá a Provibet con el operation.id: la conciliación consulta el resultado real y nunca reenvía la transferencia.

Nunca reintentes con una clave nueva ni compenses con otra operación: podés duplicar el movimiento. Consultá /operations/{id} y esperá un estado terminal.



Integración

Estudios y categorías

Las facetas del catálogo para armar los filtros de tu lobby: estudios con juegos habilitados y cantidad de juegos por categoría.

Consultar facetas

GET/api/b2b/v1/studios
Scope games:readSin parámetros
cURL
provibet GET /api/b2b/v1/studios
Node.js
const { data } = await provibetRequest('GET', '/api/b2b/v1/studios');
PHP
$r = provibetRequest('GET', '/api/b2b/v1/studios');
200 · Respuesta (recortada)
{
  "studios": [
    { "id": "pragmaticplay", "name": "Pragmatic Play", "games": 614 },
    { "id": "amusnet", "name": "Amusnet", "games": 347 },
    { "id": "playngo", "name": "Play'n GO", "games": 317 }
  ],
  "categories": [
    { "id": "slots", "games": 3222 },
    { "id": "live", "games": 211 },
    { "id": "table", "games": 7 },
    { "id": "crash", "games": 2 },
    { "id": "sports", "games": 1 }
  ]
}
  • Ambas listas vienen ordenadas por cantidad de juegos, de mayor a menor.
  • Usá studios[].id como valor de studio en GET /games, y categories[].id como valor de category.
  • Los estudios sin nombre informado no se listan.

La clave del estudio

studioId se deriva del nombre público: se quitan acentos, se pasa a minúsculas y se conservan sólo letras y dígitos (hasta 60 caracteres). Por eso studio=Play'n GO y studio=playngo filtran lo mismo.

Nombre públicostudioId
Pragmatic Play Livepragmaticplaylive
Play'n GOplayngo
Hacksaw Gaminghacksawgaming

La lista completa, con la cantidad de juegos de cada estudio, está en el apéndice Estudios.


Integración

Apertura de juegos

Tu backend pide la apertura y recibe una URL temporal de un solo uso. El jugador la abre en una pestaña o dentro de tu sitio, y cuando sale del juego tu página recibe un aviso.

Solicitar la apertura

POST/api/b2b/v1/games/{gameId}/launch
Scope games:launchIdempotency-Key obligatoria
CampoRegla
externalPlayerId obligatoriostringJugador registrado y activo en tu cuenta.
locale opcionalstringIdioma preferido, por ejemplo es, es-AR o en-US. Por defecto es. Ver Idiomas.
device opcionalstringmobile o desktop. Se valida, pero hoy no cambia la apertura: el juego se adapta solo al dispositivo.
cURL
provibet POST "/api/b2b/v1/games/$GAME_ID/launch" '{"externalPlayerId":"jugador-123","locale":"es-AR"}' "launch-$(date +%s)-$RANDOM"
Node.js
import { randomUUID } from 'node:crypto';

const body = JSON.stringify({ externalPlayerId: 'jugador-123', locale: 'es-AR' });
const { status, data } = await provibetRequest('POST', `/api/b2b/v1/games/${gameId}/launch`, body, `launch-${randomUUID()}`);
if (status === 200) sendToPlayer(data.launch.url); // sólo al navegador de ese jugador
PHP
$body = json_encode(['externalPlayerId' => 'jugador-123', 'locale' => 'es-AR'], JSON_THROW_ON_ERROR);
$r = provibetRequest('POST', "/api/b2b/v1/games/{$gameId}/launch", $body, 'launch-' . bin2hex(random_bytes(12)));
$url = $r['data']['launch']['url']; // sólo al navegador de ese jugador
200 · Respuesta
{
  "launch": {
    "gameId": "game_0123456789abcdef0123456789abcdef01234567",
    "url": "https://api.provibet.com/play/#tenant=CUENTA_OPACA&ticket=TICKET_TEMPORAL",
    "expiresAt": "2026-09-24T12:03:00.000Z"
  }
}
  • La URL vence en expiresAt (hoy, 3 minutos después de emitida) y se canjea una sola vez. Cada solicitud genera una URL nueva.
  • Es una credencial temporal: no la publiques, no la registres completa y no la compartas entre jugadores. Si venció o ya se usó, pedí otra apertura.
  • Abrir un juego no mueve fichas: el jugador juega con el saldo que le entregaste.
RespuestaSignificado
404 game_not_foundEl juego no existe o ya no está habilitado. Actualizá tu catálogo.
404 player_not_foundJugador inexistente o inactivo.
409 player_not_readyEl alta del jugador no terminó o necesita revisión.
503 game_launch_unavailable · game_launch_disabledLa apertura de juegos está pausada. Informá al jugador y reintentá más tarde.
503 service_temporarily_unavailableServicio no disponible por el momento. Reintentá con backoff.

Juego dentro de tu sitio

Podés abrir launch.url en una pestaña nueva o embeberla en un iframe de tu lobby:

HTML
<iframe
  src="LAUNCH_URL"
  title="Juego"
  allow="fullscreen; autoplay"
  allowfullscreen
  referrerpolicy="no-referrer"></iframe>

El iframe sólo funciona desde orígenes registrados por Provibet. La página de apertura indica al navegador qué sitios pueden embeberla; desde cualquier otro origen el navegador bloquea el iframe. Pedí el alta de cada origen exacto (por ejemplo https://www.tucasino.com, sólo HTTPS) antes de publicar.

La página de apertura es neutra, sin marcas: quita el ticket de la barra de direcciones, lo canjea y muestra el juego a pantalla completa dentro de sí misma. Si el ticket venció o ya se usó, muestra «El enlace de juego expiró o no es válido. Volvé a abrir el juego desde tu casino.».

Aviso de salida del juego

Cuando el jugador sale del juego (por ejemplo, con el botón de volver al lobby del juego), la página de salida de Provibet envía a la ventana que lo contiene este mensaje con postMessage:

Mensaje
{ "type": "provibet:game-exit", "version": 1 }

El mensaje no lleva identificadores, tokens ni saldos. Llega desde el origen https://api.provibet.com: verificalo antes de actuar.

JavaScript · en la página que contiene el iframe
window.addEventListener('message', event => {
  if (event.origin !== 'https://api.provibet.com') return;
  const msg = event.data;
  if (!msg || msg.type !== 'provibet:game-exit' || msg.version !== 1) return;
  closeGameFrame();        // quitá el iframe y volvé al lobby
  refreshPlayerBalance();  // tu backend: GET /players/{id}/balance y, si corresponde, un débito
});

Si el juego se abrió en una pestaña propia, el jugador ve «Juego finalizado. Podés cerrar esta ventana y volver al casino.».

Contrato interno de la página de apertura

Tu integración no necesita llamar a estas rutas: las usa la página /play/.

  • POST https://api.provibet.com/session/exchange con {"tenantId":"…","ticket":"…"}. Requiere el encabezado Origin: https://api.provibet.com (si no, 403 origin_not_allowed); no usa HMAC. Responde {"session":{"id","expiresAt"},"playUrl":"/session/play/{id}","target":"…"} y establece una cookie de sesión Secure, HttpOnly, SameSite=Lax limitada al host, válida por una hora.
  • GET https://api.provibet.com/session/play/{sessionId} responde HTML con el juego para esa cookie de sesión.
  • Provibet puede revocar una sesión desde el panel. La revocación impide nuevos accesos a la sesión; no garantiza cortar un juego ya cargado en el navegador.

Integración

Operaciones

Cada crédito o débito crea una operación con un UUID. Consultala para conocer su estado real, sobre todo después de un 202 o de un corte de conexión.

Consultar una operación

GET/api/b2b/v1/operations/{operationId}
Scope operations:readoperationId: UUID
cURL
provibet GET /api/b2b/v1/operations/00000000-0000-4000-8000-000000000001
Node.js
const { data } = await provibetRequest('GET', `/api/b2b/v1/operations/${operationId}`);
if (data.operation.state === 'COMPLETED') markOrderPaid();
PHP
$r = provibetRequest('GET', '/api/b2b/v1/operations/' . $operationId);
$state = $r['data']['operation']['state'];
200 · Respuesta
{
  "operation": {
    "id": "00000000-0000-4000-8000-000000000001",
    "kind": "CREDIT",
    "state": "UNKNOWN",
    "amountCredits": "50.00",
    "currency": "CRD",
    "createdAt": "2026-09-24T12:00:00.000Z",
    "updatedAt": "2026-09-24T12:00:31.000Z"
  }
}
CampoDescripción
idUUID de la operación.
kindCREDIT: de tu billetera al jugador. DEBIT: del jugador a tu billetera.
stateRESERVED, COMPLETED, REJECTED o UNKNOWN. Ver Estados.
amountCredits · currencyImporte con dos decimales y unidad CRD.
createdAt · updatedAtFechas ISO 8601 en UTC.
  • Una consulta exitosa responde 200 aunque la operación siga pendiente: el resultado financiero está en operation.state.
  • Sólo ves operaciones de tu cuenta; otra responde 404 operation_not_found. Usá exactamente el UUID recibido.
  • No hay listado ni búsqueda por referencia: guardá el operation.id de cada respuesta junto a tu orden.

Seguimiento recomendado

  1. Persistí operation.id apenas lo recibas.
  2. Consultá con backoff: 1, 2, 5 y 10 segundos; luego cada 30 segundos con jitter.
  3. Cerrá la orden sólo con un estado terminal: COMPLETED o REJECTED.
  4. Si una operación sigue en UNKNOWN, escalala a Provibet con su operation.id para la conciliación.

Integración

Errores

Los errores usan un código estable en inglés y un identificador de correlación. Tu código debe decidir por error.code, no por el texto.

Formato

Error
{
  "error": {
    "code": "invalid_request_signature",
    "requestId": "5d1f0b7e-6c1a-4f1e-9a53-0e7b2b1c9d10"
  }
}
  • Todas las respuestas incluyen el encabezado X-Request-Id. Una ruta inexistente responde 404 not_found y puede omitir requestId en el body: usá el encabezado.
  • Los rechazos de créditos y débitos (422) no usan este formato: devuelven operation con state: REJECTED.
  • Ante una respuesta HTML o vacía (por ejemplo, de un intermediario de red), no la interpretes como éxito ni como rechazo.

Códigos

HTTPCódigosQué hacer
400invalid_request, invalid_json, invalid_amount, invalid_pagination, invalid_cover_filter, idempotency_key_required, duplicate_authentication_header, invalid_request_pathCorregí el formato o el campo. No repitas sin cambios.
401invalid_authentication_headers, request_timestamp_outside_window, invalid_client_credentials, invalid_request_signatureRevisá clave activa, reloj, path, query y bytes firmados.
403source_ip_not_allowed, source_ip_unavailable, insufficient_scopeRevisá la IP de salida y los permisos con Provibet.
404player_not_found, game_not_found, operation_not_found, not_foundVerificá la ruta y el identificador dentro de tu cuenta.
409nonce_replayed_or_invalid, idempotency_conflict, insufficient_credits, player_not_ready, player_initialization_pendingDistinguí nonce, intención y saldo antes de actuar.
413request_too_largeEl body no puede superar 64 KiB.
421unexpected_hostLlamá a api.provibet.com exactamente.
422operation.state = REJECTEDRegistrá el rechazo del crédito o débito.
429rate_limit_exceededBackoff exponencial con jitter. Ver Límite de solicitudes.
500service_unavailableGuardá el requestId. En un crédito o débito, tratá el resultado como incierto.
503service_temporarily_unavailable, financial_operations_disabled, registration_unavailable, game_launch_unavailable, game_launch_disabled, player_initialization_review, service_busyEsperá y reintentá con backoff; conservá la intención si había fichas involucradas.

Diagnóstico rápido

401 invalid_request_signature

Compará tu cadena canónica con la de Autenticación: nueve líneas, path con prefijo, query ordenado y codificado, clave idempotente exacta y hash de los bytes enviados. Verificá que el Content-Type sea application/json.

401 request_timestamp_outside_window

Tu reloj está desfasado más de 300 segundos o estás enviando milisegundos. Sincronizá por NTP y usá segundos Unix de 10 dígitos.

409 nonce_replayed_or_invalid

Reutilizaste un nonce o reenviaste encabezados ya firmados. Generá timestamp, nonce y firma nuevos en cada intento.

Timeout o conexión cortada en un crédito o débito

El resultado es incierto. Repetí la solicitud con la misma clave idempotente y el mismo importe, o consultá la operación si ya tenés su UUID. Ver Resultado incierto.


Integración

Límite de solicitudes

La API limita la frecuencia por clave pública y por IP de origen para proteger el servicio de todos los operadores.

Cuotas

ÁmbitoLímiteAlcance
Por clave pública (X-B2B-Key)120 / minutoTodas las solicitudes a /api/b2b/v1 con esa clave, incluidas las rechazadas por firma o permisos.
Por IP de origen360 / minutoTodo el tráfico de esa IP hacia api.provibet.com, incluida la apertura (/play/ y /session/exchange). Las portadas no cuentan para esta cuota.
Portadas, por IP de origen2.400 / minutoSolicitudes GET o HEAD a https://api.provibet.com/media/{mediaId}. Es una cuota aparte: un lobby con muchas portadas no bloquea la apertura de juegos.

Las ventanas son fijas de 60 segundos: cada una empieza con la primera solicitud y se reinicia al vencer.

Si recibís 429

  • La respuesta es 429 rate_limit_exceeded y no incluye Retry-After.
  • Esperá con backoff exponencial y jitter (por ejemplo 2, 4, 8 segundos…) y reducí la concurrencia. La ventana se libera, como máximo, 60 segundos después de abrirse.
  • En un crédito o débito, un 429 significa que la solicitud no se procesó: reintentá con la misma clave idempotente.
  • Con carga excepcional, la API puede responder 503 service_busy: tratala igual.

Buenas prácticas

  • Sincronizá el catálogo y los estudios en tu base; no los pidas en cada visita.
  • Pedí páginas de 200 juegos para reducir llamadas.
  • Consultá saldos cuando los necesites (al abrir y al cerrar un juego), no en intervalos cortos.
  • Consultá operaciones pendientes con el backoff sugerido.
  • Si descargás portadas desde tu servidor, cachealas: esas descargas cuentan para la cuota de portadas de tu IP.

Otros límites

LímiteValor
Tamaño del body JSON64 KiB
Juegos por página1 a 200 (por defecto 50)
offset máximo1.000.000
Búsqueda q100 caracteres
Importe por solicitud10000000000.00 CRD
Vigencia de la URL de apertura3 minutos, un solo uso
Desvío de reloj admitido±300 segundos

Próximamente

Carreras de caballos En preparación

Una nueva vertical de apuestas a carreras de caballos para sumar a tu plataforma, integrada al mismo saldo de fichas de tus jugadores.

Todavía no está disponible. Esta página describe el concepto. No hay endpoints de carreras en la API v1 y nada de lo que integres hoy cambia por esta vertical. El contrato de API se va a publicar en esta documentación antes de su activación.

Cómo va a funcionar

  • Mismo jugador, mismo saldo: el jugador usa las fichas que ya le entregaste; no vas a necesitar una segunda billetera ni un segundo registro.
  • Movimiento automático al abrir: cuando el jugador abre las carreras, su saldo pasa automáticamente a la billetera de carreras.
  • Regreso automático al cerrar: cuando sale, el saldo vuelve a su saldo de casino.
  • Apertura como un juego más: se va a abrir con una URL temporal, igual que hoy la apertura de juegos.

Qué esperar

Antes de activar la vertical vamos a publicar el contrato completo: endpoints, estados de los movimientos entre billeteras, errores y reglas de conciliación. Si te interesa sumarla, avisale a tu contacto de Provibet.


Apéndice

Estudios

Los 27 estudios del catálogo, con su clave de filtro y cantidad de juegos: 3.443 juegos en total al 24/09/2026.

Es una foto del catálogo a esa fecha. Los juegos habilitados pueden variar: la lista vigente para tu cuenta se obtiene con GET /studios.

#EstudiostudioIdJuegosPor categoría
1Pragmatic Playpragmaticplay614slots 612 · table 2
2Amusnetamusnet347slots 347
3Play'n GOplayngo317slots 317
4Reel Kingdomreelkingdom216slots 216
5Pragmatic Play Livepragmaticplaylive212live 211 · crash 1
6EGT Digitalegtdigital163slots 163
7Joker Gamingjokergaming155slots 155
8CQ9cq9145slots 145
9PG Softpgsoft134slots 133 · table 1
10Amaticamatic132slots 132
11Hacksaw Gaminghacksawgaming130slots 130
12Habanerohabanero122slots 122
13Ruby Playrubyplay122slots 122
14Royalgamingroyalgaming81slots 81
15DreamTechdreamtech76slots 76
16FastSpinfastspin69slots 68 · table 1
17Spadegamingspadegaming62slots 62
18NextSpinnextspin58slots 58
19Booongobooongo55slots 55
20Fa Chaifachai51slots 49 · table 2
21Evoplayevoplay49slots 49
22Playsonplayson48slots 48
23Fat Pandafatpanda33slots 33
24TopTrend Gamingtoptrendgaming33slots 33
25Fish Hunterfishhunter9slots 9
26Spribespribe9slots 7 · crash 1 · table 1
27Sportsbooksportsbook1sports 1
Total3.44368 con portada animada oficial

Apéndice

Categorías

Cada juego pertenece a una de cinco categorías de lobby. Son los valores válidos del filtro category.

categoryNombre sugeridoQué incluyeJuegos al 24/09/2026
slotsTragamonedasSlots de video y clásicos, incluidos juegos de pesca y arcade.3.222
liveCasino en vivoMesas con crupier en vivo.211
tableJuegos de mesaRuleta, blackjack, baccarat, póker y similares, no en vivo.7
crashCrashJuegos de multiplicador creciente.2
sportsDeportesApuestas deportivas.1

Los nombres sugeridos son orientativos: mostralos como prefieras en tu lobby, pero enviá siempre el valor de category tal cual. La cantidad vigente se consulta con GET /studios.


Apéndice

Idiomas

El idioma se elige al abrir un juego, con el campo opcional locale de POST /games/{id}/launch.

Formato aceptado

Dos letras en minúscula (código de idioma) y, opcionalmente, un guion y dos letras en mayúscula (región): ^[a-z]{2}(-[A-Z]{2})?$. Si no se envía, se usa es.

VálidosInválidos (400 invalid_request)
es, es-AR, es-MXES, es-ar, es_AR
en, en-USeng, en-USA
pt, pt-BRpt-br, zh-Hans

Qué idiomas ve el jugador

  • El valor se entrega al juego como preferencia. Cada estudio soporta su propio conjunto de idiomas; si no admite el pedido, el juego se muestra en su idioma por defecto.
  • La página de apertura y sus mensajes están en español.
  • Los códigos de error de la API son identificadores estables en inglés (por ejemplo player_not_found): traducilos en tu interfaz.

Apéndice

Moneda

CRD es la unidad de fichas de Provibet. No es una moneda de curso legal y la API no convierte importes: las condiciones comerciales de las fichas se acuerdan fuera de la API.

Importes que enviás

amountCredits es un string decimal:

  • Parte entera sin ceros a la izquierda (salvo 0) y, opcionalmente, un punto con una o dos posiciones decimales.
  • Mayor que cero y hasta 10000000000.00.
  • Sin signo, sin separador de miles, sin coma decimal, sin exponentes y nunca como número JSON.
  • currency es opcional; si lo enviás, tiene que ser "CRD".
VálidosInválidos (400 invalid_request)
"50", "50.5", "50.25"50 (número), "50.255", "050"
"0.01", "0.5""0", "0.00", "-5", "+5"
"10000000000.00""1,5", "1.000" como miles, "1e3", "10000000000.01"

Importes que recibís

Las respuestas usan siempre dos decimales: availableCredits, reservedCredits, balanceCredits y amountCredits tienen el formato ^(0|[1-9][0-9]*)\.[0-9]{2}$, por ejemplo "0.00" o "9500.00".

Precisión

Internamente cada importe es un entero de centésimos. Guardalo como decimal exacto o como entero de centésimos en tu base, y no lo conviertas a punto flotante.

Node.js
// String CRD → centésimos (BigInt) y vuelta, sin punto flotante.
const toCents = s => { const [i, f = ''] = s.split('.'); return BigInt(i) * 100n + BigInt(f.padEnd(2, '0')); };
const toCrd = c => `${c / 100n}.${String(c % 100n).padStart(2, '0')}`;
toCrd(toCents('50.5') + toCents('0.75')); // "51.25"
PHP
// Con BCMath: strings decimales exactos.
$total = bcadd('50.5', '0.75', 2); // "51.25"

Apéndice

Referencia OpenAPI

El contrato completo de la API v1 en formato OpenAPI 3.1: rutas, parámetros, esquemas, ejemplos y respuestas de error.

Qué incluye

  • Los nueve endpoints de /api/b2b/v1, con su permiso requerido en x-required-scope.
  • Las rutas del navegador: /play/, /media/{mediaId}, /session/exchange y /session/play/{sessionId}.
  • Esquemas de billetera, jugadores, operaciones, juegos, estudios y errores, con los códigos de cada respuesta.

Podés importarlo en tu cliente HTTP o generador de código preferido. La firma HMAC no se puede expresar en OpenAPI: implementala según Autenticación y firma o usá los helpers.

Cambios

FechaCambio
26/09/2026Catálogo: campo nuevo source en cada juego (primary o partner). Es aditivo: los demás campos, el orden y los filtros no cambian.
25/09/2026Catálogo: campo nuevo coverAnimatedSource (official, generated o null). coverAnimatedUrl entrega una animación generada por Provibet cuando el juego no tiene portada animada oficial. El orden y el filtro cover=animated siguen considerando sólo las oficiales. Las portadas (/media/{mediaId}) tienen su propia cuota por IP de origen (2.400 por minuto) y ya no descuentan de las 360 por minuto que protegen la apertura.
24/09/2026Nueva documentación. OpenAPI alineado con la implementación: límites de frecuencia y ventana de ±300 s explícitos, códigos de error reales, sin respuestas 415/502, sin mobileSupported en juegos, ruta pública /media/{mediaId} y descripciones de estudios, categorías e idiomas.
23/09/2026v1: autenticación HMAC, billetera, jugadores, créditos y débitos, catálogo con estudios y categorías, apertura embebible y operaciones.

Apéndice

Ejemplos

Helpers sin dependencias externas para firmar y enviar solicitudes desde tu backend. No hacen reintentos financieros automáticos: tu aplicación decide según el estado.

Los tres leen PROVIBET_PUBLIC_KEY, PROVIBET_SECRET y, opcionalmente, PROVIBET_ORIGIN (por defecto https://api.provibet.com) del entorno.

Node.js

Node.js 20 o superior. provibetRequest(method, path, rawBody, idempotencyKey) devuelve status, requestId, retryAfter y data. signRequest() expone la firma por separado.

examples/node.mjs
Cargando el helper… Si no aparece, usá el enlace de descarga.

PHP

PHP 8.1 o superior, con las extensiones cURL e iconv. Descargalo como request.php e incluilo sólo desde tu backend.

examples/request.php.txt
Cargando el helper… Si no aparece, usá el enlace de descarga.

cURL

Linux o macOS con Node.js 20+ y cURL. sign-for-curl.mjs importa node.mjs: descargá ambos en la misma carpeta. Crea un archivo de encabezados nuevo por intento; el secreto nunca se pasa como argumento.

examples/sign-for-curl.mjs
Cargando el helper… Si no aparece, usá el enlace de descarga.
bash · crédito con cURL
umask 077
work="$(mktemp -d)"; trap 'rm -rf "$work"' EXIT
printf '%s' '{"amountCredits":"50","currency":"CRD"}' > "$work/body"   # guardá estos bytes y la clave
url='https://api.provibet.com/api/b2b/v1/players/jugador-123/credits'
node sign-for-curl.mjs POST "$url" "$work/body" "$work/headers" credit-order-5001
curl -sS -X POST "$url" --header "@$work/headers" --data-binary "@$work/body"

No reutilices un archivo de encabezados: cada intento necesita timestamp, nonce y firma nuevos. Un error de cURL no demuestra que la entrega se haya rechazado.

Provibet · API de Casino v1 · Esta documentación no contiene credenciales.