For the complete documentation index, see llms.txt. This page is also available as Markdown.

Gamification

Gamification SDK: client integration

Embed the player's loyalty screen in an iframe. Your backend signs an authentication payload, and your frontend passes it to the iframe. The iframe handles authentication, API calls, and rendering.

1. Get integration details

Request these from the Wynta team:

  • Gamification UI URL — the base URL for the iframe.

  • client_id — your registered client identifier, linked to your site.

  • client_secret — the signing secret. Keep it on your backend only.

Use HTTPS in production. You do not need to send site_id.

2. Backend: create a signed payload

Create an authenticated endpoint on your backend that returns { client_id, payload, hash }. Use the logged-in player's ID from your server session.

Encoding and signing flow

Run all five steps on your backend:

Player claims → JSON string → UTF-8 bytes → Base64 string = payload
                                               │
                           client_secret + HMAC-SHA256
                                               │
                                       lowercase hex = hash

Return { client_id, payload, hash } to your frontend
  1. Build the claims object. Include user_id, timestamp (current Unix seconds), and transaction_id (a new UUID for this attempt).

  2. Convert the object to a JSON string. Use JSON.stringify(claims) in Node.js. For the fixed example below, the exact string is:

  3. Encode that string as Base64. Convert the JSON string to UTF-8 bytes, then encode those bytes using standard Base64, keeping any trailing = padding and adding no line breaks. This produces the payload field:

    Base64 is reversible encoding, not encryption. It does not hide the player's ID.

  4. Sign the Base64 string. Use HMAC-SHA256 with your client_secret as the key and the exact payload string from step 3 as the message. Convert the resulting digest to lowercase hexadecimal to create hash. Do not sign the raw JSON, decode the payload before signing, or include the surrounding JSON quotes.

  5. Return the request body. Add your client_id alongside the unchanged payload and hash. The frontend passes this object to the iframe without re-encoding it. The secret is never included in the body.

Node.js implementation

Example response using the dummy secret example_secret:

The payload decodes to:

The hash is HMAC-SHA256(example_secret, payload) in hex. These values are illustrative; use your registered credentials, the current timestamp, and a new transaction ID for actual requests.

Requirements:

  • Sign the Base64 string, using HMAC-SHA256, and return the hash as hex.

  • Generate the payload when requested; do not cache it. The default timestamp window is 60 seconds, so keep your server clock accurate.

  • Generate a new transaction_id and timestamp for every attempt, including retries.

  • Return only the signed body to the frontend; never return client_secret.

3. Frontend: embed and authenticate

Register the message listener before loading the iframe. When it sends PAGE_LOADED, fetch a fresh signed body from your backend and reply with SDK_AUTH_PAYLOAD.

Do not put authentication data in the iframe URL. Always validate both event.origin and event.source, and send the reply to the exact iframe origin.

Direction
Message

Iframe → your frontend

{ type: 'PAGE_LOADED' }

Your frontend → iframe

{ type: 'SDK_AUTH_PAYLOAD', body: { client_id, payload, hash } }

PAGE_LOADED means the iframe mounted; it does not confirm authentication or data loading. There is currently no ready/success message. The iframe displays its own loading and error states. Your client does not call the SDK auth endpoint or manage the player token.

Troubleshooting

If iframe authentication returns 401 Invalid request, check:

  • The client is active and client_id / client_secret are correct.

  • The HMAC signs the exact Base64 payload string.

  • The timestamp is in Unix seconds and within the allowed window (default 60 seconds).

  • The transaction_id has not already been used.

To retry, reload the iframe and let the handshake request a newly signed payload.

Last updated

Was this helpful?