# Developer overview

Base URLs, API-key and OAuth 2.1 authentication, and the error shape shared by every Easerix API.

Canonical: https://easerix.com/docs/developers/overview

<Answer label="How the Easerix API works">
Every Easerix tool exposes a REST API, most of them under one domain at
`api.easerix.com/<tool>`. You authenticate by exchanging an API key for a
short-lived access token at `auth.easerix.com`, then send that token as a
Bearer header. Every error response is a single JSON field: `{"error": "..."}`.
</Answer>

## Base URLs

| API | Base URL | Notes |
|---|---|---|
| Auth & accounts | `https://auth.easerix.com` | Token exchange, OAuth 2.1, workspaces |
| Tasks | `https://api.easerix.com/tasks` | |
| Notes | `https://api.easerix.com/notes` | |
| Sign | `https://api.easerix.com/sign` | |
| Links | `https://api.easerix.com/links` | Management API — short links themselves redirect on `esrx.ly` |
| Forms | `https://f.easerix.com` | API and public form endpoints share the form domain |
| CronCrunch | `https://api.easerix.com/croncrunch` | |
| CRM | `https://api.easerix.com/crm` | |
| Notifications | `https://notifications-api.easerix.com` | |
| MCP | `https://mcp.easerix.com` | [MCP server](/developers/mcp) for AI clients |

Older per-tool hosts of the form `<tool>-api.easerix.com` are retired — use the
bases above. Endpoints are versioned under `/v1/`, so a full URL looks like
`https://api.easerix.com/links/v1/links`.

## Authentication

Easerix has two ways in: **API keys** for scripts, integrations, and CI, and
**OAuth 2.1** for apps that act on behalf of a user (this is what the
[MCP server](/developers/mcp) and the [CLI](/developers/cli) use).

### API keys

Create keys at **account.easerix.com**:

- **Personal keys** (`esrx_u_...`) — under **API keys**. The key acts as you,
  in one workspace you pick when creating it.
- **Workspace keys** (`esrx_o_...`) — under your organization's **API keys**
  page (requires settings permission, team workspaces only). These are service
  accounts: they act as a role you choose — `member`, `billing`, or `admin`,
  never `owner`.

Either kind can be restricted to specific tools and given an expiry (1 year by
default, 90 days, or never). The full key is shown **once** at creation — store
it in a secret manager. Revoke any key from the same page.

### Using a key: exchange it for an access token

API keys are never sent to the tool APIs directly. Exchange the key for a
short-lived access token first:

```bash
curl -s https://auth.easerix.com/v1/auth/token \
  -H "Content-Type: application/json" \
  -d '{"api_key": "esrx_u_..."}'
```

```json
{
  "access_token": "<JWT>",
  "expires_at": "2026-08-09T18:04:05Z",
  "org": { "id": "...", "name": "...", "role": "member", "tools": ["links", "tasks"] }
}
```

There is no refresh token — when `expires_at` passes, exchange the key again.
Then call any tool API with the token:

```bash
curl -s https://api.easerix.com/links/v1/links \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

Sending the raw `esrx_...` key as a Bearer token to a tool API returns `401`.

### OAuth 2.1

`auth.easerix.com` is a standards-compliant OAuth 2.1 authorization server:

| Endpoint | Purpose |
|---|---|
| `GET /.well-known/oauth-authorization-server` | Discovery metadata (RFC 8414) |
| `POST /oauth/register` | Dynamic client registration (RFC 7591) — public PKCE clients |
| `GET /oauth/authorize` | Authorization code flow — PKCE `S256` required |
| `POST /oauth/token` | Token endpoint — authorization code, refresh token, and device code grants |
| `POST /oauth/device/authorization` | Device authorization (RFC 8628) for terminals and headless devices |

Registered clients are public (no client secret) and must use PKCE with
`S256`. When a user approves your app they grant it **one workspace**; the
tokens you receive are scoped to that workspace and to the tools enabled
there. Users can revoke a grant anytime under **account.easerix.com →
Connected apps**.

MCP-compliant AI clients drive this whole flow automatically — see
[MCP server](/developers/mcp).

## Errors

Every non-2xx response has the same body — one human-readable field:

```json
{ "error": "missing bearer token" }
```

Status codes follow convention: `400` for invalid input, `401` for missing or
expired credentials, `403` for a role or permission you don't have, `404` for
anything that doesn't exist. Note that resources outside your workspace also
read as `404`, never `403` — the API doesn't confirm the existence of things
you can't access.

## Workspace scoping

Every resource belongs to the workspace your token was issued for. A token
carries the workspace, your role in it, and the tools enabled there; a request
to a tool that isn't enabled for the workspace is rejected. To work across two
workspaces, create a key (or grant) per workspace.

## Next steps

- [Browse the API reference](/developers/api) — every endpoint, generated from the OpenAPI specs
- [Connect an AI client over MCP](/developers/mcp)
- [Receive webhooks](/developers/webhooks)

## Frequently asked questions

### How long do access tokens last?

They're short-lived. Don't hardcode a duration — read `expires_at` from the
exchange response and re-exchange your API key when it passes.

### Can I use one API key across all my workspaces?

No. A key is bound to a single workspace at creation. Create one key per
workspace you need to automate.

### Where do I find the OpenAPI specs?

Each API's raw spec is linked from the [API reference](/developers/api) — for
example [links.v1.yaml](/openapi/links). They're standard OpenAPI 3 YAML,
ready for client generators.
