SSOUNDIVA PARTNER LAB
SANDBOX
CHOOSE YOUR INTEGRATION One Sandbox. Two player experiences.

Online platform · browser session

PARTNER ACTIVITY INTEGRATION

Your platform.
One SounDiva account.

Sign in, choose a game and open your SounDiva account without leaving the partner platform.

PARTNER WEBSITE

This page represents your application. Browser actions call its same-origin backend; only the backend talks to SounDiva with provider credentials.

PRIVATE SOUNDIVA ACCOUNT · FIRST PREVIEW

Your account, wherever you play.

Open the account in a modal or use the embedded view below. Personal data and the full wallet stay inside the SounDiva frame. The partner page receives only presentation events.

Loading widget configuration…

BALANCE APPEARANCE

Balance themes

Choose a theme for the header balance and both previews.

Custom colors

Apply your palette to the balance, the account, or both.

Choose colors, then apply them. Reset returns to the selected theme.

Desktop 294 × 80

Mobile 170 × 56

SounDiva balance is required in every theme. Account data stays inside the private SounDiva widgets.

INLINE EMBEDSame account · independent placement

BROWSER ISOLATION CHECK

Run from the partner page's own JavaScript: attempt to access the account frame DOM and private API. Results contain only pass/fail signals.


        
Embed with HTML tags

Load the online DEV SDK directly from SounDiva. Keep this remote URL instead of copying or bundling the loader, so compatible updates arrive on page reload. Configure the partner's server-side OIDC session separately and register your exact website origin with SounDiva.

<script type="module"
  src="https://dev.soundiva.com/widget/sdk/v1.js"></script>

<soundiva-login theme="dark" locale="en"></soundiva-login>
<soundiva-balance balance-theme="brand-dark" size="auto"
  background-color="#171329" surface-color="#25203b"
  text-color="#f7f5ff" muted-color="#aaa2bf"
  accent-color="#a780ff" border-color="#665782"
  locale="en"></soundiva-balance>
<soundiva-profile theme="dark" locale="en"
  background-color="#171329" surface-color="#25203b"
  text-color="#f7f5ff" muted-color="#aaa2bf"
  accent-color="#a780ff" border-color="#665782"></soundiva-profile>

<!-- Or show the same account in a modal. -->
<button id="account-button" type="button">My account</button>
<soundiva-profile id="account-modal" mode="modal"
  theme="dark" locale="en"></soundiva-profile>
<script type="module">
  await customElements.whenDefined('soundiva-profile');
  document.querySelector('#account-button').addEventListener('click', () => {
    document.querySelector('#account-modal').open();
  });
</script>

Change balance-theme or size directly on the balance tag, and theme on the profile tag. Both tags accept the six color attributes above as #RRGGBB; remove them to restore theme colors. Colors update without reloading the widgets. Use .refresh() after settlement and .sessionEnded() after successful server logout. Profile and balances stay inside SounDiva's private frames.

IMPLEMENTATION GUIDE

What to build, what to send, what to expect

The protocol supports games, sweepstakes, contests, quizzes, loyalty experiences, and other approved external activities. SounDiva—not the partner—selects every session budget.

Server-to-serverIdempotentTwo Points sourcesNo balance read

LIVE RUNTIME TARGET

Try the Activity API with your credentials

This guide uses the exact Activity API base configured for the running sandbox. The endpoint URLs below are hydrated from the same runtime state used by the partner backend. The production base is https://activity-api.soundiva.com; do not substitute the website origin.

ACTIVITY API BASE URL Loading runtime target…
Current sandbox configuration
KEYCLOAK PROVIDER TOKEN URL Loading runtime target…
Independent machine-to-machine endpoint

SAFE, SERVER-PROXIED TEST

Run the complete API cycle here

NO TOKEN IN BROWSER

Enter the Keycloak service-account credentials issued to your provider. This page sends them once to its same-origin sandbox backend, which obtains the token from the configured Keycloak URL, opens a session at the separately configured Activity API, and completes it with 0 consumed and 0 awarded points. The unused budget is released. The browser receives only a sanitized step summary—never the provider token.

Provider credentials A confidential, service-account-only Keycloak client for your backend integration.
partnerSubject Identifies the connected member using the pairwise value from your server-side OIDC session.
activityId Selects the activity and policy approved for your provider.
Ready. No values are saved by this page.

Use this form only at https://partners.soundiva.com over HTTPS. Localhost HTTP is allowed only for local development. The request is fixed to the same-origin /api/try-it/run adapter; the Activity API base and Keycloak provider token URL cannot be entered or overridden here. The page does not write these values to local or session storage, and clears the client secret immediately after every attempt.

