Skip to main content
Before a user can have a wallet in your product, two things happen: your server proves who it is, and the user proves who they are. Both take one call each. This page is the server side of the whole integration.

Keys and tokens

There are only three credentials in the system, and each one has exactly one place it goes. The pattern to remember: your org credential stays on your server. The wallet link is the only thing that travels to the user. The connection token comes back when a user verifies, and your server keeps it.

Authenticate your server

Exchange your credentials for a bearer token. The request is form-encoded, per OAuth:
You get back an access_token with an expires_in. Cache it and refresh when it expires. Whether it’s a sandbox or production token follows the credential, not the URL.

Connect a user

Send the user a one-time code, on email or phone:
external_user_id is optional. It’s your own id for the user, and it comes back on webhooks so you can match them up. Verify the code the user gives you. In sandbox the code is always 111111:
The response is the connection: the user’s user_id, an access_token (the connection token), and a refresh_token. Store all three. The connection.created webhook fires here. Then record the user’s authorization once:
Consent is what makes the user’s wallet available to your product. Wallet links won’t be created without it. With a connected, consented user, your server can create wallet links whenever the wallet should open:
The response has a url. That link opens the user’s wallet on any platform: the web embed, the iOS sheet, a text message, or the hosted page. Links are short-lived; create a fresh one each time rather than storing them.

Keep the session alive

Connection tokens expire. Rotate them with the refresh token:
Each refresh returns a new pair and invalidates the old one, so store what comes back.

Webhooks you will receive

  • connection.created when a user verifies, with your external_user_id on it

When it fails

  • An auth error usually means the wrong credential for the call. Check the table at the top.
  • user_info_required on wallet links means consent was never recorded for that user. Call /connect/consent.
  • subscription_required in production means your organization hasn’t subscribed yet. Sandbox never requires one.
  • invalid_refresh_token means the refresh token was already used or expired. Reconnect the user.

Sandbox behavior

Sandbox never sends a real email or SMS, and the code is always 111111. Sandbox users are isolated: connecting anyone@example.com in sandbox can never touch a real account with that email. Next: Webhooks