# Authentication

Two credential types exist. Both hit the same API; they differ in what they can do.

## API keys (server-to-server)

Send the key in the `x-api-key` header. Keys are prefixed by environment: `fl_test_…` for sandbox, `fl_live_…` for live. The prefix decides which environment the request touches — there is no env parameter.

| Action | Endpoint | Who |
| --- | --- | --- |
| Create | `POST /apikeys` `{org_id, env, name}` | Session, owner or admin |
| Verify | `GET /apikeys/verify` (with `x-api-key`) | Any holder |
| List | `GET /orgs/{id}/keys` | Session, org member |
| Revoke | `DELETE /orgs/{id}/keys/{key_id}` | Session, owner or admin |

```bash
curl -s https://api.fluense.social/apikeys/verify -H "x-api-key: $KEY"
```

```json
{ "key_id": "019a…", "org_id": "019a…", "env": "sandbox" }
```

Notes:

- The raw key is returned **once** at creation; the server keeps only a one-way digest, never the key itself.
- A key created for `live` cannot be used while the server resolves `sandbox` and vice versa — mismatches return `403 key env mismatch`.
- Keys can be revoked at any time; revocation is immediate.

## Sessions (merchant console)

Email OTP, no passwords.

```bash
curl -s https://api.fluense.social/auth/otp/request \
  -H 'content-type: application/json' -d '{"email":"owner@acme.test"}'

curl -s https://api.fluense.social/auth/otp/verify \
  -H 'content-type: application/json' -d '{"email":"owner@acme.test","code":"123456"}'
```

```json
{
  "session_token": "sess_…",
  "user_id": "019a…",
  "env": "sandbox",
  "live_eligible": false,
  "live_blockers": ["business verification required"]
}
```

Sessions last 30 days. Switch environments with `POST /auth/mode {env, org_id?}` — sandbox always, live only when eligible. `GET /auth/live-eligibility?org_id=` reports `{eligible, blockers}`.

Business registration happens in the console at [business.fluense.social](https://business.fluense.social) — sign up with an email code and the API picks up from your first key; see the [Quickstart](/quickstart).

## Roles

Roles are per organization, ranked: **owner > admin > finance > support > developer**.

| Capability | owner | admin | finance | support | developer |
| --- | --- | --- | --- | --- | --- |
| Manage members and invites | yes | yes | no | no | no |
| Issue and revoke API keys | yes | yes | no | no | no |
| View sales and cashback | yes | yes | yes | yes | yes |
| Create payouts | yes | yes | yes | no | no |
| Configure webhooks | yes | yes | no | no | no |

Use `GET /orgs` to list the organizations a session belongs to, with its role in each.

## Errors

- `401` — missing or invalid credentials.
- `403` — valid credentials, insufficient role or environment mismatch.
