On-chain integration

Integration guide

Connect EVM or Solana webhooks, or POST normalized wager events from your indexer. Wallet address is the player ID — triggers run on the same engine for prediction markets and crypto casinos.

Snippets below use https://wagerly.tech as your app origin.

Connect your chain

Primary setup wizard — Alchemy webhook, no app code (~15 min).

wizard
# Connect your chain (10–15 min, no app code)

Primary path for all on-chain operators:

1. Open Dashboard → Connect your chain
   https://wagerly.tech/dashboard/setup

2. Choose market type
   - Prediction market → Polymarket full (Polygon, pre-filled addresses)
   - Crypto casino → generic contract on Base
   - Other on-chain → pick chain + contract address

3. Copy into Alchemy (Address Activity webhook)
   URL:    POST https://wagerly.tech/api/v1/ingest/{tenantId}/evm
   Header: Authorization: Bearer <ingest_secret>
   Watch:  contract addresses shown in the wizard

4. Paste Alchemy signing key in the wizard → wait for Retention live

5. Optional on finish step
   - Enable Dormancy (pure inactivity) trigger
   - Set outbound webhook URL

No API key or SDK required for core retention. Player ID = {chainId}:0xWallet.

Solana: Settings → Advanced connectors → Solana
Advanced HTTP/Supabase: Settings → Advanced connectors

Open Dashboard → Connect your chain after sign-in.

Quickstart

Advanced paths — HTTP ingest and npm SDK for indexers or legacy off-chain apps.

Recommended: use the setup wizard at /dashboard/setup. Manual reference below.

setup
# EVM webhooks (via setup wizard)

Use Dashboard → /dashboard/setup for copy-paste URL, ingest secret, and watch addresses.

Manual reference:
  POST https://wagerly.tech/api/v1/ingest/{tenantId}/evm
  Authorization: Bearer <ingest_secret>

Logs decode to wager_placed / wager_settled / deposit_completed.
Player IDs: {chainId}:0xWallet — e.g. 8453:0x742d…
See #connect-chain · #ingest-auth · #connectors · #polymarket

Go-live checklist

Connect chain → Retention live → optional trigger and webhook.

checklist
# Go-live checklist

1. Open Dashboard → Connect your chain (/dashboard/setup)
2. Choose market type and configure Alchemy Address Activity webhook
3. Paste signing key → confirm Integration health shows "Retention live"
4. Optional: enable dormancy trigger + outbound webhook on wizard finish step
5. Polymarket: use prediction market path (Exchange + USDC pre-filled)

Advanced: Settings → Advanced connectors for HTTP, Supabase, Solana, manual EVM.

See also: #connect-chain · #metrics · #webhooks · #operations · #events

Track progress in Settings → Integration health and wire connectors at Settings → Connectors.

GGR / NGR metrics

Rolling 7d and 30d gross and net gaming revenue per wallet.

definitions
# GGR / NGR metrics

Wagerly computes per-wallet rollups from ingested wager events:

| Metric | Definition | Source events |
|--------|------------|---------------|
| GGR (7d / 30d) | Gross gaming revenue — total wager handle | SUM(handle) on wager_placed |
| NGR (7d / 30d) | Net gaming revenue — operator margin | SUM(net_margin) on wager_settled |

Where to view:
  - Dashboard → Overview: tenant 7d GGR / NGR rollup
  - Dashboard → Players: per-wallet 7d GGR / NGR columns
  - Player detail: 7d + 30d + lifetime GGR/NGR cards

Trigger webhook payload (financials block):
  ggr_7d, ngr_7d, ggr_30d, ngr_30d
  lifetime_handle, lifetime_net_margin
  handle_7d, handle_30d

Ingest authentication

Bearer ingest_secret on every webhook ingest route — enforced since Phase 2.

auth
# Ingest authentication (required)

Every webhook ingest route rejects unauthenticated requests:

  Authorization: Bearer <ingest_secret>

Routes:
  POST /api/v1/ingest/{tenantId}           — universal JSON
  POST /api/v1/ingest/{tenantId}/evm       — Alchemy / QuickNode / raw logs
  POST /api/v1/ingest/{tenantId}/supabase  — Supabase DB webhook
  POST /api/v1/ingest/{tenantId}/solana    — Helius / raw Solana txs
  POST /api/v1/ingest/{tenantId}/test-mapping — field mapping preview

Copy or rotate ingest_secret in Settings → Credentials or Advanced connectors.
After rotation, update every Alchemy endpoint and curl script — old secrets stop working immediately.

Note: POST /api/v1/track uses your app tracking key (wg_live_…), not the ingest secret.
Optional for browser/SDK integrations — on-chain operators use the setup wizard + ingest routes above.

EVM contract templates

Pre-configured decoders in Settings → Connectors → EVM.

templates
# EVM contract templates

First-time setup: use Dashboard → /dashboard/setup (templates pre-selected by market type).

Advanced manual config: Settings → Advanced connectors → EVM → Contract template.