If an API result is uncertain, submit the same participant and activity again with valid credentials. The server retains and retries the exact pending request; it does not create a second session or switch a pending completion to cancellation. Recovery data lives in this portal process only. Resolve pending runs before restarting it.

CURL fallback: server terminal only—never browser code.

Run these commands only from a trusted backend host or local server terminal. Outside the official form above, never paste provider credentials or access tokens into browser DevTools, frontend JavaScript, a mobile app, or client-visible logs. Store the client secret in a secret manager and redact tokens from observability data.

Replace the three values marked YOUR_…, then enter your provider secret at the hidden terminal prompt. The partnerSubject must be the pairwise subject resolved by SounDiva for this logical partner, and the activityId must already be approved by SounDiva. Production provider tokens are obtained only from https://auth.soundiva.com/realms/soundiva/protocol/openid-connect/token. This example requires curl, jq and openssl.

Loading commands…

To abandon an unfinished activity, use cancel instead of completion for its open session:

Loading commands…

01 · SYSTEM BOUNDARIES

How the integration works

01Partner catalogMember selects a game
→
02Partner backendRequests access server-to-server
→
03SounDiva decisionDenied, or approved + hold
→
04Game iframeLocal play, then one Exit result
Embedded game

Only after approval, HTML is returned by the emulated partner backend with the playable game balance and runs in an iframe without same-origin privileges. It never receives a provider secret, access token, member subject, or writable point amount.

Partner backend

Owns the local session, verified subject, correlation references, idempotency keys, service-account secret, and API calls.

SounDiva

Authorizes a maximum point amount and atomically applies one terminal accounting effect.

The iframe sends intent; the backend sends money-like effects.

The partner page first sends only the selected catalog gameId to its backend. A successful SounDiva open response creates an opaque GET /partner-game/game_... URL; before that there is no iframe. The game channel can request only an allowlisted actionId, exit, cancel, or new. The parent validates the exact source window before calling its same-origin backend. The backend—not browser JavaScript—creates correlation and idempotency identifiers, issues a one-time commandId, validates each action against server-owned game state, deduplicates retries, and freezes actions during finalization. Local actions never call SounDiva: the backend calls Keycloak plus open before the iframe, and exactly one complete call when the member exits.

02 · SIGN-IN, BALANCE AND PROFILE

Add hosted SounDiva widgets

The updated widgets are available on DEV. Load one public module directly from SounDiva, then place the HTML tags where you need sign-in, the account balance or the player profile. The partner runs its own frontend and backend; SounDiva serves the private widget frames and the same account page used on its website.

<script type="module"
  src="https://dev.soundiva.com/widget/sdk/v1.js"></script>

<soundiva-login locale="en"></soundiva-login>

<soundiva-balance balance-theme="brand-dark" size="auto"
  locale="en"></soundiva-balance>

<soundiva-profile theme="dark" mode="inline"
  locale="en"></soundiva-profile>
Always load the online SounDiva URL.

Keep this remote script include in your website. Do not download, self-host, proxy or bundle a local copy of the loader. Keep the URL external to your bundler and service-worker precache; do not pin changing SDK bytes with an SRI hash. SounDiva publishes compatible updates at the same v1.js URL, so your next page load gets them without a partner deployment. An open page keeps its loaded code until reloaded. Breaking API changes use a new major version URL.

Put <soundiva-balance> in a header, sidebar, game screen or another page container. size="auto" adapts between desktop and compact presentations. Points are Bonus Points; Tickets are Winnings Points. The SounDiva balance signature is required in every theme and in the signed-out state. The balance opens the account; a separate profile tag can also render it inline or in a modal.

Balance presets are neutral, dark, light, brand-neutral, brand-dark and brand-light. Balance and profile support optional background-color, surface-color, text-color, muted-color, accent-color and border-color, each an opaque #RRGGBB value. The sign-in button keeps SounDiva's canonical styling. Try the themes, colors and inline/modal account in the SounDiva account demo.

PROFILE_WIDGET_URL=https://dev.soundiva.com/widget
SOUNDIVA_WIDGET_URL=https://dev.soundiva.com/widget
OIDC_CLIENT_ID=<your interactive client ID>
OIDC_CLIENT_SECRET=<server-only secret>
AUTH_SESSION_SECRET=<partner-generated secret>

The HTML tags infer the widget URL from the hosted script and need no OIDC credentials. The two URL settings above configure the separate reference applications. Use the URLs issued for your environment. Before using your own website, have SounDiva register its exact origin and interactive OIDC client.

Keep your backend's OIDC session and binding routes

