> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://imo.ferndocs.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://imo.ferndocs.com/_mcp/server.

# Authentication

Precision Normalize uses the **OAuth 2.0 client credentials** flow. You exchange
a client ID and secret for a short-lived JWT, then send that JWT as a bearer
token on every request.

## Get your credentials

Request a client ID and secret through the
[IMO Health Developer Portal](https://developer.imohealth.com/). You'll receive:

| Value           | Description                                                              |
| --------------- | ------------------------------------------------------------------------ |
| `client_id`     | Public identifier for your application                                   |
| `client_secret` | Secret — keep it server-side, never in browser or mobile code            |
| `audience`      | The API the token is scoped to, e.g. `https://api-sandbox.imohealth.com` |

> **Warning**
>
> Never ship `client_secret` in client-side code. Exchange credentials on your
> backend and pass only the resulting short-lived token to any edge service.

## With an SDK (recommended)

The generated SDKs implement the token exchange **and refresh** internally. You
provide credentials once; the client fetches a token on first use and renews it
before `expires_in` elapses.

**`TypeScript`**

```typescript title="TypeScript"
import { ImoHealthClient } from "@imo-demo/precision-normalize";

const client = new ImoHealthClient({
  clientId: process.env.IMO_CLIENT_ID!,
  clientSecret: process.env.IMO_CLIENT_SECRET!,
});

// No token handling required — the client manages it.
const session = await client.sessions.create();
```

**`Python`**

```python title="Python"
import os
from imo_precision_normalize import ImoHealth

client = ImoHealth(
    client_id=os.environ["IMO_CLIENT_ID"],
    client_secret=os.environ["IMO_CLIENT_SECRET"],
)

session = client.sessions.create()
```

**`Java`**

```java title="Java"
ImoHealth client = ImoHealth.builder()
    .clientId(System.getenv("IMO_CLIENT_ID"))
    .clientSecret(System.getenv("IMO_CLIENT_SECRET"))
    .build();

SessionResponse session = client.sessions().create();
```

The SDKs read these environment variables by default, so you can also construct
a client with no arguments:

| Variable            | Used for                                         |
| ------------------- | ------------------------------------------------ |
| `IMO_CLIENT_ID`     | OAuth client ID                                  |
| `IMO_CLIENT_SECRET` | OAuth client secret                              |
| `IMO_TOKEN`         | A pre-fetched JWT, if you manage tokens yourself |

## Manually, over HTTP

**`Fetch a token`**

```bash title="Fetch a token"
curl -X POST https://api-sandbox.imohealth.com/oauth/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "client_credentials",
    "client_id": "'"$IMO_CLIENT_ID"'",
    "client_secret": "'"$IMO_CLIENT_SECRET"'",
    "audience": "https://api-sandbox.imohealth.com"
  }'
```

**`Response`**

```json title="Response"
{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6...",
  "scope": "normalize",
  "expires_in": 3600,
  "token_type": "Bearer"
}
```

Then send it on each call:

```bash
curl -X POST https://api-sandbox.imohealth.com/precision/normalize/v1 \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "client_request_id": "demo-001", "requests": [
        { "record_id": "1", "domain": "Problem", "input_term": "chest pain" } ] }'
```

## Scopes

Normalization endpoints require the `normalize` scope, which is granted
automatically to Precision Normalize credentials. The granted scopes come back in
the token response's `scope` field.

## Token errors

`POST /oauth/token` returns an OAuth-shaped error rather than the standard API
error envelope:

```json
{
  "error": "invalid_client",
  "error_description": "Client authentication failed",
  "error_uri": "https://developer.imohealth.com/errors/invalid_client"
}
```

| Error                 | Cause                                                       |
| --------------------- | ----------------------------------------------------------- |
| `invalid_client`      | Wrong `client_id` / `client_secret`, or credentials revoked |
| `invalid_request`     | Missing a required field, or malformed JSON                 |
| `unauthorized_client` | Credentials aren't provisioned for the requested `audience` |

See [Errors and rate limits](/documentation/guides/errors-and-limits) for the
error shape used by the normalization endpoints.