| Template | Use when |
|----------|----------|
| generic_erc20_wager | Custom casino contracts with Trade / Deposit / Settle events |
| polymarket_style | Single-contract Polymarket-style decode (legacy) |
| polymarket_full | Production Polymarket — CTF Exchange + USDC + optional NegRisk |

Advanced fields (trade/deposit/settle event sigs, arg indices) are pre-filled per
template. Override only when your ABI differs.

Player IDs are always stored as {chainId}:0xWallet (e.g. 137:0x742d…).

Polymarket setup

Multi-contract ingest for prediction markets on Polygon.

polymarket_full
# Polymarket on Polygon (polymarket_full)

1. Settings → Connectors → EVM → select "Polymarket full (multi-contract)"
2. Paste contract addresses (verify on Polygonscan before production):

   CTF Exchange (primary — OrderFilled trades/settles):
     0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E

   USDC (deposit_completed via Transfer):
     0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174

   NegRisk adapter (optional — same OrderFilled sig):
     paste your deployment address or leave blank

3. Alchemy / QuickNode webhook:
   - Register ALL watched addresses (Exchange + USDC + NegRisk if used)
   - Webhook URL: POST …/api/v1/ingest/{tenantId}/evm
   - Header: Authorization: Bearer <ingest_secret>
   - Prefer GRAPHQL / custom log webhooks that include transaction logs

4. Wagerly decodes:
   - OrderFilled on Exchange/NegRisk → wager_placed / wager_settled
   - USDC Transfer to player wallet → deposit_completed
   - Same wallet maps to player_id 137:0x… across contracts

Solana ingest

Helius webhooks and Anchor program presets.

solana
# Solana ingest

URL:
  POST https://wagerly.tech/api/v1/ingest/{tenantId}/solana
  Authorization: Bearer <ingest_secret>

Player ID format:
  solana:{base58Pubkey}

Settings → Connectors → Solana:
  - Program ID (your Anchor wager program)
  - Template: generic_wager (WagerPlaced / WagerSettled discriminators)
  - Optional Helius webhook secret → verifies x-helius-signature

Helius enhanced webhooks: point at the URL above with the ingest secret header.
Raw transaction JSON (logs array) is also supported when you POST manually.

Outbound webhooks

Verify trigger.fired deliveries with HMAC-SHA256.

signing
# Outbound trigger webhooks

When a trigger fires with action_type=webhook, Wagerly POSTs JSON to your URL.

Headers:
  Content-Type: application/json
  X-Wagerly-Event: trigger.fired
  X-Wagerly-Signature: sha256=<hex>

Signing:
  HMAC-SHA256 of the raw request body bytes, keyed with webhook_signing_secret
  (Settings → Webhook → Signing secret). Same signature on "Send test event".

Payload shape (abbreviated):
  wagerly_event: trigger.fired
  trigger: { id, type, name, fired_at, condition_met, … }
  player: { id, last_wager_at, days_since_last_wager, … }
  behavior: { current_loss_streak, handle_7d, handle_30d, … }
  financials: { ggr_7d, ngr_7d, ggr_30d, ngr_30d, … }
  promotion_guidance: { safe_to_promote, suggested_offer, … }
verify (openssl)
# Verify with openssl (replace SECRET and paste exact raw body)
echo -n '{"wagerly_event":"trigger.fired",…}' | openssl dgst -sha256 -hmac "wg_whsec_YOUR_SECRET"
verify (Node.js)
import { createHmac, timingSafeEqual } from 'node:crypto'

export function verifyWagerlyWebhook(rawBody: string, signatureHeader: string | null, secret: string) {
  if (!signatureHeader?.startsWith('sha256=')) return false
  const expected = 'sha256=' + createHmac('sha256', secret).update(rawBody, 'utf8').digest('hex')
  const a = Buffer.from(expected)
  const b = Buffer.from(signatureHeader)
  return a.length === b.length && timingSafeEqual(a, b)
}

// Express: use express.raw({ type: 'application/json' }) so you hash the raw body

Copy your signing secret from Settings → Webhook. Test deliveries from the same page include X-Wagerly-Signature.

Operator tools

Compliance, credentials, dedup, and vertical presets.

dashboard
# Operator tools (dashboard)

Compliance export (Settings → Compliance export)
  CSV download of trigger_fires + RG intervention rows for a date range.
  Use for operator audits and regulator requests.

Credential history (Settings → Credential history)
  Last 10 rotations of ingest_secret, API key, and webhook signing secret.
  Secret values are never stored — only type, timestamp, and actor label.

Data health (Dashboard → Players → Data health)
  Scan for duplicate wallet profiles (e.g. bare 0x vs 137:0x…) after migrating
  to canonical player IDs. Merge reassigns events and aggregates stats.

Vertical presets (Settings → Platform vertical)
  Changing vertical updates trigger threshold hints. After save, choose
  "Apply presets" to rewrite seeded trigger thresholds (Loss streak, Dormancy, Tilt).

Framework guides

Complete, production-ready snippets. Paste and replace YOUR_API_KEY.

.env
# .env / .env.local
WAGERLY_API_KEY=wg_live_your_key_here

