Sonar uses three token types, each with a specific scope. The right token for a request depends on what it's doing and where the request originates — not just who is making it.
Token Types at a Glance
| Token | Issued by | Lifetime | Scope |
|---|---|---|---|
| API Key | Dashboard | 90 days, rotatable | Full account — any user, any operation |
| User Token | Your backend | 1 hour, renewable | Single user, read-only |
| Mobile Token | Your backend | 5 minutes, single-use | SDK initialization only |
API Key
Your API key gives full account access. Use it for server-to-server operations: creating and deleting users, querying any user's data, configuring webhooks.
Authorization: Bearer sk_live_abc123
API keys come in two variants:
| Prefix | Environment | Behavior |
|---|---|---|
sk_live_ |
Production | Full access to real user data |
sk_sandbox_ |
Sandbox | New users are auto-seeded with 7 days of realistic data and pre-computed scores |
What API keys can do:
| Operation | API Key |
|---|---|
| Create / delete users | ✓ |
| Query any user's health data | ✓ |
| Manage webhooks and events | ✓ |
| Generate User Tokens and Mobile Tokens | ✓ |
Using an API Key
import requests
resp = requests.get(
"https://api.sonarhealth.co/v1/users/usr_abc123/scores",
headers={"Authorization": "Bearer sk_live_abc123"},
)
const resp = await fetch("https://api.sonarhealth.co/v1/users/usr_abc123/scores", {
headers: { Authorization: "Bearer sk_live_abc123" },
});
Rotate API keys every 90 days from the dashboard. Keys older than 90 days are automatically revoked. If a key is compromised (committed to a public repo, logged to a client), rotate immediately — the old key is revoked the moment you generate a new one. After rotating, audit your webhook configurations and any server-side code that references the old key.
User Token
A User Token is a short-lived, read-only JWT scoped to a single Sonar user. Issue one from your backend and pass it to your frontend so clients can query health data directly — without a backend proxy and without ever seeing your API key.
What User Tokens can do:
| Operation | User Token |
|---|---|
GET /users/{user_id}/scores (own user) |
✓ |
GET /users/{user_id}/daily (own user) |
✓ |
GET /users/{user_id}/sleep (own user) |
✓ |
GET /users/{user_id}/workouts (own user) |
✓ |
GET /users/{user_id}/timeseries (own user) |
✓ |
GET /users/{user_id}/devices (own user) |
✓ |
| Any other user's data | — |
| Create / delete users | — |
| Manage webhooks | — |
Issuing a User Token
Your backend generates a User Token for a specific Sonar user, then passes it to the client:
# Your backend — authenticated with API key
import requests
resp = requests.post(
"https://api.sonarhealth.co/v1/auth/user-token",
headers={"Authorization": "Bearer sk_live_abc123"},
json={"user_id": "usr_abc123"},
)
token = resp.json()["token"]
expires_at = resp.json()["expires_at"]
# Pass token to your frontend
// Your backend — authenticated with API key
const resp = await fetch("https://api.sonarhealth.co/v1/auth/user-token", {
method: "POST",
headers: {
Authorization: "Bearer sk_live_abc123",
"Content-Type": "application/json",
},
body: JSON.stringify({ user_id: "usr_abc123" }),
});
const { token, expires_at } = await resp.json();
// Pass token to your frontend
{
"token": "eyJhbGciOiJSUzI1NiIs...",
"user_id": "usr_abc123",
"expires_at": "2025-01-15T11:00:00Z"
}
Using a User Token
The client uses the token exactly like an API key — the Bearer scheme is the same:
// Your frontend — safe to use client-side
const resp = await fetch("https://api.sonarhealth.co/v1/users/usr_abc123/scores", {
headers: { Authorization: `Bearer ${userToken}` },
});
# Mobile app / script — safe to expose in this context
resp = requests.get(
"https://api.sonarhealth.co/v1/users/usr_abc123/scores",
headers={"Authorization": f"Bearer {user_token}"},
)
Refreshing a User Token
Tokens expire after 1 hour. Refresh before expiry to keep the session alive:
resp = requests.post(
"https://api.sonarhealth.co/v1/auth/user-token/refresh",
headers={"Authorization": "Bearer sk_live_abc123"},
json={"user_id": "usr_abc123"},
)
token = resp.json()["token"]
const { token } = await fetch("https://api.sonarhealth.co/v1/auth/user-token/refresh", {
method: "POST",
headers: {
Authorization: "Bearer sk_live_abc123",
"Content-Type": "application/json",
},
body: JSON.stringify({ user_id: "usr_abc123" }),
}).then(r => r.json());
Mobile Token
A Mobile Token is a single-use credential for initializing the Sonar Mobile SDK. It expires in 5 minutes and can only be used once — for the SDK handshake that associates a device with a Sonar user. It is not a REST API credential.
Generate a fresh Mobile Token each time a user opens your app and the SDK needs to connect:
# Your backend — authenticated with API key
resp = requests.post(
"https://api.sonarhealth.co/v1/auth/mobile-token",
headers={"Authorization": "Bearer sk_live_abc123"},
json={"user_id": "usr_abc123", "scopes": ["activity", "sleep", "vitals"]},
)
token = resp.json()["token"]
# Pass to your mobile app — the SDK will use and consume it
// Your backend — authenticated with API key
const { token } = await fetch("https://api.sonarhealth.co/v1/auth/mobile-token", {
method: "POST",
headers: {
Authorization: "Bearer sk_live_abc123",
"Content-Type": "application/json",
},
body: JSON.stringify({ user_id: "usr_abc123", scopes: ["activity", "sleep", "vitals"] }),
}).then(r => r.json());
// Pass to your mobile app
| Parameter | Type | Required | Description |
|---|---|---|---|
user_id |
string | yes | The Sonar user ID (e.g., usr_abc123) |
scopes |
string[] | no | Metric categories to request (default: all) |
{
"token": "mt_xK9mP2...",
"user_id": "usr_abc123",
"expires_at": "2025-01-15T10:05:00Z"
}
The SDK exchanges this token for an internal session during initialize(). After that, the token is consumed and cannot be reused. If the token expires before the SDK uses it, initialize() fails with invalid_token — generate a fresh one from your backend and retry. Since Mobile Tokens are cheap to create (one API call, no state), the simplest pattern is to generate a new one every time your app foregrounds.
See Mobile SDK for how to pass it to the SDK.
Which Token to Use
| Scenario | Token |
|---|---|
| Backend querying a user's scores for a nightly report | API Key |
| Backend creating a new Sonar user on signup | API Key |
| React dashboard displaying the logged-in user's recovery trend | User Token |
| Mobile app showing health scores in-app (without going through your server) | User Token |
| Mobile app initializing the Sonar SDK to start syncing device data | Mobile Token |
Error Responses
| Status | Error | Meaning |
|---|---|---|
401 |
invalid_token |
Token is malformed or does not exist |
401 |
token_expired |
Token has passed its expires_at — refresh or reissue |
403 |
insufficient_scope |
Token does not have permission for this operation (e.g., User Token trying to create a user) |
403 |
wrong_user |
User Token was issued for a different user than the one in the path |
{
"error": "insufficient_scope",
"message": "This token can only access data for user usr_abc123.",
"details": {}
}
Next Steps
- Mobile SDK Where Mobile Tokens are used to initialize device connections
- API Conventions REST conventions, pagination, error handling, and rate limits
Sonar