Testing
Verify a placement from request to rendered ad, including consent, no-fill, and measurement. Start with test credentials, then check production dry-run before enabling billable traffic.
Test safely
Environments and credentials
Test
Use a Server Test Key (sk_test_*) for integration checks. Test traffic is non-billable, including validation and guardrail diagnostics.
Production dry-run
Use a Server Production Dry-run Key (sk_dry_...). Dry-run is selected by credential class and remains non-billable. No payout setup is required.
Live boundary
Before sending billable traffic, complete payout setup and activate live delivery for your project in Dashboard. Billable impressions require verified delivery records.
Secret visibility
Credential classes
Server credentials
Server Test, Production Dry-run, and Live keys are backend-only secrets. Copy the full value when it is created or rotated; Dashboard stores only a masked preview and cannot reveal the original secret later.
Browser credentials
Browser publishable keys are limited to their configured origins and project. They do not replace a server key and must never grant access to billing, payout, or private account operations.
Checklist
Minimum validation path
Check credentials and project
The server key and browser publishable key must belong to the same Test project. Use its Project ID as client_id or WAVEBIRD_CLIENT_ID. In Dashboard API Keys, create or rotate a Server Test Key and copy the full value immediately; masked previews are for identification and cannot authenticate requests.
Exercise no-fill and fill
A filled response should render the selected format. No-fill should leave no empty ad container and must not interrupt the AI task. Use the controlled no-fill request below to test this branch.
Exercise optional fields
Test prompt, slot_hint, request-level consent, and advanced overrides separately. Validation errors include request_id and field paths such as slot_hint.max_width.
Confirm beacons
Check rendered and completion beacons for the formats you actually ship. Billing evidence depends on that path, not just on placement creation.
Server Test Key only
Deterministic Test no-fill
Trigger one controlled no-fill
Call /v1/placements?wait_ms=1500&test_outcome=no_fill with the same valid body you use for a normal Test request. The response is successful and processed, but returns decision.fill: false, placement: null, and test_controlled_no_fill as its no-fill reason.
Keep it outside Production
The control is accepted only with a Server Test Key and remains non-billable. Production, production dry-run, and browser keys cannot use it. Remove the query parameter for ordinary fill testing.
Script Tag
Browser-specific checks
Verify activation against the expected origin, validate consent-required behavior, and confirm that the browser receives only the runtime-safe project config for the active client_id.
Sandbox
Expected negative tests
Beacon validation failures
Intentionally missing asset_token, missing slot_id, invalid event, or stale occurred_at requests should return actionable 400 validation errors in sandbox tests.
A stale timestamp can return BEACON_TOO_LATE. Generate a fresh timestamp for every direct server beacon with new Date().toISOString().
Consent validation failures
Invalid consent purposes should return field-level validation errors. These are expected negative tests when deliberately triggered and should not be treated as production integration failures.
Hosted renderer first
Default integrations should rely on hosted renderer beacons sent to /public/wrapper/v1/beacons. Direct server /v1/beacons is an advanced QA or custom-rendering path.
Direct beacon token handling
Direct server beacons require the sensitive asset_token from placement.asset_token or decision.asset_token. Redact it from logs, screenshots, reports, and client-visible debug output.
Sandbox
Renderer proof diagnostics
Hosted renderer beacons are accepted for sandbox QA even when they are not proof-eligible or billable. Dashboard logs may expose safe diagnostic fields such as proof_source=public_wrapper_renderer, proof_eligible=false, billable=false, non_billable_reason, geometry_reason, renderer_diagnostic_case, contract_exists=true, and renderer_geometry_summary.
| Case | Expected reason | Use in QA |
|---|---|---|
tiny | INSUFFICIENT_VISIBLE_AREA | Positive but too-small visible geometry, such as a 1x1 diagnostic slot. |
clipped_8x1_25 | INSUFFICIENT_VISIBLE_AREA | A clipped renderer row with visible/intersection geometry below the paid contract. |
hidden or opacity_zero | HIDDEN_OR_NOT_VIEWABLE | CSS-hidden or transparent renderer evidence that must remain non-billable. |
offscreen | OFFSCREEN | Renderer evidence outside the viewport or intersection path. |
Pre-live
Production dry-run contract
Use the dry-run server key
Production dry-run is a public pre-live validation mode selected by credential class, not by a request-body flag. Create a Server Production Dry-run Key in Dashboard API Keys, or request one from your wavebird contact. Set its sk_dry_... value as WAVEBIRD_SECRET_KEY for normal server-side /v1/placements calls. Do not send undocumented production_dry_run, production_billing_dry_run, billing_suppressed, or production_live_approved fields in request bodies; those names are response, log, or dashboard diagnostics only.
Check project activation
If live delivery is not activated for your project, use a Server Test Key to validate the integration. Use a Production Dry-run Key when it is available in Dashboard; it verifies the delivery path without billing.
Expect safe blocked responses
A dry-run placement with no active SSP readiness should fail closed with 409 ssp_not_ready. The expected response/log fields include mode=production-dry-run, production_dry_run=true, production_billing_dry_run=true, billing_suppressed=true, production_live_approved=false, live_billing=false, and billable=false.
Check renderer evidence
Qualifying hosted renderer proof in dry-run should report mode=production-dry-run, PRODUCTION_DRY_RUN_NON_BILLABLE, production_dry_run=true, production_live_approved=false, billing_suppressed=true, dry_run_billable_suppressed=true, and proof_would_be_eligible=true.
Live billing stays separate
Use test or dry-run credentials for these checks. Do not click real ads for testing. A successful dry-run verifies delivery, not live billing. The mode is production-dry-run, not production-live-approved.
Need help with your integration?
Share the affected endpoint, request ID, and the behavior you expected. Leave out keys and user content.