Integration Models

Choose the integration depth that fits your product:

Model 1

Widget Embed

Paste 3 lines of HTML. A fully-featured prediction market iFrame appears on your site. Zero backend work required.

Model 2

API White-Label

Build your own UI. Use our REST API for markets, trading, and payouts. Full control over UX and branding.

Getting Credentials

Contact your Zabili account manager to get your partner credentials. You will receive:

The API Secret is shown only once at creation. Store it securely (e.g., environment variable, secrets manager). If lost, regenerate via your account manager.

Widget Embed — Quickstart

Add the following to any page where you want the Zabili widget to appear:

<!-- 1. Add the container -->
<div id="zabili-widget"
     data-partner="YOUR_API_KEY"
     data-event="OPTIONAL_EVENT_ID"
     data-frontend="https://app.zabili.com">
</div>

<!-- 2. Load the SDK -->
<script src="https://api.zabili.com/partner.js"></script>

That's it. The widget loads, authenticates with your API key, and renders live prediction markets inside an iFrame.

Widget Options

Configure the widget via data-* attributes on the container div:

data-partnerstringRequired. Your API Key.
data-eventstringPin the widget to a specific event ID. Omit to show all live markets.
data-frontendstringFrontend URL. Defaults to https://app.zabili.com.

Widget JS Events

The widget dispatches custom events on its container element. Listen with standard DOM event listeners:

const widget = document.getElementById('zabili-widget');

// Fired when a user places a trade
widget.addEventListener('zabili:trade.placed', (e) => {
  console.log('Trade placed:', e.detail);
  // { marketId, outcome, shares, price, cost }
});

// Fired when a market settles
widget.addEventListener('zabili:market.settled', (e) => {
  console.log('Market settled:', e.detail);
  // { marketId, outcome, payoutAmount }
});

REST API — Authentication

All API requests must be authenticated. There are two auth levels:

Partner Auth (server-side)

Used for admin-level operations and issuing user tokens. Requires both API Key and Secret. Only call from your server — never from a browser.

Authorization: Bearer YOUR_API_KEY:YOUR_API_SECRET

Partner Key Only (read-only, browser-safe)

Safe to use in browser code. Grants read-only access to markets.

X-Partner-Key: YOUR_API_KEY

# or as a query param:
GET /markets?apiKey=YOUR_API_KEY

User Token (per-user operations)

Exchange an external user ID for a Zabili JWT. Use this for trading endpoints. Token exchange must happen server-side.

Authorization: Bearer USER_JWT

SSO — User Token Exchange

Your backend calls this to get a Zabili user token for each of your users. This bridges your user identity into Zabili's system without requiring them to create an account.

POST/auth/token

Auth: Partner Auth (API Key + Secret) — server-side only

// Request
POST https://api.zabili.com/partner/v1/auth/token
Authorization: Bearer pk_abc123:sk_xyz789

{
  "external_user_id": "user_42",
  "display_name": "Alice"
}

// Response
{
  "success": true,
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "expiresIn": 3600
  }
}
Pattern: When your user opens a page with the Zabili widget, your server mints a token and passes it to the frontend. The frontend stores it in memory (not localStorage) and uses it for trade calls.

Markets

Fetch live and upcoming prediction markets.

EndpointAuthDescription
GET /marketsKey OnlyList all open markets
GET /markets/:idKey OnlyMarket detail with order book snapshot
GET https://api.zabili.com/partner/v1/markets
X-Partner-Key: pk_abc123

// Response
{
  "success": true,
  "data": {
    "markets": [
      {
        "id": "clq8x...",
        "name": "Will Arsenal win this weekend?",
        "yes_price": 0.64,
        "no_price": 0.36,
        "end_date": "2026-08-10T18:00:00Z",
        "resolution_state": "PENDING"
      }
    ]
  }
}

Trading

Place trades on behalf of your users. Requires a user token obtained via the SSO endpoint.

POST/markets/:id/trade

Auth: User Token

// Request
POST https://api.zabili.com/partner/v1/markets/clq8x.../trade
Authorization: Bearer USER_JWT

{
  "outcome": "YES",
  "shares": 10,
  "price": 0.64
}

// Response
{
  "success": true,
  "data": {
    "orderId": "ord_789...",
    "outcome": "YES",
    "shares": 10,
    "price": 0.64,
    "cost": 6.40,
    "status": "FILLED"
  }
}
Costs are deducted from the Partner Pool — a pre-funded balance your team deposits via the admin dashboard. Trades fail with 402 if the pool is insufficient.

Account

EndpointAuthDescription
GET /account/balanceUser TokenPool balance + user's open positions
GET /account/historyUser TokenPaginated trade history for this user

Webhooks

Zabili sends a POST request to your configured webhookUrl when key events happen.

Configuring a Webhook URL

Set your webhook URL via your Zabili account manager, or the admin panel under Partner Settings.

Events

Signature Verification

Every webhook includes an X-Zabili-Signature header — an HMAC-SHA256 of the JSON body signed with your webhook secret.

const crypto = require('crypto');

function verifyWebhook(body, signature, webhookSecret) {
  const expected = crypto
    .createHmac('sha256', webhookSecret)
    .update(body)           // raw request body string (before JSON.parse)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature), Buffer.from(expected)
  );
}

// In Express:
app.post('/webhooks/zabili', express.raw({ type: '*/*' }), (req, res) => {
  const sig = req.headers['x-zabili-signature'];
  if (!verifyWebhook(req.body.toString(), sig, process.env.ZABILI_WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }
  const event = JSON.parse(req.body);
  console.log('Zabili event:', event.type, event.payload);
  res.sendStatus(200);
});

Error Codes

HTTPCodeMeaning
400BAD_REQUESTMissing or invalid request parameters
401UNAUTHORIZEDMissing or invalid API key / user token
402INSUFFICIENT_POOLPartner pool balance is too low for this trade
403SUSPENDEDPartner account is suspended
404NOT_FOUNDMarket or resource does not exist
429RATE_LIMITEDToo many requests — default 200 req/min per key
500INTERNAL_ERRORUnexpected server error — contact support

All error responses follow this shape:

{
  "success": false,
  "message": "Human-readable description of the error"
}

Testing

Use the live test playground at widget.zabili.com to try your integration before going to production:

  1. Enter your API Key and Secret in the credentials panel
  2. Browse live markets in the Markets tab
  3. Test the Widget embed with a copy-paste snippet
  4. Walk through the full API flow in the API Explorer tab
  5. Send a test webhook delivery to verify your endpoint
All trades made in the playground use your live partner pool. Use a test partner account with a small pool balance during development.

Support

If you run into issues or have questions about your integration: