# Authentication

> Which credential each part of the Coritan API accepts, how to get and refresh an access token, and what API keys do today.

Source: https://docs.coritan.com/api/authentication/

Each route of the Coritan API takes one kind of credential, and the part of the API it belongs to decides which. This page lists each credential, the header it goes in, how you get it and how long it lasts. A few routes take none, such as [`GET /auth/turnstile`](/api/reference/client/authentication/#op-get-api-v1-auth-turnstile) and the list of locations.

## Credentials by part of the API

| Part of the API | Paths | Credential | Header |
| --- | --- | --- | --- |
| Client API | `/api/v1/...`, except the paths below | Your access token | `Authorization: Bearer <token>` |
| Organization API | `/api/v1/orgs/{org_slug}/...` | A member's access token, or a staff console token | `Authorization: Bearer <token>` |
| Customer portal and storefront checkout | `/api/v1/orgs/{org_slug}/portal/...`, and the storefront's order and checkout routes | A customer token | `Authorization: Bearer <token>` |
| Commerce API | `/api/v1/orgs/{org_slug}/commerce/...` | A member's access token or staff console token, or an organization API key | `Authorization: Bearer <token>` or `X-API-Key: <key>` |
| Store API | `/api/v1/orgs/{org_slug}/store/...` | The store's publishable key, plus a customer token on the routes for a signed-in shopper | `x-publishable-api-key: <key>`, and `Authorization: Bearer <token>` |
| SMTP Relay send API | `/api/v1/mail/send` and `/api/v1/mail/send/limits` | A send API key | `X-Api-Key: <key>` |

[Access and refresh tokens](#access-and-refresh-tokens), [Organization credentials](#organization-credentials) and [Other keys](#other-keys) describe each credential.

## Access and refresh tokens

Sign in with [`POST /auth/login`](/api/reference/client/authentication/#op-post-api-v1-auth-login), sending `email` and `password` as JSON. [Make your first API request](/get-started/first-steps-with-the-api/) walks through it with curl. The answer holds `access_token`, `refresh_token`, `token_type` (`bearer`) and `expires_in`.

Access token
: Proves who you are on every Client API request, and on the Organization API for each organization you are a member of. It lasts 30 minutes unless Coritan changes the setting, and `expires_in` gives its lifetime in seconds.

Refresh token
: Gets a new pair from [`POST /auth/refresh`](/api/reference/client/authentication/#op-post-api-v1-auth-refresh) with the body `{"refresh_token": "..."}`, with no password, verification check or second factor. It lasts 30 days unless Coritan changes the setting. Each new pair holds a refresh token with a full lifetime; the one you sent keeps working until its own expiry.

Pending token
: What the sign-in answers when [two-factor authentication](/account/two-factor-authentication/) is on: `mfa_required: true` and an `mfa_token` that lasts 10 minutes (`expires_in: 600`). Send it as the bearer token to [`POST /auth/mfa/verify`](/api/reference/client/authentication-mfa/#op-post-api-v1-auth-mfa-verify) with `{"code": "123456"}`, and the answer holds the access and refresh tokens. Every other route refuses it with `401` and `"error": "mfa_required"`.

Changing or resetting the password stops every token issued before it, and a suspended or closed account cannot use any token. The API has no sign-out route. [Sign out and end sessions](/account/sessions/) explains how to stop a token working.

## Organization credentials

Member access token
: The same access token as the Client API. It reaches an organization's routes when you are its owner or a member, and your [role](/organizations/roles-and-permissions/) decides what it may do. Without membership, a route answers `403` `Not a member of this organization`.

Staff console token
: A console session for one organization, from [`POST /orgs/{org_slug}/staff/auth/login`](/api/reference/organizations/customer-authentication/staff/#op-post-api-v1-orgs-org-slug-staff-auth-login) with your Coritan email and password. Its tokens start with `ost_`. The access token lasts 24 hours, and [`POST /orgs/{org_slug}/staff/auth/refresh`](/api/reference/organizations/customer-authentication/staff/#op-post-api-v1-orgs-org-slug-staff-auth-refresh) swaps the refresh token, which lasts 30 days, for a new pair. It reaches every route a member's access token reaches. The actions that move money or end things also need a *step-up* in the last 10 minutes, which only a console session has: [Confirm it is you](/organizations/staff-console/sign-in/#confirm-it-is-you) lists them.

Customer token
: A customer of an organization's storefront, from [`POST /orgs/{org_slug}/auth/login`](/api/reference/organizations/customer-authentication/auth/#op-post-api-v1-orgs-org-slug-auth-login). The access token lasts 24 hours and the refresh token 30 days, and [`POST /orgs/{org_slug}/auth/refresh`](/api/reference/organizations/customer-authentication/auth/#op-post-api-v1-orgs-org-slug-auth-refresh) swaps them for a new pair. [Sign in your storefront's customers](/organizations/storefront/customer-sign-in/) has the details. A customer token never reaches the routes for members.

Organization API key
: Lets your own systems call the [Commerce API](/organizations/storefront/commerce-api/) without a person signing in. An owner or admin creates it with [`POST /orgs/{org_slug}/api-keys`](/api/reference/organizations/api-keys/#op-post-api-v1-orgs-org-slug-api-keys); it starts with `ct_`, and the answer shows it once. Its scopes decide which routes it reaches, it has an hourly request limit (4,000 by default), and it can have a list of addresses it may be used from. No route outside the Commerce API reads it. [Create organization API keys](/organizations/api-keys/) lists the scopes.

Publishable key
: Names a store to the [Store API](/organizations/storefront/store-api/), which serves shoppers, so the key can sit in your storefront's code. Create one with [`POST /orgs/{org_slug}/commerce/publishable-keys`](/api/reference/organizations/commerce/commerce/#op-post-api-v1-orgs-org-slug-commerce-publishable-keys). A test key starts with `pk_test_`. A live key starts with `pk_live_`, and works once the store is live.

## Other keys

Send API key
: Sends mail through one SMTP Relay service over HTTPS, and does nothing else. Create it on the relay, as [Manage send API keys](/mail/smtp-relay/api-keys/) shows; it starts with `mk_`. The send API reads it from `X-Api-Key` only, never from `Authorization`. [Send email over HTTPS](/mail/smtp-relay/send-with-the-api/) has the requests.

Account API key
: The keys on the **API keys** tab of **Settings**, which start with `ct_`. No route accepts them yet: sent as a bearer token, one answers `401` `Invalid or expired token`. Use an access token instead. [Manage API keys](/account/api-keys/) explains what the keys are for today.

## The verification check

Coritan can put a verification check (Cloudflare Turnstile) on the forms that sign people in or give something away for free. [`GET /auth/turnstile`](/api/reference/client/authentication/#op-get-api-v1-auth-turnstile) says whether it is on:

```json
{"enabled": true, "site_key": "..."}
```

While it is on, these routes also need a `turnstile_token` in the body. Only the check on a web page makes one, so a script cannot sign in with a password while the check is on.

| API | Routes |
| --- | --- |
| Client API | `POST /auth/login`, `POST /auth/register`, `POST /auth/forgot-password`, `POST /auth/reset-password`, and `POST /services/order` for a plan that costs nothing |
| Organization API | `POST /orgs/{org_slug}/auth/register`, `POST /orgs/{org_slug}/staff/auth/login`, `POST /orgs/{org_slug}/staff/auth/forgot-password`, and a storefront order for a plan that costs nothing |

Without a token, or with one that did not pass, the route answers `403` with `{"detail": {"error": "turnstile_failed", "message": "Verification required"}}`. The `message` is `Verification failed` for a token that did not pass.

## Authentication errors

| Status | `detail` | Meaning |
| --- | --- | --- |
| `403` | `Not authenticated` | A Client API route got no `Authorization` header. |
| `403` | `Invalid authentication credentials` | The `Authorization` header does not start with `Bearer`. |
| `401` | `Invalid or expired token` | The token is not one Coritan issued, or it has expired. Refresh it, or sign in again. |
| `401` | `Invalid token type` | A refresh token was sent where an access token belongs. |
| `401` | `Invalid refresh token` | `POST /auth/refresh` was sent a token that is not a refresh token. |
| `401` | `{"error": "mfa_required", ...}` | A pending token was sent to a route other than the two-factor routes. Finish the sign-in first. |
| `401` | `Token invalidated by password change` | The password changed after the token was issued. Sign in again. |
| `403` | `Account is suspended or closed` | The account cannot sign in or use a token. |
| `401` | `Missing bearer token` | An Organization API route got no `Authorization: Bearer` header. |
| `403` | `Not a member of this organization` | The token is valid, but you are not a member of that organization. |
| `404` | `Organization not found` | No active organization has that slug. With an organization API key, the slug is not the key's organization. |
| `401` | `Token expired` | A staff console token has expired. Refresh it. |
| `403` | `{"error": "reauth_required", ...}` | The action needs a step-up, and the last one was more than 10 minutes ago. |
| `401` | `Invalid API key` | A Commerce API request sent an `X-API-Key` that is wrong or revoked. |
| `403` | `API key is not allowed from this address` | The request came from an address outside the organization API key's allow-list. |
| `403` | `Organization inactive` | The organization API key's organization is not active. |
| `403` | `{"error": "scope_required", ...}` | The organization API key lacks the scope the route needs. The `message` names it. |
| `401` | `{"error": "publishable_key_required", ...}` | A Store API request had no `x-publishable-api-key` header. |
| `401` | `{"error": "invalid_publishable_key", ...}` | The publishable key is wrong, revoked, or belongs to another store. |
| `401` | `X-Api-Key header required` | A send API request had no `X-Api-Key` header. |

[Errors](/api/errors/) describes the shapes of `detail`, and [Rate limits](/api/rate-limits/) the `429` that too many failed sign-ins bring.

## API

- `POST /api/v1/auth/login`: Login (https://docs.coritan.com/api/reference/client/authentication/#op-post-api-v1-auth-login)
- `POST /api/v1/auth/refresh`: Refresh (https://docs.coritan.com/api/reference/client/authentication/#op-post-api-v1-auth-refresh)
- `GET /api/v1/auth/turnstile`: Turnstile config (https://docs.coritan.com/api/reference/client/authentication/#op-get-api-v1-auth-turnstile)
