---
title: API
---


## Introduction

The Sahha API lets you manage user profiles, and send and receive data on demand. Explore endpoints with the API Reference, or interactively test queries in the dashboard with the Query Builder.


{% button-group %}
{% button href="https://api.sahha.ai/api-docs/index.html" size="lg" %}API Reference{% /button %}
{% button href="https://app.sahha.ai/dashboard/query-builder" variant="secondary" size="lg" %}Query Builder{% /button %}
{% /button-group %}

{% image src="connect/api/api-query-builder.webp" alt="Sahha Query Builder" width=768 height=300 / %}

---

## Getting Started

1. **Get an Account Token** — Exchange your `clientId` and `clientSecret` for an account token. 
2. **Create Profiles** — Register user profiles via account token (server-side) or `appId`/`appSecret` headers (client-side) to receive a profile token and refresh token
3. **Access Data** — Use account tokens (server-side) or profile tokens (client-side) to retrieve data

{% callout %}
Find your `clientId` and `clientSecret` or `appId` and `appSecret` in the [Sahha Dashboard](https://app.sahha.ai/dashboard/credentials) under API Keys.
{% /callout %}


### 1. Get an Account Token

Exchange your credentials for an account token.

{% button href="https://app.sahha.ai/dashboard/credentials" %}Get Credentials{% /button %}

{% tabs %}

{% tab label="cURL" %}

```bash {% title="Get Account Token" %}
curl -X POST https://sandbox-api.sahha.ai/api/v1/oauth/account/token \
  -H "Content-Type: application/json" \
  -d '{
    "clientId": "your-client-id",
    "clientSecret": "your-client-secret"
  }'
```

{% /tab %}

{% tab label="JavaScript" %}

```javascript {% title="Get Account Token" %}
const response = await fetch('https://sandbox-api.sahha.ai/api/v1/oauth/account/token', {
	method: 'POST',
	headers: { 'Content-Type': 'application/json' },
	body: JSON.stringify({
		clientId: 'your-client-id',
		clientSecret: 'your-client-secret'
	})
});

const { accountToken, expiresIn } = await response.json();
```

{% /tab %}

{% tab label="Python" %}

```python {% title="Get Account Token" %}
import requests

response = requests.post(
    'https://sandbox-api.sahha.ai/api/v1/oauth/account/token',
    json={
        'clientId': 'your-client-id',
        'clientSecret': 'your-client-secret'
    }
)

data = response.json()
account_token = data['accountToken']
```

{% /tab %}

{% /tabs %}

**Response:**

```json {% title="Account Token Response" %}
{
	"accountToken": "eyJhbGciOiJIUzI1NiIs...",
	"expiresIn": 86400
}
```

### 2. Create a Profile

Register a user profile using your account token:

{% callout title="Just exploring?" %}
You can also create a [Sample Profile](https://app.sahha.ai/dashboard/profiles) in the dashboard or use the [Demo App](https://app.sahha.ai/dashboard/demo-app) to sync your own data to your project.
{% /callout %}

{% tabs %}

{% tab label="cURL" %}

```bash {% title="Register Profile" %}
curl -X POST https://sandbox-api.sahha.ai/api/v1/oauth/profile/register \
  -H "Authorization: account your-account-token" \
  -H "Content-Type: application/json" \
  -d '{
    "externalId": "user-123"
  }'
```

{% /tab %}

{% tab label="JavaScript" %}

```javascript {% title="Register Profile" %}
const response = await fetch('https://sandbox-api.sahha.ai/api/v1/oauth/profile/register', {
	method: 'POST',
	headers: {
		Authorization: `account ${accountToken}`,
		'Content-Type': 'application/json'
	},
	body: JSON.stringify({
		externalId: 'user-123'
	})
});

const { profileToken, refreshToken, expiresIn } = await response.json();
```

{% /tab %}

{% tab label="Python" %}

```python {% title="Register Profile" %}
response = requests.post(
    'https://sandbox-api.sahha.ai/api/v1/oauth/profile/register',
    headers={'Authorization': f'account {account_token}'},
    json={'externalId': 'user-123'}
)

data = response.json()
profile_token = data['profileToken']
refresh_token = data['refreshToken']
```

{% /tab %}

{% /tabs %}

**Response:**

```json {% title="Profile Token Response" %}
{
	"profileToken": "eyJhbGciOiJIUzI1NiIs...",
	"expiresIn": 86400,
	"tokenType": "Profile",
	"refreshToken": "hIw0LuHXHZKMD7bWV0Dl..."
}
```

### 3. Retrieve Scores

Fetch health scores for a profile:

{% tabs %}

{% tab label="cURL" %}

```bash {% title="Get Scores" %}
curl "https://sandbox-api.sahha.ai/api/v1/profile/score/user-123?types=activity,sleep" \
  -H "Authorization: account your-account-token"
```

{% /tab %}

{% tab label="JavaScript" %}

```javascript {% title="Get Scores" %}
const response = await fetch(
	'https://sandbox-api.sahha.ai/api/v1/profile/score/user-123?types=activity,sleep',
	{
		headers: { Authorization: `account ${accountToken}` }
	}
);

const scores = await response.json();
```

{% /tab %}

{% tab label="Python" %}

```python {% title="Get Scores" %}
response = requests.get(
    'https://sandbox-api.sahha.ai/api/v1/profile/score/user-123',
    headers={'Authorization': f'account {account_token}'},
    params={'types': 'activity,sleep'}
)

scores = response.json()
```

{% /tab %}

{% /tabs %}

{% callout %}
With a **profile token**, omit the `externalId` from the path — the token identifies the profile:
`/api/v1/profile/score?types=activity,sleep`
{% /callout %}

**Response:**

```json {% title="Scores Response" %}
[
	{
		"type": "activity",
		"value": 72.5,
		"state": "medium",
		"createdAt": "2024-01-15T10:30:00Z",
		"factors": []
	}
]
```

{% button href="https://app.sahha.ai/dashboard/query-builder" %}Try Query Builder{% /button %}

---

## Environments

All API requests are made against one of two environments:

| Environment    | Base URL                          | Use Case                                      |
| -------------- | --------------------------------- | --------------------------------------------- |
| **Sandbox**    | `https://sandbox-api.sahha.ai`    | Development, testing, and getting started      |
| **Production** | `https://api.sahha.ai`            | Live apps with real user data                  |

{% callout %}
The examples above use the **Sandbox** environment. When you're ready for production, replace `sandbox-api.sahha.ai` with `api.sahha.ai`.
{% /callout %}

---

## Authentication

Sahha uses token-based authentication. You'll work with two types of tokens depending on your use case:

| Token Type        | Use Case                                                                          | Header Format                    |
| ----------------- | --------------------------------------------------------------------------------- | -------------------------------- |
| **Account Token** | Server-to-server operations: create profiles, query any user's data by externalId | `Authorization: account {token}` |
| **Profile Token** | Single-user operations: query own data, update demographics                       | `Authorization: profile {token}` |

{% callout title="Which token should I use" %}

- **Building a backend service?** Use your **Account Token** to manage all profiles and query data by `externalId`
- **Building a client app?** Use **Profile Tokens** so each user can only access their own data
  
{% /callout %}

### Token Details

| Aspect         | Details                                                                              |
| -------------- | ------------------------------------------------------------------------------------ |
| **Expiration** | Tokens expire after 24 hours                                                         |
| **Refresh**    | Use the `refreshToken` from registration to get a new profile token via `/oauth/profile/refreshToken` |
| **Storage**    | Store tokens securely; never expose secrets in client-side code               |

---

## Endpoints Summary

Quick overview of available endpoints.

### Authentication

| Method | Endpoint                               | Description                            | Auth Required                     |
| ------ | -------------------------------------- | -------------------------------------- | --------------------------------- |
| {% badge color="blue" %}POST{% /badge %}   | `/api/v1/oauth/account/token`          | Get account token                      | None (uses clientId/clientSecret) |
| {% badge color="blue" %}POST{% /badge %}   | `/api/v1/oauth/profile/register`       | Register profile, get profile token    | Account                           |
| {% badge color="blue" %}POST{% /badge %}   | `/api/v1/oauth/profile/register/appId` | Register profile via SDK credentials   | AppId/AppSecret headers           |
| {% badge color="blue" %}POST{% /badge %}   | `/api/v1/oauth/profile/token`          | Get profile token for existing profile | Account                           |
| {% badge color="blue" %}POST{% /badge %}   | `/api/v1/oauth/profile/refreshToken`   | Refresh profile token                  | Profile                           |

### Manage Profiles

| Method | Endpoint                               | Description               | Auth Required |
| ------ | -------------------------------------- | ------------------------- | ------------- |
| {% badge color="green" %}GET{% /badge %}    | `/api/v1/account/profile/{externalId}` | Get profile by externalId | Account       |
| {% badge color="green" %}GET{% /badge %}    | `/api/v1/account/profile/search`       | Search profiles           | Account       |
| {% badge color="red" %}DELETE{% /badge %} | `/api/v1/profile`                      | Delete profiles           | Account       |

### Upload Data

| Method | Endpoint                   | Description    | Auth Required |
| ------ | -------------------------- | -------------- | ------------- |
| {% badge color="blue" %}POST{% /badge %}   | `/api/v1/profile/data/log` | Post data logs | Profile       |

### Retrieve Profile Data

| Method | Endpoint                                          | Description                   | Auth Required |
| ------ | ------------------------------------------------- | ----------------------------- | ------------- |
| {% badge color="green" %}GET{% /badge %}    | `/api/v1/profile/score`                           | Get scores                    | Profile       |
| {% badge color="green" %}GET{% /badge %}    | `/api/v1/profile/score/{externalId}`              | Get scores by externalId      | Account       |
| {% badge color="green" %}GET{% /badge %}    | `/api/v1/profile/biomarker`                       | Get biomarkers                | Profile       |
| {% badge color="green" %}GET{% /badge %}    | `/api/v1/profile/biomarker/{externalId}`          | Get biomarkers by externalId  | Account       |
| {% badge color="green" %}GET{% /badge %}    | `/api/v1/profile/archetypes`                      | Get archetypes                | Profile       |
| {% badge color="green" %}GET{% /badge %}    | `/api/v1/profile/archetypes/{externalId}`         | Get archetypes by externalId  | Account       |
| {% badge color="green" %}GET{% /badge %}    | `/api/v1/profile/insight/trend`                   | Get trends                    | Profile       |
| {% badge color="green" %}GET{% /badge %}    | `/api/v1/profile/insight/trend/{externalId}`      | Get trends by externalId      | Account       |
| {% badge color="green" %}GET{% /badge %}    | `/api/v1/profile/insight/comparison`              | Get comparisons               | Profile       |
| {% badge color="green" %}GET{% /badge %}    | `/api/v1/profile/insight/comparison/{externalId}` | Get comparisons by externalId | Account       |

### Profile Metadata

| Method | Endpoint                            | Description          | Auth Required |
| ------ | ----------------------------------- | -------------------- | ------------- |
| {% badge color="green" %}GET{% /badge %}     | `/api/v1/profile/demographic`       | Get demographics     | Profile       |
| {% badge color="orange" %}PUT{% /badge %}    | `/api/v1/profile/demographic`       | Replace demographics | Profile       |
| {% badge color="purple" %}PATCH{% /badge %}  | `/api/v1/profile/demographic`       | Update demographics  | Profile       |
| {% badge color="green" %}GET{% /badge %}     | `/api/v1/profile/deviceInformation` | Get device info      | Profile       |
| {% badge color="orange" %}PUT{% /badge %}    | `/api/v1/profile/deviceInformation` | Update device info   | Profile       |

### Common Query Parameters

| Parameter       | Type     | Description                                |
| --------------- | -------- | ------------------------------------------ |
| `startDateTime` | ISO 8601 | Filter results from this date/time         |
| `endDateTime`   | ISO 8601 | Filter results until this date/time        |
| `types`         | string[] | Filter by type (e.g., `activity`, `sleep`) |
| `categories`    | string[] | Filter by category                         |
| `periodicity`   | string   | Filter by periodicity (`daily`, `weekly`)  |

{% button href="https://api.sahha.ai/api-docs/index.html" %}View Full API Reference{% /button %}

---

## Response Codes

| Code  | Status       | Description                                                         |
| ----- | ------------ | ------------------------------------------------------------------- |
| `200` | OK           | Request successful                                                  |
| `201` | Created      | Profile registered successfully                                     |
| `204` | No Content   | Request successful but no data found for the query                  |
| `400` | Bad Request  | Invalid parameters, malformed request, or externalId already in use |
| `401` | Unauthorized | Missing, invalid, or expired token                                  |

{% callout title="Handling 204 responses" %}
A `204` response means the request was valid but there's no data yet—common for new profiles or date ranges with no activity. Don't treat this as an error.
{% /callout %}

---

## Best Practices

- **Secure your credentials** — Store `clientId` and `clientSecret` in environment variables, never in client-side code
- **Handle token expiration** — Tokens expire in 24 hours; implement automatic refresh logic
- **Use appropriate authorization** — Use account tokens server-side, profile tokens client-side
- **Filter your queries** — Use `startDateTime` and `endDateTime` to limit response size
- **Handle errors gracefully** — Check for `401` (invalid token) and `400` (bad request) responses

---

## Support

Need help? Contact [support@sahha.ai](mailto:support@sahha.ai) or join the [Slack Community](https://join.slack.com/t/sahhacommunity/shared_invite/zt-1w0fmfbvk-qUwQ83tJgXyjT9XSxJvKIw).
