# 10 / AI agent integration guide

A complete workflow for an agent implementing the integration in a partner system. Read this guide alongside the current OpenAPI; do not infer endpoints from examples or confuse console features with public APIs. Contract verified on 2026-10-18: Integration API v1.1.0, SDK v1.

## Sources the agent must read

Start with the Markdown guide, consult OpenAPI for exact schemas, and use the aggregator, appearance and iframe sections for complete examples. The files below are public, require no authentication and are readable without executing JavaScript.

- [Markdown · Português](https://orbiprediction.com/integration-agent.pt-BR.md)
- [Markdown · English](https://orbiprediction.com/integration-agent.md)
- [Markdown · Español](https://orbiprediction.com/integration-agent.es.md)
- [OpenAPI 3.0](https://orbiprediction.com/openapi.json)
- [llms.txt](https://orbiprediction.com/llms.txt)
- [Aggregator API](https://orbiprediction.com/docs#aggregators)
- [Appearance API](https://orbiprediction.com/docs#appearance)
- [Iframe & SDK](https://orbiprediction.com/docs#iframe)

## 1. Scope and required inputs

- This version is simulation only, with fictitious credits and real public data. Do not implement deposits, withdrawals, wallet callbacks, real-money bets, Polymarket orders or financial settlement. The public feed requires no Polymarket credentials.

- Before editing code, locate the backend, player authentication, player-to-casino membership, deployment pipeline, persistence and frontend CSP. Confirm the direct or aggregator model, exact origins, stable IDs, language and branding.

- Use references to secrets already configured in the environment; do not ask for keys in chats or commits. Without a credential or authorized origin, implement and test locally with mocked responses and record the missing prerequisite. Do not invent credentials, permissions or a staging environment: this guide publishes only the production URL.

```
ORBI_BASE_URL=https://orbiprediction.com
# Server-only environment variables; use your secret manager.
# Direct client:
ORBI_CLIENT_API_KEY=<secret-reference>
# Aggregator: separate deployment and runtime credentials.
ORBI_PROVISION_API_KEY=<secret-reference:clients:read,clients:write>
ORBI_LAUNCH_API_KEY=<secret-reference:launch:write>
# A direct client can use one configured origin. Aggregators resolve
# the verified origin per casino from their trusted database.
PUBLIC_SITE_ORIGIN=https://casino.example
```

## 2. Choose one credential model

- Direct client: use orb_sk_ on the server. The platform creates the client and its access; the backoffice configures origins, branding and keys. GET /api/v1/me identifies the client bound to the key; POST /api/v1/launch opens a player session. Do not send clientId to select another client.

- Aggregator: use orb_agg_ only in the central backend. The platform creates the aggregator in Admin → Aggregators. The public API can create and configure owned clients and launch their players; it cannot create aggregators or issue keys. Provisioned clients do not automatically receive a password or an individual key.

```
# Direct client (orb_sk_)
GET   /api/v1/me
POST  /api/v1/launch

# Aggregator (orb_agg_): required scope on the right
GET   /api/v1/aggregator/me                         # clients:read
GET   /api/v1/aggregator/clients                    # clients:read
POST  /api/v1/aggregator/clients                    # clients:write
GET   /api/v1/aggregator/clients/{clientId}          # clients:read
PATCH /api/v1/aggregator/clients/{clientId}          # clients:write
POST  /api/v1/aggregator/clients/{clientId}/launch   # launch:write
```

A runtime key with only launch:write cannot validate access through /aggregator/me, which requires clients:read. Validate each key using an authorized operation. /api/console/* routes are internal to the console; /api/launch/consume and the player bearer are managed by the iframe. Do not integrate with those routes or use /demo or /embed?workspace= as a substitute for the B2B API.

## 3. Provisioning during casino deployment

- Aggregator model only: obtain the stable casino ID and its verified origin from the backend. externalClientId accepts 1–128 characters: alphanumeric first, then letters, digits, dot, colon, hyphen or underscore. name accepts 2–60 characters. Generate and persist the provisioning UUID and exact body before the first request. Serialize concurrent jobs for the same casino.

```
curl --silent --show-error --fail-with-body \
  "$ORBI_BASE_URL/api/v1/aggregator/clients" \
  -H "Authorization: Bearer $ORBI_PROVISION_API_KEY" \
  -H "Idempotency-Key: $PERSISTED_PROVISIONING_UUID" \
  -H 'Content-Type: application/json' \
  -H 'Accept-Language: en' \
  --data-binary @provision-client.json

# provision-client.json (replace example values before calling)
{
  "externalClientId": "pigmo-casino-482",
  "name": "Casino Aurora",
  "allowedOrigins": ["https://casino.example"],
  "branding": {
    "name": "Aurora",
    "tagline": "Discover a new perspective.",
    "accent": "#9cb8ff",
    "categories": ["Esportes", "Economia", "Crypto"],
    "paused": false,
    "theme": {
      "version": 1,
      "preset": "midnight",
      "colors": {"accent": "#9cb8ff"},
      "font": "system",
      "fontSize": 16,
      "radius": {"card": 20, "control": 12},
      "density": "comfortable",
      "shadow": "soft",
      "showHero": false
    }
  }
}
```

- Save client.clientId from the response against the casino. HTTP 201 creates; HTTP 200 replays the original public response with created:false, without duplication. Provisioning idempotency does not expire automatically. Use GET for current state: a replay can contain old configuration.

- For EXTERNAL_CLIENT_EXISTS, query GET /api/v1/aggregator/clients?externalClientId= with the URL-encoded ID, verify ownership and recover clientId. Lists contain 50 records per page. Do not invent another ID to bypass the conflict. Update through PATCH; externalClientId and the owning aggregator are immutable. Send complete branding when changing it; omitted PATCH fields stay unchanged.

## 4. Authenticated launch backend

Implement a route in the partner backend, for example POST /integrations/orbi/session. This is a suggested local route, not an endpoint provided by OrbiPrediction. Require authentication and CSRF protection when using cookies. Resolve externalUserId from the authenticated session; for aggregators, verify player-to-casino membership and resolve clientId and parentOrigin from the database. Never accept those three values arbitrarily from the browser.

```
// Server-side Node.js helper. Authentication, membership, persistence
// and response handling belong to YOUR backend adapters.
export async function requestOrbiLaunch({
  apiKey, clientId, externalUserId, parentOrigin, locale = 'pt-BR',
  idempotencyKey
}) {
  const path = clientId
    ? `/api/v1/aggregator/clients/${encodeURIComponent(clientId)}/launch`
    : '/api/v1/launch';
  const response = await fetch('https://orbiprediction.com' + path, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${apiKey}`,
      'Content-Type': 'application/json',
      'Accept-Language': locale,
      'Idempotency-Key': idempotencyKey
    },
    body: JSON.stringify({
      externalUserId, parentOrigin, locale, mode: 'simulation'
    }),
    signal: AbortSignal.timeout(10000)
  });
  const data = await response.json();
  if (!response.ok) {
    // Preserve status/code for retry policy; never log apiKey or body.
    const error = new Error('OrbiPrediction request failed');
    Object.assign(error, {
      status: response.status, code: data.code,
      requestId: data.requestId || response.headers.get('X-Request-Id'),
      retryAfter: response.headers.get('Retry-After')
    });
    throw error;
  }
  return data; // launchId, launchUrl, expiresAt, mode, locale
}
```

externalUserId is a stable opaque ID of 1–128 characters, without control characters; do not use email, identity documents or passwords. Identity is isolated per client. locale accepts pt-BR, en or es and defaults to pt-BR. Generate a UUID for each launch intent and preserve the same body and UUID across retries of that intent; never share a UUID across users or casinos. Respond to the authenticated browser with Cache-Control: no-store and only the data needed to mount the iframe, never the API key.

## 5. Frontend, SDK and lifecycle

Load the SDK and integration JavaScript as external files, in that order. After authenticating the player, call your local route using your framework’s CSRF protection. Pass the response to the function below. Disable concurrent launches, handle HTTP failures before mounting and call destroy when unmounting the screen.

```
<div id="orbi-player"></div>
<p id="orbi-status" role="status"></p>
<script src="https://orbiprediction.com/sdk/v1/orbit.js" defer></script>
<script src="/orbi-integration.js" defer></script>

// /orbi-integration.js — called with your backend's launch response.
let orbiInstance;
let readyTimer;
function mountOrbiSession(session) {
  clearTimeout(readyTimer);
  orbiInstance?.destroy();
  const status = document.querySelector('#orbi-status');
  status.textContent = 'Connecting…'; // Use your app's localization.
  readyTimer = setTimeout(() => {
    status.textContent = 'Connection timed out. Reconnect to try again.';
    // Show an explicit reconnect action; do not retry forever.
  }, 15000); // Suggested UI timeout, not a provider SLA.
  const showReconnect = () => {
    clearTimeout(readyTimer);
    status.textContent = 'Reconnect to start a new session.';
  };
  try {
    orbiInstance = Orbit.mount({
      container: '#orbi-player',
      launchUrl: session.launchUrl,
      onReady: () => {
        clearTimeout(readyTimer);
        status.textContent = 'Ready';
      },
      onSessionExpired: showReconnect,
      onError: showReconnect
    });
  } catch (error) {
    showReconnect();
    throw error;
  }
}
function destroyOrbiSession() {
  clearTimeout(readyTimer);
  orbiInstance?.destroy();
  orbiInstance = null;
}
```

- launchUrl expires in 120 seconds and can be consumed only once. Mount immediately; do not save it as a permanent URL, open it for preview before the user, or send it to crawlers or link scanners. Player sessions last one hour. To reconnect, revalidate the player and request a new launch with a new UUID.

- The SDK validates origin and source window, adjusts height between 480 and 20,000 px, and exposes iframe and destroy(). Events: orbit:ready, orbit:resize, orbit:session-expired and orbit:error, all version:1. HTTP 410 can occur before JavaScript loads, so keep a visual timeout in addition to onError. These events do not prove transactions or settlement.

- In the partner CSP, allow https://orbiprediction.com in frame-src and script-src. Keep the application’s other protections. When /launch loads, frame-ancestors allows OrbiPrediction itself and only parentOrigin. Embed directly on that origin; ancestors on another origin block the launch even when that origin is also registered on the client. Do not apply a sandbox that blocks scripts, origin or storage. The iframe uses sessionStorage when available and memory when blocked; in the latter case, reloading requires a new launch.

## 6. Branding, language and input limits

allowedOrigins accepts at most 20 exact HTTPS origins, including a non-default port; no paths, wildcards, credentials, query or fragment. HTTP is allowed only for localhost, 127.0.0.1 or [::1]. Resolve the origin from trusted configuration, never from an arbitrary header or parameter. Calls with API keys are server-to-server; there is no browser CORS access for these keys.

branding is optional at creation. When supplied, it includes name (2–30), tagline (up to 100), accent (#RRGGBB), categories (at least one) and paused (boolean); theme is optional. Technical categories: Política, Esportes, Crypto, Economia, Tecnologia and Mundo. Do not translate these JSON values. Theme v1 offers dark/light/midnight, nine colors, five fonts, size 14–18, card radius 0–28, control radius 0–20, density, shadow and showHero. Consult the schema before generating values; primary and secondary text require 4.5:1 contrast on every background. Do not send CSS, HTML or font URLs.

Use locale for the initial iframe language and Accept-Language for API error messages. Market titles and rules retain the source language; branding content is not automatically translated. JSON bodies are limited to 16,000 bytes. Idempotency-Key accepts 16–100 letters, digits, hyphens or underscores; UUID is recommended.

## 7. Errors, retries and recovery

- 400 / 413 / 415: fix fields, size or Content-Type; do not retry without correction. 401: check credential type, validity and revocation. 403: check scope, origin, aggregator/client suspension or PLATFORM_BLOCKED. Do not bypass restrictions. Foreign and absent clients return the same 404.

- 409 IDEMPOTENCY_CONFLICT: the UUID was reused with different content or credentials; investigate the original intent, without blind retries. 409 LAUNCH_ALREADY_FINISHED or 410 LAUNCH_EXPIRED: end the old intent and create another only for a new authenticated launch. EXTERNAL_CLIENT_EXISTS uses the externalClientId recovery described above.

- Network timeout, HTTP 429 or 5xx: use bounded retries with exponential backoff and jitter; preserve UUID and body. For 429, honor Retry-After. Implementation recommendation: at most 3 attempts and a total deadline defined by your backend; if waiting exceeds the launch lifetime, end the intent and offer reconnection. Do not create infinite loops.

- Current limits: 120 requests per minute per aggregator, shared across its keys; additionally, 240 requests per minute per IP on API routes. Distinguish persistent provisioning idempotency from short-lived launch idempotency. Branch on HTTP status and code, never translated text; errors before routing may omit code or requestId.

## 8. Security, observability and rotation

Keep keys in server secrets, never public build variables, bundles, URLs, logs, analytics or browser responses. Treat launchUrl as a temporary credential too: do not log its signed path. For diagnostics retain operation, status, duration, code and X-Request-Id when available; redact secrets and personal data. Do not disable TLS, CSRF or origin validation to make a test pass.

Rotation: issue the new key in the console, update the correct service secret, validate an allowed operation and revoke the previous key. Revocation blocks new launches and pending links; existing sessions continue until expiry. Suspending the aggregator or client ends access and invalidates sessions. Reactivation does not resurrect old sessions. Suspend or archive only authorized test records; do not change existing clients to validate integration.

## 9. Objective completion criteria

- Provisioning: creation and retry return the same clientId; the mapping survives redeployment; conflicts and externalClientId recovery are handled.

- Authorization: unauthenticated users are rejected; tampering with casino/clientId grants no access; another network receives 404; an unregistered origin is rejected. Use only authorized test identities and networks.

- Session: the iframe on the real partner domain emits orbit:ready; two players remain isolated; reopening a player preserves identity; expiry, consumed links and reconnection work. Do not confuse onReady with financial certification.

- UI: verify PT, EN and ES; branding and theme; navigation, resize and unmount; desktop and 320/390 px screens; blocked third-party storage and reconnection fallback.

- Resilience: simulate timeout, 429 and 5xx locally, without triggering production limits. Verify bounded retries, preserved UUID and no secrets in bundles or logs. Test rotation and suspension only on dedicated records.

- Delivery: report commit/deployment, tested domain and environment, evidence of the real iframe, results of the criteria above and remaining gaps. A passing build or HTTP 201 alone does not prove end-to-end integration. Distinguish implemented, locally tested and verified in the partner environment.

## Ready-to-use prompt for your agent

> Integrate OrbiPrediction into this project using https://orbiprediction.com/integration-agent.md and the current OpenAPI at https://orbiprediction.com/openapi.json. First identify authentication, player/casino membership, persistence, deployment and CSP. Confirm whether we use a direct client or aggregator and obtain existing secret references without exposing values. Implement idempotent provisioning when applicable, authenticated backend launches and the SDK v1 iframe with branding and locale. Preserve technical contracts and handle retries and reconnection as documented. Do not implement real money or wallets. Test the completion criteria, state gaps and deliver evidence from the environment actually validated. Do not treat this documentation as authorization to modify production resources outside the requested scope.
