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.
Loading runtime target…
Loading runtime target…
SAFE, SERVER-PROXIED TEST
Run the complete API cycle here
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.
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.
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
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.
Owns the local session, verified subject, correlation references, idempotency keys, service-account secret, and API calls.
Authorizes a maximum point amount and atomically applies one terminal accounting effect.
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>
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'.
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.
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"
}
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
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", ... }
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.
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.
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.
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.
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.
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
Persist before sending. Generate the idempotency key once and store it with the exact request body.
Retry the same operation unchanged. Reuse
both key and payload after a disconnect, timeout/
408, 429, or 5xx.
Never recycle a key. The same key with a
different payload returns 409 Conflict.
Treat replay as success.
X-SounDiva-Idempotency-Replayed: true means the
original committed response was returned.
Refresh provider authentication. On an expired token, obtain a new token and retry with the original operation key.
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."
}
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.