# Only needed for browser tracking in Next.js
NEXT_PUBLIC_WAGERLY_KEY=wg_live_your_key_here
Rule: Never let Wagerly calls block your main response — always use fire-and-forget or catch errors silently. Wagerly is behavioral analytics, not business logic.
Next.jsApp Router · TypeScript
lib/wagerly.ts + app/api/bets/route.ts
// lib/wagerly.ts — module-level singleton
import { Wagerly } from '@wagerly/sdk'

export const wagerly = new Wagerly({
  apiKey: process.env.WAGERLY_API_KEY!,
})

// app/layout.tsx — browser SDK for front-end tracking
// Add before </body>:
// <script
//   src="https://wagerly.dev/sdk.js"
//   data-api-key={process.env.NEXT_PUBLIC_WAGERLY_KEY}
//   data-user-id={session?.user?.id}>
// </script>

// app/api/bets/route.ts — track wager on placement
import { wagerly } from '@/lib/wagerly'

export async function POST(req: Request) {
  const bet = await placeBet(req)

  // Fire-and-forget — never block the response
  wagerly.wager(userId, {
    wager_id:  bet.id,
    handle:    bet.amount,
    odds:      bet.odds,
    currency:  'USD',
    market:    bet.marketType,
  }).catch(console.error)

  return Response.json(bet)
}

SDK options

Pass to new Wagerly(options)

OptionTypeDefaultDescription
apiKeystringrequiredLive API key — starts with wg_live_
baseUrlstringhttps://wagerly.devOverride for on-prem or staging
batchSizenumber20Force-flush after this many queued events
flushIntervalnumber5000Auto-flush interval in ms
debugbooleanfalseVerbose console logging

Event reference

All 16 core event types plus 3 auto-captured browser events.

EventSDK methodWhen to callKey properties
wager_placedwagerly.wager()When a bet is submittedwager_id, handle, currency, odds, market
wager_settledwagerly.wagerSettled()When the outcome is knownwager_id, outcome, payout, net_margin
wager_voidedwagerly.track()Bet voided/cancelledwager_id, handle, currency, void_reason
wager_cashed_outwagerly.track()Early cashout acceptedwager_id, cashout_amount, net_margin
bet_slip_openedAuto (browser SDK)Slip opened (has data-wagerly attr)selections_count, total_stake
bet_slip_abandonedwagerly.track()Slip closed without placingselections_count
account_createdwagerly.track()New player signs upsignup_channel, promo_code
deposit_initiatedwagerly.track()Deposit flow startedamount, currency, payment_method
deposit_completedwagerly.deposit()Deposit cleared/settledamount, currency, payment_method, transaction_id
withdrawal_requestedwagerly.withdrawal()Withdrawal initiatedamount, currency, payment_method
withdrawal_completedwagerly.track()Funds sentamount, currency, transaction_id
session_startwagerly.session("start")Player logs in / session begins
session_endwagerly.session("end")Player logs out / session endsduration_seconds
loginwagerly.track()Player authenticatesmethod
promo_claimedwagerly.track()Promo code or bonus acceptedpromo_id, promo_code, promo_type, value
promo_expiredwagerly.track()Promo window lapsedpromo_id

Browser SDK auto-captured events

EventTriggerKey properties
page_viewEvery page navigationurl, referrer
rage_click3+ clicks on same element within 500 mselement, x, y
tab_visibilityTab gains or loses focusvisible

Direct API

Legacy track endpoint — on-chain operators should use ingest routes (#ingest-auth) instead.

cURL
curl -X POST https://wagerly.dev/api/v1/track \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer wg_live_your_key" \
  -d '{
    "player_id": "user_123",
    "event": "wager_placed",
    "client_timestamp": "2026-06-23T14:00:00Z",
    "properties": {
      "wager_id": "bet_001",
      "market":   "moneyline",
      "handle":    50,
      "odds":      1.91,
      "currency": "USD"
    }
  }'

# Response: {"ok": true}

Endpoint: POST https://wagerly.tech/api/v1/track

Auth: Authorization: Bearer <api_key> (not ingest_secret)

Preferred for on-chain: POST /api/v1/ingest/{tenantId}/evm with ingest secret

Content-Type: application/json

Response: {"ok": true} on success

Responsible gaming

Built-in RG suppression — not bolted on after the fact.

Automatic suppression

Wagerly's trigger engine checks RG status before firing any action. Promotional and retention triggers are automatically blocked for at-risk players — no extra code required.

cooling_off_untilAll triggers suppressed until the date elapses
rg_status = 'intervene'Non-RG promotional triggers suppressed
rg_status = 'suspend'All non-RG triggers suppressed

Setting RG flags

RG flags (rg_flag_level, rg_status, cooling-off periods) are set from the Responsible Gaming dashboard. Wagerly auto-elevates rg_flag_level based on behavioral signals — operators review and act via the dashboard.

Suppression audit log: Every suppressed trigger is logged to trigger_fires with status='suppressed' and a reason string. Export trigger + RG history as CSV from Settings → Compliance export.

Wagerly © 2026