# HTTP API (/en-GB/ontwikkelaars/http)

The Tillor API is a REST API. Authenticate with an API key and send the right headers with every request. API keys are **per account** - one key works for all organisations you have access to.

## API reference and OpenAPI [#api-referentie-en-openapi]

- **[API reference](/api-referentie)** - Every endpoint with parameters and examples; try requests directly in the browser
- **[OpenAPI specification](https://tillor.eu/api/openapi.json)** - Full API documentation in JSON format for code generation or import into Postman/Insomnia

---

## API keys [#api-sleutels]

### Create [#aanmaken]

You create API keys in the Tillor app via the user menu under **Account > API Keys** (marker **1** = menu, marker **2** = API Keys in the screenshot). You can also create them through the API with a logged-in session (no API key). See the [OpenAPI specification](https://tillor.eu/api/openapi.json) under tag `api-keys` for paths, request and response schemas.

<DocScreenshot src="/screenshots/http/api-keys-nav.png" alt="API keys in account settings" />

The raw key is returned **once** on creation or regeneration. Store it safely; you cannot retrieve it afterwards.

### Restrict permissions [#rechten-beperken]

By default a key can do everything you can do yourself, in every organisation you are a member of. In the key window, turn on **Limit to selected permissions** and choose the permissions the integration needs, for example only reading invoices. A restricted key gets, in each organisation, the intersection of its permissions and your membership there: a key can never do more than you. A call outside the chosen permissions gets `403 Forbidden`, just like a user without that permission. A change to the permissions applies within a minute. Through the API: `scopes` (list of permission IDs such as `invoices:read`, or `null` for everything) when creating and updating a key.

### Usage [#gebruik]

Send the API key in the `x-api-key` header:

```http
x-api-key: tkn_xxx
```

Keys use the `tkn_` prefix. For organisation endpoints the organisation ID is in the path (`/orgs/org_abc123/...`); a separate header is not needed.

### Rate limits [#rate-limits]

- **Default:** 5000 requests per 60 seconds per key
- When exceeded: `429 Too Many Requests` with `Retry-After`
- Every API key response contains IETF `RateLimit-*` headers:

```http
RateLimit-Limit: 5000
RateLimit-Remaining: 4999
RateLimit-Reset: 58
RateLimit-Policy: 5000;w=60
```

`RateLimit-Reset` is the number of seconds until the current window starts again. Browser clients can read these headers via CORS (`Access-Control-Expose-Headers`).

### Security [#beveiliging]

<Callout type="warn" title="Note">
- **Never** expose API keys in client-side code or public repositories
- Regenerate keys periodically via **Regenerate** in the app (OpenAPI: `POST /api/api-keys/:id/renew`)
- Delete unused keys with `DELETE /api/api-keys/:id`
</Callout>

---

## Base URL and organisation context [#base-url-en-organisatie-context]

**Base URL:** `https://tillor.eu` (or your deployment URL; OpenAPI mentions `{APP_URL}/api`)

**Organisation paths:** Add the organisation ID to the path:

```
/api/orgs/:orgId/...
```

**Example:**

```
GET https://tillor.eu/api/orgs/org_abc123/customers
```

**Required header for organisation endpoints:** `x-api-key: tkn_xxx`. The organisation ID is in the path.

---

## Example: cURL [#voorbeeld-curl]

```bash
curl -X GET "https://tillor.eu/api/orgs/org_abc123/customers" \
  -H "x-api-key: tkn_xxx" \
  -H "Content-Type: application/json"
```

---

## Integrations (marketplace) [#integraties-marketplace]

Organisation integrations (formerly "apps" in the API) use one resource per type under `/api/orgs/:orgId/integrations/{integrationType}`. Required permissions are `organization:integrations:read` and `organization:integrations:write` (replaces `organization:apps:*`).

| Method | Path | Description |
|--------|------|-------------|
| GET | `/integrations/{integrationType}` | Status (`installed`, `available`) and redacted `settings` |
| POST | `/integrations/{integrationType}` | Install (optional `settings` in body; OAuth can return `redirectUrl`) |
| PUT | `/integrations/{integrationType}` | Update settings (`settings` in body) |
| DELETE | `/integrations/{integrationType}` | Uninstall |

On `GET` and after `PUT`, `settings` contains only **public** fields plus `{ configured: true }` per secret field. Passwords, API keys and OAuth tokens are not returned. Leave secret fields empty on `PUT` to keep the existing value.

See the [OpenAPI specification](https://tillor.eu/api/openapi.json) (tag `integrations`) for exact schemas.

---

## Related [#gerelateerd]

- [API reference](/api-referentie) - Every endpoint, to try out in the browser
- [OpenAPI](https://tillor.eu/api/openapi.json) - API specification (JSON)
- [MCP](/ontwikkelaars/mcp) - Connect Cursor, Claude, ChatGPT or Gemini via OAuth
- [External notifications](/ontwikkelaars/externe-notificaties) - Register emails your own system sent
- [Webhooks](/ontwikkelaars/webhooks) - Receive events via HTTP POST
- [Incoming webhook of an automation](/ontwikkelaars/automatiseringen#een-automatisering-starten-met-een-webhook) - Start an automation with a POST, without an API key
- [SSE](/ontwikkelaars/sse) - Stream events via Server-Sent Events
- [Examples](/ontwikkelaars/voorbeelden) - Code and payload examples
