Skip to main content
Authentication is the first thing your integration does. Souldi uses passwordless email OTP: the end-user receives a 6-digit code by email, exchanges it for a JWT session, and you refresh that session as the access token nears expiry. This page walks through all four /auth endpoints in the order you’ll call them.
New here? The API Overview explains Souldi’s two-layer auth model — the tenant key that identifies your store and the user session that identifies the shopper. Read it first if you haven’t.

Conventions

Every request on this page includes these headers: The base URL is the Souldi API endpoint, https://api.souldi.io. Only /auth/logout needs an additional header: Authorization: Bearer <access_token>.
The x-api-key header authenticates your store on every request. The access_token you receive from OTP verification authenticates the end-user on user-scoped endpoints. Keep the two layers distinct.

The OTP flow

1

Send a code

Collect the user’s email and call POST /auth/otp/send. Souldi emails them a 6-digit code.
2

Verify the code

Collect the code and call POST /auth/otp/verify. You receive an access_token, a refresh_token, and expires_in. Store all of them.
3

Use the access token

Send Authorization: Bearer <access_token> on every user-scoped request (profile, image upload, generation).
4

Refresh before expiry

Shortly before the access token expires, call POST /auth/refresh with the refresh_token to get a fresh pair — no need to make the user log in again.
5

Log out when done

Call POST /auth/logout to end the session, then clear your stored tokens.

POST /auth/otp/send

Sends a 6-digit one-time code to the user’s email address. Call this first, after the user enters their email. Required headers: x-api-key

Body parameters

string
required
The end-user’s email address. The OTP code is sent here.

Response 200

string
A human-readable confirmation that the code was sent.

POST /auth/otp/verify

Exchanges the 6-digit code for a JWT session. On success you receive an access token and a refresh token — store both. Required headers: x-api-key

Body parameters

string
required
The same email address you sent the code to.
string
required
The 6-digit code the user received by email.

Response 200

string
The JWT to send as Authorization: Bearer <access_token> on user-scoped requests.
string
The token you exchange at /auth/refresh for a fresh session.
number
Seconds until the access_token expires. Use it to schedule a proactive refresh.
string
Always "Bearer".
Send access_token as Authorization: Bearer <access_token> on every user-scoped call. Store both tokens securely — see Token handling.

POST /auth/refresh

Exchanges a refresh token for a brand-new session before the current access token expires. Use this to keep the user logged in without prompting for the email code again. Required headers: x-api-key

Body parameters

string
required
The refresh_token from your most recent verify or refresh response.

Response 200

The response shape is identical to /auth/otp/verify.
string
A fresh access token.
string
A fresh refresh token. Both tokens rotate on every refresh.
number
Seconds until the new access_token expires.
string
Always "Bearer".
Both tokens rotate on every refresh — the old refresh_token is consumed. Always persist the new pair from the response, or your next refresh will fail. The official widget refreshes about 60 seconds before expiry.

POST /auth/logout

Ends the user’s session. After this call, clear your stored tokens. Required headers: x-api-key and Authorization: Bearer <access_token> This endpoint takes no body.

Response 200

string
A human-readable confirmation that the session ended.

Token handling

Persist both tokens and refresh proactively. The official widget stores them in localStorage under these keys:
  • Refresh proactively — call /auth/refresh shortly before vto_expires_at (the widget uses a ~60-second margin) rather than waiting for a 401.
  • Rotate the stored pair — every verify and refresh returns a new access_token and refresh_token; overwrite both.
  • On 401 or a "Not authenticated" response — clear the stored session and re-run the OTP flow from /auth/otp/send.
Treat both tokens as secrets. Never send a user’s access_token or refresh_token to any party other than the Souldi API, and never log them.

Next step

User & Image

The user is logged in. Next, make sure they have a base photo — read their profile and upload one if needed.