One user sign-in creates separate partner and SounDiva widget token sessions, each refreshed automatically on its own server. Your backend retains /api/auth/* and implements /api/widget-auth/login and /api/widget-auth/complete as in this reference. Validate the single-use bindingId transaction and finish your own Authorization Code + S256 PKCE callback before binding the verified partner session server to server:

POST https://dev.soundiva.com/widget/api/partner/bind
Authorization: Bearer <partner interactive access token>
Content-Type: application/json

{ "bindingId": "<verified one-use binding ID>" }

EXPECT · 200
{ "requestId": "<opaque request ID>" }

Before clearing your partner session, call POST https://dev.soundiva.com/widget/api/partner/logout with the same verified interactive bearer credential and JSON {}; expect 204. Preserve the partner session if linked revocation fails. SounDiva checks the registered client, subject and SSO session. Neither endpoint returns private profile fields, points or widget credentials.

The public soundiva-auth-success and soundiva-auth-signed-out events contain no account data. Use them to recheck your own backend status. After confirmed settlement, call the balance tag's .refresh(); after confirmed server-side logout, call .sessionEnded(). The partner frontend cannot read the SounDiva frame DOM, profile or global balance. Activity budgets remain a separate backend API.

Your CSP must allow the exact SDK URL in script-src and https://dev.soundiva.com in frame-src. The current loader inserts inline presentation styles, so its style-src policy must permit those. No private API CORS access is needed; the parent can keep connect-src 'self'.

Independent partner credentials.

Your interactive OIDC client uses the restricted partner-auth-api audience. A separate provider service client uses client_credentials for Activity API calls. Both secrets remain on your backend. SounDiva owns the widget's confidential client and its private account access.

This DEV release displays the current profile and Points/Tickets. Unsupported account edits, responsible-play changes, purchases, history and subscriptions remain unavailable. The bundled soundiva-sign-in artifact is retained for explicit offline and legacy examples; use the online soundiva-login tag for this integration.

PHONE SIGN-IN · OPTIONAL

QR sign-in for websites and kiosks

The hosted SounDiva login can offer phone sign-in with qr-login, or qr-login="only" on a kiosk. Keep the same online SDK; SounDiva renders the QR and private account inside its iframe.

Online platform uses the sign-in button without QR. Game machine uses QR without a sign-in button. The experience selector preserves the current player, waiting sign-in and game. The public machine emulator remains a web session; the server determines the actual channel. Settle with Exit, then choose End session for the next player.

SANDBOX_QR_BROKER=true
SANDBOX_QR_CHANNEL=web
# Isolated loopback kiosk demo only (never the public Sandbox):
# SANDBOX_TERMINAL_MACHINE_ID=<registered machine ID>

The backend fixes the channel and machine. Browser commands carry only a public widget request reference and statuses. Device secrets, private profile data and full balances stay with SounDiva.

Terminal demo mode requires loopback backend and frontend addresses. A public kiosk gateway needs its own verified per-device adapter; a machine ID is not a credential. Enable terminal games after SounDiva confirms Activity API support for partner sessions. The local vendored SDK remains available for explicit legacy examples; use the hosted component for QR.

03 · AUTHORIZE THE ACTIVITY

Get a Keycloak service token, then open one session

Both calls originate from your backend, but they have different destinations. Keycloak authenticates the provider service; The OIDC identity endpoint separately identifies the member. The browser never chooses a point amount. Your backend sends only the approved activity, pairwise participant correlation, and your participation reference to the Activity API.

Service account and required token claims.

SounDiva provisions a confidential client with service accounts enabled and browser/end-user grants disabled. The RS256 token has aud: platform-activity-api and permissions in resource_access.platform-activity-api.roles: activity:open and activity:complete. activity:read is reserved and has no provider route in the current contract; do not request it unless SounDiva enables a future read surface. The edge verifies its signature and claims locally against Keycloak JWKS. The request names optional OAuth scopes; Keycloak maps approved scopes to these roles, and the edge authorizes from the roles rather than the plain scope claim.

POSTLoading runtime target…Provider authentication
Authorization: Basic <clientId:clientSecret>
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
scope=activity:open activity:complete
{
  "access_token": "<server-only>",
  "token_type": "Bearer",
  "expires_in": 300
}
POST/api/v1/activity-sessionsAccess decision + atomic hold
Authorization: Bearer <provider access token>
Content-Type: application/json

{
  "activityId": "partner-activity:demo",
  "partnerSubject": "sdpa_<pairwise-subject>",
  "externalRef": "participation-123",
  "idempotencyKey": "open-<unique-id>"
}
{
  "sessionId": "asession_...",
  "activityId": "partner-activity:demo",
  "sessionBudgetPoints": 1000,
  "balanceMode": "combined",
  "resultMode": "partner_balances",
  "spendingMode": "bonus_first",
  "sessionBalances": { "bonusPoints": 700, "winningsPoints": 300 },
  "expiresAt": "...",
  "status": "open"
}
FieldMeaningRule
activityIdApproved activity identifierMax 128 chars / 512 UTF-8 bytes
partnerSubjectPairwise partner correlationRequired; backend only; max 256 / 512 bytes
externalRefYour evidence / participation referenceUnique per provider + activity; max 128 / 512
idempotencyKeyIdentity of this exact operationUnique and persisted; max 128 / 512
201 means “yes” and the hold already exists.

This route is both the eligibility decision and the hold; do not add a separate check that could race. SounDiva selects the budget from available points and the approved profile. Only one of the five business-denial codes below is “no”, creates no new hold, and returns a stable public { code, message } reason that your backend may pass to the member unchanged. A 201 exposes only sessionBudgetPoints and the reserved source split in sessionBalances, never the account or global balance. Retain the returned resultMode, balanceMode and spendingMode for the entire session. Use expiresAt to prevent play after the hold expires.

Other 4xx responses are validation, expiry, or idempotency outcomes. They are not access decisions and may require exact-request recovery.

HTTP 400  { "code": "insufficient_member_points", ... }
HTTP 404  { "code": "activity_not_available", ... }
HTTP 404  { "code": "partner_subject_not_linked", ... }
HTTP 409  { "code": "account_not_active", ... }
HTTP 409  { "code": "activity_session_limit_reached", ... }
Identifier limits are checked before storage.

activityId, externalRef, resultRef, idempotencyKey, and path sessionId allow 128 Unicode characters and 512 UTF-8 bytes. partnerSubject allows 256 characters and 512 bytes. They must be well-formed Unicode without control characters. Any violation returns 400.

04 · PARTNER-OWNED PROCESS

Run gameplay on your backend

Play steps, quiz answers, prize reveals, contest transitions, and other activity events remain in your system. You do not send one SounDiva transaction per event.

Partner event 1local
Partner event 2local
Partner event Nlocal
→
One final resultAPI
Point calculation belongs to your backend.

The member browser may submit an action identifier, never a point delta or final counters. Your backend applies its rule version, stores the resulting evidence, and is the only component that sends final balances or financial rounds to SounDiva.

Keep evidence even when calls are aggregated.

Store your internal event IDs, timestamps, rule version, outcome source, and externalRef so the final result can be reconstructed during audit or abuse review.

05 · ONE TERMINAL EFFECT

Complete the session or cancel

When the member presses Exit, your backend derives and sends one completion using the resultMode returned at open. For partner_balances, send final bonusPoints and winningsPoints in result: {type: "balances", ...}. For round_history, send result: {type: "history", rounds: [...]} with every closed round in order. This applies to both combined and separate game displays. Only legacy_aggregate accepts the old consumedPoints / awardedPoints format. For either new result mode, use its example below. Never turn a new session into one net delta: losing bonuses and winning the same amount changes the composition of Points.

One displayed balance still has two sources.

balanceMode: combined shows one total; separate shows Bonus and Winnings Points. Both result methods work with either display mode. Bonus Points come from SounDiva rewards; new game winnings go to Winnings Points. Games cannot create new Bonus Points.

bonus_first spends bonus before winnings; winnings_first uses the reverse order. bonus_only and winnings_only restrict the permitted stake source. Explicit funding cannot override these restrictions. A win can create Winnings Points even when their starting reserve is zero.

Try It and the default demo have zero Points effect.

With SANDBOX_ALLOW_NONZERO_RESULTS=false, local demo moves do not change the real balances. Send unchanged starting balances for partner_balances, empty rounds: [] for round_history, or legacy 0/0. Try It always uses this safe completion. Enabling the demo flag does not grant nonzero permission at SounDiva.

Authorization: Bearer <provider access token>
Content-Type: application/json
POST/api/v1/activity-sessions/{sessionId}/completeAtomic terminal result

These examples start with 700 Bonus Points and 300 Winnings Points, using bonus_first. A 200-point stake pays 450 (including the stake), then a 100-point stake loses. Final balances are 400 Bonus Points and 750 Winnings Points. Send only the format selected by the opened session.

{
  "result": { "type": "balances", "bonusPoints": 400, "winningsPoints": 750 },
  "resultRef": "partner-result-123",
  "idempotencyKey": "complete-<unique-id>"
}
{
  "result": {
    "type": "history",
    "rounds": [
      { "roundId": "r1", "stakePoints": 200, "payoutPoints": 450 },
      { "roundId": "r2", "stakePoints": 100, "payoutPoints": 0 }
    ]
  },
  "resultRef": "partner-result-123",
  "idempotencyKey": "complete-<unique-id>"
}
{
  "sessionId": "asession_...",
  "status": "completed",
  "sessionBalances": { "bonusPoints": 400, "winningsPoints": 750 },
  "consumedPoints": 300,
  "awardedPoints": 450,
  "releasedPoints": 700
}

Variant A: debit the entire stake from its sources and credit all payoutPoints to Winnings Points, including the stake component. Only a cancelled round refunds sources. Optional funding: {bonusPoints, winningsPoints} specifies actual stake sources. With cancelled: true, the payout must equal the full stake and refund both sources. Send unique round IDs, only closed rounds, at most 10,000 rounds and 4 MiB JSON. The demo itself allows 200 actions.

Compatibility with existing legacy profiles

Only legacy_aggregate sessions use consumedPoints and awardedPoints in the request. A legacy separate session also requires per-source balanceResults. Do not mix those fields with result or switch format on retry.

{
  "consumedPoints": 0,
  "awardedPoints": 0,
  "balanceResults": {
    "bonus": { "consumedPoints": 0, "awardedPoints": 0 },
    "winnings": { "consumedPoints": 0, "awardedPoints": 0 }
  },
  "resultRef": "partner-result-123",
  "idempotencyKey": "complete-<unique-id>"
}

For legacy combined mode, omit balanceResults.

Completion counters describe settlement effects.

For new result modes, the response also contains final sessionBalances. Consumed and awarded counters are net effects by source, not total stake or payout turnover. consumedPoints may not exceed the session budget; awardedPoints may not exceed the profile award cap. resultRef links the final result to your local evidence, is globally unique per provider, and has the same 128-character / 512-byte bound. Reusing either correlation in its scope returns 409. Unused budget is released atomically.

POST/api/v1/activity-sessions/{sessionId}/cancelCancel an unfinished session
Authorization: Bearer <provider access token>
Content-Type: application/json

{ "idempotencyKey": "cancel-<unique-id>" }
{
  "sessionId": "asession_...",
  "status": "cancelled"
}

Cancellation releases the entire unused budget without a points effect.

06 · OPERATIONS

Retries, idempotency, and failures

01

Persist before sending. Generate the idempotency key once and store it with the exact request body.

02

Retry the same operation unchanged. Reuse both key and payload after a disconnect, timeout/ 408, 429, or 5xx.

03

Never recycle a key. The same key with a different payload returns 409 Conflict.

04

Treat replay as success. X-SounDiva-Idempotency-Replayed: true means the original committed response was returned.

05

Refresh provider authentication. On an expired token, obtain a new token and retry with the original operation key.

06

Stop on authoritative expiry. HTTP 400 with activity_session_expired is not transient. For a late open retry, clear that attempt and start with a new externalRef and key. For complete or cancel, mark the session expired, clear the pending terminal operation, and do not retry it.

{
  "code": "activity_session_expired",
  "message": "The activity session is expired."
}
400 invalid input / insufficient points / authoritative expiry 401 invalid or expired provider token 403 missing activity service-account role 404 member or provider session not found 409 idempotency or terminal-state conflict 408 / 429 / 5xx retry unchanged

07 · READY TO INTEGRATE

Partner checklist

  • Mount the shared SounDiva auth SDK on your backend.
  • Load the online SounDiva SDK and place <soundiva-login>, <soundiva-balance> and <soundiva-profile> where needed. Do not keep a local copy of the loader.
  • Register the exact website origin and interactive client with SounDiva. Complete the backend binding and linked logout flow.
  • Keep identity correlation, secrets, and access tokens server-side.
  • Use the service-account-only Keycloak client issued by SounDiva and verify it has the required Activity API roles.
  • Persist externalRef, session IDs, idempotency keys, and internal evidence.
  • Open one session without sending an amount; use the returned budget, source balances and modes throughout that session.
  • Preserve ordered financial rounds and send exactly one result in the opened format. Finish before expiry and limits.
  • Implement unchanged retry and explicit cancellation.
  • Pass all contract tests before production credentials are enabled.

ACTIVITY CONTRACT CERTIFICATION

Sixteen required scenarios

This in-memory suite follows the opened result format and retains redacted evidence for authentication, holds, completion, retries and correlation references. Automated HTTP tests also cover both game displays and completion methods. Verify your approved profile on the real DEV environment before partner rollout.