Partner Credentials & Key Handling
Everything your integration needs comes down to three credentials and two rules. This page is the canonical reference for what each key is, where it lives, and how to handle it safely.
Your tenant gets three things
| Credential | Lives | What it's for |
|---|---|---|
| game_id | Anywhere | Your tenant id. Not secret. |
| SDK secret_key ivsdk_live_… / ivsdk_test_… | Server-side ONLY | Sent as the X-Game-Secret-Key header to mint player tokens and for server-to-server calls. |
| Webhook signing secret whsec_live_… / whsec_test_… | Server-side ONLY | Used to verify the webhooks we send you. It is NOT sent in requests to us. |
Where to find them
- • game_id and the SDK secret_key: on the dashboard's Platform & Keys page (user menu, top-right → Platform & Keys → pick your game). The key has a reveal/copy affordance and a self-serve rotate.
- • Webhook signing secret: shown once when you create the webhook on the dashboard's Webhooks page (and again on each rotation). Store it then; it isn't retrievable later.
Never put a secret in a frontend bundle
Anything in a browser build (Vite/React, etc.) is world-readable. The ivsdk_… secret and the webhook signing secret are server-side credentials. They must never ship to the client.
Two rules that matter
1. The browser never holds the SDK key
Your server mints a short-lived player token; the browser uses that for every /api/sdk/* call.
POST /api/sdk/player-token
Header: X-Game-Secret-Key: <SDK secret_key> // server-to-server ONLY
Body: { "player_email": "user@example.com" } // player must exist in your tenant
→ { "token": "<player token>", // Authorization: Bearer <token>
"expires_at": "...Z", // ~15-min lifetime, NO refresh
"identity_id": "id_…" }
// On a 401 from any /api/sdk/* call → mint a fresh token and retry.2. Verify every webhook
X-Invo-Signature: t=<ts>,v1=<hmac> is HMAC-SHA256 over the literal string "<timestamp>.<raw_body>", keyed with your whole signing secret exactly as issued (prefix included). Verify constant-time, reject if the timestamp is outside a 5-minute window, and de-duplicate on X-Invo-Idempotency-Key.
Full reference + verify snippet: Receiving Webhooks.
Web platforms: passkey step-up
Passkeys run on INVO’s hosted page
Web platforms approve high-value transfers and sends with a passkey. The ceremony runs on INVO’s hosted approval page, so there is nothing to configure: no domain, no keys. Un-enrolled users fall back to QR device approval or hosted approval automatically. See Platform Step-Up (WebAuthn).
Sandbox only: the clock key
A fourth credential, if you test subscriptions
The three above are everything a production integration needs. Sandbox adds one more, and only if you use the subscription time-travel endpoints (the ones that let you make a renewal due, bill it, fail it, or force a step-up challenge without waiting a month). Those calls require X-Sandbox-Clock-Key in addition to X-Game-Secret-Key.
- • Why two. One of those endpoints genuinely charges a test card on demand. A leaked SDK key should not also be a charge-on-demand button, so the second factor is a separate credential. It is deliberately not retrievable using the SDK key, or it would not be a second factor at all.
- • Where to get it. Per game, from the sandbox developer API:
GET /sandbox/api/dev/games/{game_id}/sandbox-clock-key(the first call mints it). A console screen is on the way; until it ships, that endpoint is how you get one. It authenticates with your dashboard session token (Authorization: Bearer …, signed in as the developer who owns the title) and not withX-Game-Secret-Key, which is the point: if the SDK key could fetch this one, the SDK key would again be sufficient on its own. Full walkthrough on Sandbox Testing. - • Rotation. Self-serve at
POST …/sandbox-clock-key/rotate, same session auth. By default the previous key keeps working for a 7-day grace window so a test run already in flight does not break, the same dual-key handling as the SDK key below. A leaked clock key is the exception: send{"immediate": true}to revoke the old value on the spot, or the grace window keeps a charge-on-demand credential live for a week. - • There is no production version of this key, because the endpoints it opens do not exist in production. Server-side only, like every other secret on this page.
Key rotation
Every key rotates self-serve, each with a 7-day grace window
- • The SDK secret_key rotates from the dashboard's Platform & Keys page (user menu → Platform & Keys → your game → Rotate key). The old key keeps working for 7 days so your integration can cut over without downtime. The new key appears in a copy dialog and stays readable on that page afterwards, and the page banners the grace deadline. Pass
{"immediate": true}to revoke the old key at once instead. The new key always startsivsdk_live_orivsdk_test_for its environment, even if the old one had no prefix. - • The webhook signing secret rotates from the dashboard's Webhooks page, also with a 7-day grace window. Deliveries are dual-signed (two
v1=values) so you can deploy the new value without downtime.X-Invo-Secret-Versiontells you which is current. - • The sandbox clock key (above) rotates at
POST /sandbox/api/dev/games/{game_id}/sandbox-clock-key/rotate, also with a 7-day grace window by default. - • If a key is ever exposed, rotate it immediately, and check that "immediately" is what you actually got. For the SDK key, rotate with
{"immediate": true}so the exposed key stops working now (deploy the new key first, or expect failures until you do). For the signing secret, rotate from the console and treat the grace window as the time in which the exposed value still works, so cut over quickly. For the sandbox clock key, the default rotation is not a revocation: pass{"immediate": true}or the leaked key stays live for 7 more days. Don't wait on us, and never share keys in chat or commit them to a repo.