Server API

Quick answer

Use the Server API when your backend owns sessions, orchestration, or rollout policy and must keep secret keys off browser clients.

POST /v1/placements creates the default placement response, while the hosted renderer owns browser media rendering.

Server API is the recommended default backend path. Use it when your backend already owns session state, orchestration, or rollout policy, while Wavebird owns hosted media rendering in the browser.

.env.local

bash

SERVER ENV
1# Project ID (client_id, WAVEBIRD_CLIENT_ID): wbproj_your_project_id2WAVEBIRD_API_BASE_URL=https://api.wavebird.ai3WAVEBIRD_SECRET_KEY=sk_test_your_server_test_key4WAVEBIRD_CLIENT_ID=wbproj_your_project_id

create-placement.sh

bash

RECOMMENDED
1curl -X POST https://api.wavebird.ai/v1/placements?wait_ms=1500 \2  -H "Authorization: Bearer sk_test_wavebird_demo_secret" \3  -H "Content-Type: application/json" \4  -d '{5    "client_id": "wbproj_demo_8jK42",6    "session_id": "sess_demo_123",7    "job_type": "chat",8    "slots_requested": 1,9    "prompt": {10      "topic": "travel"11    },12    "slot_hint": {13      "position": "below",14      "max_width": 728,15      "max_height": 9016    },17    "consent": {18      "semantic_targeting": false,19      "prompt_shared": false,20      "gdpr_applies": false,21      "consent_source": "wavebird_consent"22    }23  }'

chat.html

html

RENDER
1<script src="https://api.wavebird.ai/v1/render.js"></script>2<section id="wavebird-slot" data-wavebird-endpoint="/api/sponsor-slot" hidden></section>34<script type="module">5  async function onUserMessage(message) {6    return window.wavebird.withTurn("#wavebird-slot", () => sendChatMessage(message));7  }8</script>

advanced-policy-controls.json

json

ADVANCED
1{2  "overrides": {3    "allowed_formats": ["banner", "clip", "native"],4    "native_template_id": "card",5    "timing": "during",6    "bidfloor": 0.5,7    "bidfloor_currency": "EUR",8    "publisher": {9      "app_name": "MyAIChatApp",10      "app_domain": "mychatapp.com",11      "categories": ["IAB19"]12    },13    "blocked_categories": ["IAB7"]14  }15}

direct-server-beacon.mjs

javascript

ADVANCED
1const occurred_at = new Date().toISOString();23await fetch("https://api.wavebird.ai/v1/beacons", {4  method: "POST",5  headers: {6    Authorization: "Bearer " + process.env.WAVEBIRD_SECRET_KEY,7    "Content-Type": "application/json"8  },9  body: JSON.stringify({10    beacon_id: "bcn_example_rendered_001",11    slot_id: "slot_...",12    asset_token: "wbat_...",13    event: "rendered",14    occurred_at,15    metadata: {}16  })17});

Backend control

Why use the server path

Own the lifecycle

Request placements from your backend without exposing secret keys to the browser.

Keep secrets server-side

The secret-key path is simpler when your app already has a backend and does not need direct browser-side monetization control.

Pair with your renderer

Use the hosted renderer for the default path, or keep raw decision polling only for advanced compatibility flows.

Credentials

Server Test Key

Start with a Server Test Key from Dashboard API Keys. The full sk_test_... value is shown only when the key is created or rotated, so copy it immediately into WAVEBIRD_SECRET_KEY. If the dashboard only shows a masked preview, create or rotate a Server Test Key before running the Server API examples. Test/Sandbox requests are non-billable.

Credentials

Key classes

Server Test Key

Use sk_test_... only from your backend for Test/Sandbox requests. These requests are non-billable.

Server Production Dry-run Key

Use sk_dry_... only from your backend for non-billable pre-live dry-run. Dry-run is selected by credential class, not by request fields.

Server Production Key

Use a Server Production Key only after Dashboard live readiness, SSP readiness, payout requirements, and live approval gates are complete.

Browser Publishable Key

Use a Browser Publishable Key only for browser activation and Script Tag flows with allowed origins. Do not put server secrets in browser code.

Raw server secrets are shown only once after create or rotate. Existing masked server-secret previews identify a key but are not usable credentials. Do not send production_dry_run, production_billing_dry_run, billing_suppressed, or production_live_approved in request bodies.

Contracts

Consent and beacon boundaries

Request-level placement consent

Send semantic_targeting, prompt_shared, gdpr_applies, and consent_source inside /v1/placements when consent is scoped to the current request. This does not require a prior /v1/consent sync.

Separate /v1/consent sync

Use /v1/consent only when you want to persist a session or user consent decision outside a single placement request.

Hosted renderer beacons

The default renderer path sends browser beacons automatically to /public/wrapper/v1/beacons. Most integrations do not need manual beacon code.

Direct server beacons

Use /v1/beacons only for advanced server-rendered, custom-rendered, or QA validation flows. It requires the issued asset_token, a fresh occurred_at, and idempotent beacon_id values.

Troubleshooting

Timestamp freshness

Avoid BEACON_TOO_LATE

occurred_at must be close to when the event actually happened. Copying an old static sample timestamp can return BEACON_TOO_LATE. Generate it at runtime with const occurred_at = new Date().toISOString();.

Need rollout review?

Start with the Server API. Use contact only when you need rollout review, enterprise coordination, or non-standard integration help.

Contact the team