# Hosted renderer script

Returns the browser renderer that mounts hosted placement frames and manages turn lifecycle.

Load this script in the customer frontend for the default Server API render path. It exposes window.wavebird.renderPlacement, clearPlacement, startTurn, and withTurn.

| Method | Path | Authentication | Stability |
| --- | --- | --- | --- |
| GET | /v1/render.js | No authorization header | stable |

## Response fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| window.wavebird.renderPlacement(options) | function | Yes | Renders a decision or placement into a target element using placement.render.frame_url. |
| window.wavebird.withTurn(target, work) | function | Yes | Starts the placement request, runs the app work, and preserves required viewability timing before cleanup. |
| renderer diagnostics | beacon metadata | No | Hosted renderer beacons include safe geometry and dry-run diagnostics such as proof_source, proof_eligible, billable, non_billable_reason, geometry_reason, renderer_diagnostic_case, production_dry_run, production_live_approved, contract_exists, and renderer_geometry_summary for dashboard debugging. |

## Errors

| Status | Code | Description |
| --- | --- | --- |
| 404 | not_found | Renderer script route is unavailable. |

## Response example

```json
window.wavebird.renderPlacement({ target: "#wavebird-slot", decision });
```

## Renderer proof diagnostics
The hosted renderer sends diagnostic beacons automatically. A renderer beacon can be accepted for logging while still being proof-ineligible and non-billable.
Dashboard logs may show proof_source=public_wrapper_renderer, proof_eligible=false, billable=false, non_billable_reason, geometry_reason, renderer_diagnostic_case, production_dry_run, production_live_approved, contract_exists, and renderer_geometry_summary.
Common geometry reasons are INSUFFICIENT_VISIBLE_AREA, HIDDEN_OR_NOT_VIEWABLE, OFFSCREEN, RENDERED_SIZE_TOO_SMALL, and NO_RENDERER_GEOMETRY_PROOF.
Sandbox and test traffic remains non-billable. Production dry-run is also non-billable: create a Server Production Dry-run Key in Dashboard API Keys or use an operator-issued sk_dry_... server key as WAVEBIRD_SECRET_KEY.
Production dry-run is selected by credential class, not by request fields. Do not send production_dry_run, production_billing_dry_run, billing_suppressed, or production_live_approved in request bodies; those names are response, log, or dashboard diagnostics only.
Qualifying renderer proof can show proof_eligible=true and proof_would_be_eligible=true while billable=false, billing_suppressed=true, dry_run_billable_suppressed=true, and non_billable_reason=PRODUCTION_DRY_RUN_NON_BILLABLE.
Production dry-run requires no payout setup and does not use production-live-approved. Dashboard rows should expose mode=production-dry-run and production_live_approved=false during dry-run QA. If no active SSP demand is available, placement creation returns 409 ssp_not_ready with production_dry_run=true, production_billing_dry_run=true, billing_suppressed=true, production_live_approved=false, live_billing=false, and billable=false.

## cURL

```bash
curl "https://api.wavebird.ai/v1/render.js" \
  -H "Accept: application/javascript"
```

## Node

```javascript
const response = await fetch("https://api.wavebird.ai/v1/render.js", {
  headers: { Accept: "application/javascript" }
});

console.log((await response.text()).slice(0, 80));
```

## Python

```python
import urllib.request

request = urllib.request.Request(
    "https://api.wavebird.ai/v1/render.js",
    headers={"Accept": "application/javascript"},
)

with urllib.request.urlopen(request) as response:
    print(response.read().decode("utf-8")[:80])
```
