Web SDK authentication

Authentication is how your backend lets the Web SDK act on behalf of an account holder without ever exposing your credentials in the browser. This guide covers both supported flows, so you can pick the right one and, if needed, migrate.

Before you begin

What you'll do

  1. Choose a flow. New integrations should use the new flow.
  2. Mint a token on your backend and pass it to the iframe.
  3. Handle the session lifecycle.

The Web SDK supports two authentication flows. Pick one per environment.

AspectNew flow (recommended)Legacy flow (deprecated)
Secrets you manageOAuth client_id / client_secret onlyOAuth credentials plus a customer-specific encryption secret
OAuth token type usedClient-level (no sender_id)Sender-level (with sender_id)
Backend endpoint customer callsPOST /v1/oauth/sla-tokenNone (customer encrypts locally)
Token passed to iframeslaToken (single-use, short-lived)resources + customerId
Session lifetimeBounded, sliding (see Session lifecycle)Lifetime of the encrypted token

Use the New flow for new integrations. If you have an existing integration on the legacy flow, see Migration for the cutover playbook.

New flow (recommended)

The recommended flow is a three-step sequence: your backend obtains a client-level OAuth access token, exchanges it for an SLA token bound to a specific sender, and your frontend passes that SLA token to the iframe.

Step 1: Obtain a client-level access token

In your backend, mint a client-level OAuth access token using the standard client_credentials grant. A client-level token is obtained by calling the Get Access Token endpoint without a sender_id in the request body, see the Authentication guide for full details.

curl --request POST \
     --url https://sandbox-api.readyremit.com/v1/oauth/token \
     --header 'accept: application/json' \
     --header 'content-type: application/json' \
     --data '
{
     "client_id": "{{client_id}}",
     "client_secret": "{{client_secret}}",
     "audience": "https://sandbox-api.readyremit.com",
     "grant_type": "client_credentials"
}'
📘

Why no sender_id here?

Unlike the legacy flow, do not include sender_id when obtaining the access token you'll use against the SLA-token endpoint. The senderId is supplied to the SLA-token endpoint instead, which lets one client-level token mint SLA tokens for any sender in your account.

Step 2: Request an SLA token

In your backend, exchange the client-level access token for an SLA token bound to a specific sender. The SLA-token endpoint is co-located with POST /v1/oauth/token:

  • Sandbox: POST https://sandbox-api.readyremit.com/v1/oauth/sla-token
  • Production: POST https://api.readyremit.com/v1/oauth/sla-token
curl --request POST \
     --url https://sandbox-api.readyremit.com/v1/oauth/sla-token \
     --header 'accept: application/json' \
     --header 'authorization: Bearer {{client_level_access_token}}' \
     --header 'content-type: application/json' \
     --data '{ "senderId": "{{sender_id}}" }'

A successful response returns 201 with:

{
  "slaToken": "9f1c8e…",
  "expiresIn": 60
}
PropertyDescriptionType
slaTokenSingle-use bearer credential. The Web SDK consumes it on initialization.string
expiresInToken lifetime in seconds. The token must be passed to the iframe within this window.integer

Operational constraints:

  1. Mint on your backend only. The endpoint requires your client level access token; never call it from the browser.
  2. Single-use. The token is invalidated once the iframe consumes it. A page refresh requires a fresh SLA token.
  3. Short-lived. Mint on demand, immediately before initializing the iframe.
  4. One token per sender. senderId is required and bound to the resulting session.

Step 3: Pass the SLA token to the iframe

Deliver the slaToken from your backend to your frontend over an authenticated, HTTPS-only response, then pass it to the SDK's init call:

window.rremit.init(
  {
    sdkCore: {
      slaToken,           // returned from your backend's /v1/oauth/sla-token call
      language: "en-US"
    },
    sdkFeatures: {
      darkMode: false,
      confetti: true
    }
  },
  async (eventPayload) => {
    // …same create-transfer callback as today…
  }
);

The create-transfer callback contract, window.rremit.completed(transferId), and window.rremit.failed(error) are unchanged from the legacy flow. See Iframe integration for the full iframe wiring (helper instance, iframe element, callback handling).

