Skip to main content

REST API

The Integrations page is the way into your account for a program. It offers two interfaces over one set of operations, and every action passes the same validation as the panel interface.

InterfaceForAuthenticated with
REST API, this pageYour scripts and integrations.API keys that you create.
MCP serverAI tools.OAuth, with no key at all.

Both reach everything the panel manages: databases, app servers, websites and mail domains, mailboxes and forwarders, DNS records, containers, files, shares, OIDC applications, and the mail logs.

tip

The live API reference (Integrations → API Documentation) comes from the server itself, so it's always the authoritative list.

API keys​

Create a key under Integrations → API Keys, with:

  • a name, so you know which script it belongs to,
  • a set of scopes, and
  • an optional expiry.
The full key is shown once

It looks like epat_…. Put it in your integration right away; after that the panel shows only its start.

  • If you lose a key, create a new one.
  • If a key leaks, press Revoke. That's immediate and permanent.
tip

The list shows each key's last use. Revoke one that nobody has used for months.

A scope has the form resource:access, e.g. database:readonly or appserver:readwrite, plus two umbrella scopes, all:readonly and all:readwrite. Write access includes read access.

Give each integration the smallest scope that works

A monitoring script needs all:readonly, not all:readwrite. A key with all:readwrite and no expiry has the same power as your account.

A call carries the key as a bearer token:

curl -H "Authorization: Bearer epat_..." \
https://panel.example.com/api/v1/databases

What the API looks like​

The REST API follows JSON:API, so every response has the same predictable shape, whatever the endpoint:

  • what you asked for is under data,
  • its fields under attributes, and
  • its links to other things under relationships.
{
"data": [
{
"type": "databases",
"id": "019dd…",
"attributes": { "databaseName": "myaccount_blog" },
"links": { "self": "/api/v1/databases/019dd…" }
}
],
"links": { "self": "/api/v1/databases" }
}

Three things to know before writing code against it:

  • A write sends the same shape back. To create a database, post {"data": {"type": "databases", "attributes": {…}}} with Content-Type: application/vnd.api+json.
  • A long list comes in pages. Follow links.next until it no longer appears. Don't build page numbers yourself: the cursors are opaque, and the link already carries the filter you applied.
  • Errors come as a list. A failed request answers with an errors array, one entry per problem, each naming its field, so a form with two bad values reports both at once. Decide on the HTTP status code, not the message text.
A name works where an id works

Most paths accept a human-readable name as well as an id: /api/v1/accounts/alice@example.com works the same as the UUID. So most scripts need no lookup first.

Bot challenge​

A website's bot challenge is under its website service, which the path names by id or by hostname. Reading needs service:readonly, changing service:readwrite.

Method and pathWhat
GET /api/v1/website-services/{website}/bot-challengeThe rules with their modes, the settings, the exemptions of the website and of the server, whether Adaptive is switched on, and the crawlers that can be named.
POST /api/v1/website-services/{website}/bot-challenge/rulesGate a path: path, and mode adaptive or always. Without a mode it's always while Adaptive is in its trial period (adaptiveEnforced is false), adaptive after.
GET, PATCH, DELETE /api/v1/website-services/{website}/bot-challenge/rules/{rule}Read a rule, change its mode, or remove it.
PUT /api/v1/website-services/{website}/bot-challenge/settingsChange difficulty (a preset name such as medium), passLifetime (seconds), requestsPerPass (0 for no limit) or exemptions. Send only what changes.

exemptions holds kinds (search, ai), crawlers (names from availableCrawlers) and networks, and replaces the website's additions whole. A path or an exemption the panel refuses answers 400 with the reason, the same one the tab shows; an unknown website or rule answers 404, and a value outside a field's choices, such as a difficulty that isn't a preset, 422. Advice that doesn't stop a rule, such as a rule ending in /index.php, is in the rule's warnings.

When to use MCP instead​

When an AI tool calls your account, rather than code you wrote, connect it to the MCP server. It offers the same operations, authenticates over OAuth with no key to store, and describes each operation to the model so it can choose the right one.

note

API keys and MCP connections are separate credential systems. Revoke an MCP client under Integrations → OAuth → Connected apps, not on the API Keys page.