Production
TL;DR — For every Wandura call, log the time, endpoint, status,
error_code, latency, your key's prefix, the episode and yourviewer_ref, and the request id once it is live. Never log keys, tickets, signed links or raw device values. Watch ticket success by code,5xxand latency, catalog sync health, and deprecation headers.
What to log on every call
| Field | Why |
|---|---|
| UTC timestamp (to the millisecond), endpoint and method | When and where. |
HTTP status, error_code (or creatives code), reason, fields |
What happened. |
| Latency | Spot slowness before viewers do. |
Wandura-Request-Id, or request_id from an error body |
The exact request in Wandura's logs. |
Wandura-Version, Wandura-API-Status, Retry-After, Deprecation, Sunset |
Version and lifecycle signals. |
Key prefix (wnd_live_ab12cd), never the key |
Which key, safely. |
Episode (episode_id, or film_ref + episode_no), intent, batch size and per-item statuses |
Playback problems are per episode. |
Your viewer_ref |
Ties Wandura's records to your viewer, without personal data. |
Every response carries Wandura-Request-Id, and every JSON error body request_id. Log it on every call.
Never log
| Value | Instead |
|---|---|
The API key, the Authorization header |
The key prefix |
play_auth_token, manifest_url, open video links |
Nothing, or "present" |
Raw union_info, drm_kek |
A short hash |
Viewer email, phone, name — also not inside viewer_ref |
Your opaque viewer_ref |
Full /playback response bodies |
Status, code and expires_at |
This applies to APM tools, proxies, analytics and crash reporters too. The examples ship a redaction helper:
/** Secrets are dropped; device values are hashed; API keys are cut to the prefix the portal shows. */
const SECRET_FIELDS = new Set(['authorization', 'api_key', 'play_auth_token', 'grant_ticket', 'manifest_url']);
const HASHED_FIELDS = new Set(['union_info', 'drm_kek']);
/** `wnd_live_ab12cd…` → `wnd_live_ab12cd`. Enough for support, useless to an attacker. */
export function keyPrefix(key: string): string {
return key.slice(0, 15);
}
/** Copy with secrets removed. Run everything you log, trace or send to a crash reporter through it. */
export function redact(value: unknown): unknown {
if (Array.isArray(value)) return value.map(redact);
if (typeof value === 'string') return value.replace(/wnd_live_[A-Za-z0-9_-]{6,}/g, (k) => `${keyPrefix(k)}…`);
if (!value || typeof value !== 'object') return value;
const out: Record<string, unknown> = {};
for (const [k, v] of Object.entries(value)) {
const lk = k.toLowerCase();
if (SECRET_FIELDS.has(lk)) out[k] = '[redacted]';
else if (HASHED_FIELDS.has(lk) && typeof v === 'string') out[k] = `sha256:${createHash('sha256').update(v).digest('hex').slice(0, 12)}`;
else out[k] = redact(v);
}
return out;
}What to monitor
| Signal | Alert when | Usually means |
|---|---|---|
Ticket answers by error_code |
A jump in not_entitled |
A licence ended — check your catalog sync |
A jump in invalid_device_info or sdk_required |
An app release broke the Player SDK wiring | |
429 on play tickets |
A retry loop, or a peak above your limit | |
5xx rate and ticket latency |
5xx above 1 % or latency above 2 s for 5 minutes |
A Wandura incident: report it (Troubleshooting) |
| Last successful catalog sync | Older than two intervals | A stalled job keeps showing withdrawn titles |
| Number of films and playable episodes | A sudden drop | Worth a message to Wandura |
Deprecation / Sunset headers |
Any | A change is coming: Migrating versions |
| Last used per key, on Developers | A key you think unused is used | A leak, or a forgotten system |
On partner.wandura.ai → Developers → API usage you see calls per key and endpoint for the last 30 days.
Related
Updated 2 Oct 2026 · Change history (2)