Session lifecycle

  • The SDK consumes the slaToken immediately on load and establishes its own session. You do not see or store a session identifier; the session is internal to the iframe.

  • Sessions time out after a period of inactivity. You can set the session lifetime with sdkCore.sessionTtl (integer seconds), up to a maximum PayMitto applies.

  • The session extends automatically while the user is active in the iframe. No customer action required.

  • Extending the session from outside the iframe. If your host page expects activity that the iframe cannot see (for example, the user is interacting with your host shell rather than the SDK), call window.rremit.activity() to extend the SDK session.

  • Session expiry. When the session ends (whether by inactivity or by your host calling window.rremit.logout()), the SDK posts readyremit-logout to the host. Register a handler to react:

    window.rremit.on("logout", () => {
      // e.g. remove the iframe from the DOM, navigate user back to your shell
    });
  • Re-initializing after expiry. To resume after a logout, mint a new SLA token from your backend and call window.rremit.init(...) again with the fresh token.

Tokens at a glance

TokenNotes
Client level access tokenThe expires_in value in the token response tells you how long it lasts. Request a new one before it expires.
SLA tokenSingle-use. Request it right before you initialize the iframe.
SDK sessionTimes out after inactivity

Security considerations

  • Treat the SLA token as a bearer credential, even though it is single-use. Serve it to the browser only over HTTPS, in the response body of an authenticated request to your own backend, and never log it.
  • Do not retry the SLA-token call from the browser on failure. Re-mint on your backend instead.
  • Use a client-level OAuth token (no sender_id) when calling the SLA-token endpoint. This avoids issuing sender-scoped tokens through middle tiers.
  • Call the SLA-token endpoint at https://sandbox-api.readyremit.com (sandbox) or https://api.readyremit.com (production). The iframe host (sandbox-sdk.readyremit.com / sdk.readyremit.com) is a separate domain and is not the backend target.

Error responses

The SLA-token endpoint returns the following customer-actionable errors:

HTTPerror bodyCauseWhat to do
400senderId requiredRequest body missing senderId.Add the senderId field.
401missing_or_invalid_authorizationAuthorization header absent or not Bearer ….Re-mint your client-level OAuth token and retry.
401invalid_tokenBearer token rejected (expired, malformed, or revoked).Re-mint your client-level OAuth token and retry.
401unknown_customerToken verified but the associated client is not provisioned for the Web SDK.Contact PayMitto support.

If the iframe shows a session-expired view after a successful SLA-token mint, treat it as session expiry and re-initialize the SDK with a fresh SLA token (see Session lifecycle).

Legacy flow (deprecated)

🚧

Deprecated

This authentication mechanism (encrypted resources + customerId) is supported for existing v4.0+ integrations but no longer recommended for new integrations. PayMitto will continue accepting x-resources until further notice; a removal date has not been set. New integrations should use the New flow. Existing customers should plan a migration, see Migration.

Note: this is unrelated to the pre-v4.0 SDK callout at the top of this page, which covers a different (older) Web SDK version.

The legacy authentication flow is a three-step sequence: your backend obtains a sender-level OAuth access token, encrypts it with a customer-specific secret to produce the resources string, and your frontend passes that string (along with customerId) to the iframe.

Step 1: Obtain a sender-level access token

In your backend, mint a sender-level OAuth access token by calling the Get Access Token endpoint with sender_id in the request body. See the Authentication guide for full details on sender-level vs. client-level tokens.

curl --request POST \
     --url https://sandbox-api.readyremit.com/v1/oauth/token \
     --header 'accept: application/json' \
     --header 'content-type: application/json' \
     --data '
{
     "client_id": "{{client_id}}",
     "client_secret": "{{client_secret}}",
     "audience": "https://sandbox-api.readyremit.com",
     "grant_type": "client_credentials",
     "sender_id": "{{sender_id}}"
}'

Step 2: Encrypt the access token (resources)

In your backend, encrypt the access token using a customer-specific encryption secret provided by PayMitto during onboarding. The encryption input is a JSON object of the form:

{ "token": "{{access_token}}" }

Encrypting this payload yields the opaque resources string passed to the iframe.

Secret management is your responsibility. Store the customer-specific secret in your secrets manager and never embed it in frontend code or commit it to source control. PayMitto Technical Support provides encryption examples on request.

Step 3: Pass resources to the iframe

