> This page is for Documentation.

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

# Authentication

Every 52 Cloud API call is authenticated with an access token issued by the
[Auth API](/api/auth/overview) at `https://auth.fiftytwo.com`.

There are two tokens:

| Token              | Lifetime   | Purpose                                          |
| ------------------ | ---------- | ------------------------------------------------ |
| Access token (AT)  | \~24 hours | Sent on every API request                        |
| Refresh token (RT) | \~30 days  | Exchanged for a new AT without re-authenticating |

Both are JWTs carrying an `exp` claim (a Unix timestamp), so you can read the
exact expiry without calling the API.

## Sign in

**`cURL`**

```bash title="cURL"
curl -X POST https://auth.fiftytwo.com/v1/user \
  -H "Content-Type: application/json" \
  -d '{
    "username": "integration@example.com",
    "password": "YOUR_PASSWORD"
  }'
```

**`Python`**

```python title="Python"
import requests

resp = requests.post(
    "https://auth.fiftytwo.com/v1/user",
    json={"username": "integration@example.com", "password": PASSWORD},
    timeout=30,
)
resp.raise_for_status()
tokens = resp.json()
access_token = tokens["access_token"]
refresh_token = tokens["refresh_token"]
```

**`TypeScript`**

```typescript title="TypeScript"
const resp = await fetch("https://auth.fiftytwo.com/v1/user", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    username: "integration@example.com",
    password: process.env.FIFTYTWO_PASSWORD,
  }),
});
if (!resp.ok) throw new Error(`Auth failed: ${resp.status}`);
const { access_token, refresh_token } = await resp.json();
```

A `200` returns both tokens:

```json
{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
}
```

## Use the token

Send the access token on every request:

```bash
curl https://api.fiftytwo.com/masterdata/articles/v1/chain/1/5701234000019 \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

## Refresh, don't re-sign-in

> **Warning**
>
> Sign-in is rate limited to **15 requests per 60 seconds**. Calling `/v1/user`
> before every API request will get you `429 Too Many Requests`. Cache the
> access token for its full lifetime.

When a call returns `401`, exchange the token pair for a fresh one:

```bash
curl -X POST https://auth.fiftytwo.com/v1/refresh \
  -H "Content-Type: application/json" \
  -d '{
    "accessToken": "EXPIRED_ACCESS_TOKEN",
    "refreshToken": "VALID_REFRESH_TOKEN"
  }'
```

Both tokens must belong to the same session. If the refresh fails, sign in again
with `/v1/user`.

## The recommended loop

#### Sign in once

`POST /v1/user`. Store both tokens somewhere your whole integration can read
them, not per-process, or every worker will burn a sign-in.

#### Read \`exp\` from the access token

Decode the JWT payload and refresh a few minutes before it expires. This
avoids the `401` round trip entirely.

#### Treat 401 as 'refresh, then retry once'

If you would rather react than predict: on `401`, call `/v1/refresh`, then
replay the original request exactly once.

#### Fall back to sign-in

If the refresh also fails, the session is gone. Sign in again, and make sure
only one process does, or you will hit the sign-in rate limit.

> **Note**
>
> A `401` does not always mean "expired". It can also mean the account lacks
> privileges for that endpoint. If refreshing succeeds but the call still
> returns `401`, check the account's permissions rather than retrying.

## Register Sale is different

The [Register Sale API](/api/register-sale/overview) authenticates with an **Auth0-issued**
Bearer JWT rather than a token from `auth.fiftytwo.com`. The header looks
identical; the issuer does not. Ask your FiftyTwo consultant which tenant issues
tokens for your integration.