Pass resources and customerId to the iframe via sdkCore instead of slaToken. The rest of the integration (iframe element, helper instance, create-transfer callback) is identical to the new-flow example shown in Iframe integration. Only the sdkCore fields differ:

const customerId = "<this will be provided by PayMitto>";
const resources = "<encrypted resources, generated in your backend>";

window.rremit.init(
  {
    sdkCore: {
      customerId,
      resources,
      language: "en-US",
    },
    sdkFeatures: { darkMode: false, confetti: true }
  },
  async (eventPayload) => {
    // …same create-transfer callback as the new flow…
  }
);

Resources/Token security

❗️

Resources/Token security

In the wrong hands, the PayMitto API access token used in the legacy flow can be used to view personal information about your users or create fraudulent transfers. Be sure to encrypt the token in your backend and only send the encrypted resources string to the frontend.

If you suspect your customer-specific encryption secret has been exposed, contact PayMitto support to rotate it.

Migration

This section is for existing customers cutting over from the legacy encrypted-resources flow to the SLA-token + session flow within Web SDK v4.0+.

What changes

Before (legacy)After (new)
OAuth token type mintedSender-level (sender_id in body)Client-level (no sender_id in body)
Encryption stepYour backend encrypts { "token": ... } with a customer-specific secretNone. Exchange the OAuth token for an SLA token via POST /v1/oauth/sla-token
Fields in sdkCoreresources + customerIdslaToken
Secrets you manageOAuth client_id / client_secret plus a customer-specific encryption secretOAuth client_id / client_secret only

What stays the same

  • Iframe src URL and embedding pattern.
  • The window.rremit helper API (init, completed, failed, on, activity, logout, postMessage).
  • All postMessage events visible to the host (readyremit-create-transfer, readyremit-transfer-completed, readyremit-transfer-failed, readyremit-unload-iframe, readyremit-logout).
  • All sdkFeatures (including customStyles theming), sdkRtaf, and virtualSenderAccounts configuration.
  • The create-transfer callback shape (the eventPayload your backend submits to the PayMitto transfer API).
  • The OAuth client_credentials mechanism for obtaining access tokens (only the body changes; no sender_id in the new flow).

Migration checklist

  1. Verify your OAuth client supports client-level tokens (your client_id is allowed to mint without sender_id). If unsure, run the cURL sample from Step 1 of the New Flow against the sandbox. If you receive an access token, you're set.
  2. Add an SLA-token mint endpoint in your backend. Implement a backend route that authenticates your user, retrieves the user's senderId, obtains a client-level OAuth token, and POSTs to /v1/oauth/sla-token. Cache or re-mint the OAuth token per your existing strategy.
  3. Update your frontend to call the new mint endpoint and pass slaToken to the iframe. Replace the resources + customerId pair in sdkCore with slaToken.
  4. Remove the encryption step from your backend and decommission the customer-specific encryption secret from your code paths (you can leave the secret in your secrets manager until cutover is fully validated).
  5. Add a window.rremit.on("logout", …) handler to react to session expiry (if not already in place).
  6. Test end-to-end in sandbox. Verify a full transfer flow: SDK initializes, user completes a transfer, create-transfer callback fires, transfer posts, confirmation renders.
  7. Roll out per-environment. Sandbox → production. Both flows are accepted on your backend concurrently, so a partial rollout is safe.

Rollback

Both flows are accepted concurrently by PayMitto, so reverting is a frontend-only change: ship a hotfix swapping slaToken back to resources / customerId in your sdkCore. No coordination with PayMitto is required.

FAQ

Do I need a new PayMitto account or new credentials?
No. Use the same client_id / client_secret. Only the OAuth call's request body changes (omit sender_id).

What happens to my customer-specific encryption secret?
You can stop using it after migration. PayMitto will retire customer-specific encryption secrets only after the legacy flow is removed.

Can I support both flows from one frontend?
Technically yes (omit slaToken or resources / customerId from sdkCore based on configuration), but it is not recommended. Pick one flow per environment.

My users have long sessions in my host app. Will their SDK session expire mid-flow?
The SDK session extends automatically while the user is active in the iframe. If you expect long idle periods inside the iframe or activity only in your host shell, call window.rremit.activity() on host-side user interaction to extend the SDK session.

Next steps


Did this page help you?