Developers

The ATAILA Cloud Platform API

Every ATAILA Cloud Platform installation can be driven from code: customers, tenants, users, projects, releases, the licence, the brand and Private AI, through one versioned, documented contract. Use it from OpenTofu or Terraform, from an AI agent over MCP, or with plain HTTPS.

Where the API lives

Each installation serves its own API at https://<your portal>/api/v1, next to its portal. There is no central endpoint: you talk to the installation you manage.

It is internal only. The API is reachable on the installation's own network, as the portal is, and never from the public internet. Run your tools where the portal is reachable: an administrator's workstation, a CI runner on that network, or over its VPN.

It is off until an administrator turns it on in the portal's settings. While it is off, every /api/v1 path answers 404.

Your installation also serves the contract of the exact version it runs, at /api/v1/openapi.json, with an interactive reference at /api/v1/docs. That interactive page loads its scripts from a public CDN, so on an installation without internet access it stays blank: use the contract file or the reference on this site there. The reference on this site is platform release 1.0.187 (API 1.0.0). Examples use portal.example.com.

Read this first

Treat every API token as an operator credential

An API token is a remote handle on the portal, and the portal holds the credentials the platform itself runs on. Whoever holds a token can do, through the API, everything its scopes allow on that installation.

So handle a token as you would an operator's password: one token per purpose, with only the scopes that purpose needs and the shortest lifetime that works. Keep it in a secret store or a protected CI variable, never in a repository or a configuration file. Turn on Allow destroy only on a token that is meant to remove things, and revoke a token when its job is done.

Get a token

Creating a token, your own or a service account's, needs the API tokens admin permission (api-tokens-admin-global); ask an administrator for it if the portal does not offer Create token. Revoking your own token needs nothing more than being signed in.

  1. In the portal, open Profile → API tokens and choose Create token.
  2. Name it after its job, for example terraform-ci.
  3. Pick its scopes. A scope is a permission key, such as tenancy-read-global or projects-read-global. Only keys you hold yourself are offered.
  4. Set its lifetime: 1 to 365 days, 90 if you leave it.
  5. Leave Allow destroy off unless the token is meant to archive, delete, deactivate or retire.
  6. Copy the token when it is shown. It is shown once.

Service accounts

For automation that should not depend on one person, create a service account on the portal's Service accounts page and mint its tokens there. A service account cannot sign in; it acts only through its tokens, and a token carries no more than the account holds. You can give an account, or its tokens, only permissions you hold yourself. Creating and managing service accounts needs the same API tokens admin permission as creating a token.

Destroy is a double opt-in

Removing anything through the API needs a token minted with Allow destroy; without it the API answers 403. The OpenTofu and Terraform provider adds a second switch, allow_destroy = true in the provider block, and refuses a destroy at plan time without it. Even with both, nothing is wiped: see what the API never does.

Make a first call

Send the token as a bearer credential. whoami answers with the calling principal, how it authenticated, its effective scopes and, for a token, its expiry. If the portal's certificate comes from a private certificate authority, add --cacert ca.pem.

shell
export ATAILA_TOKEN='<token from the portal>'

# Who am I, and what may this token do?
curl -s https://portal.example.com/api/v1/whoami \
  -H "Authorization: Bearer $ATAILA_TOKEN"

# Which API and platform version does this installation run?
curl -s https://portal.example.com/api/v1/meta \
  -H "Authorization: Bearer $ATAILA_TOKEN"

# The first page of customers, two at a time
curl -s "https://portal.example.com/api/v1/customers?limit=2" \
  -H "Authorization: Bearer $ATAILA_TOKEN"

A create, with an idempotency key so that a retry can never create twice:

shell
curl -s https://portal.example.com/api/v1/customers \
  -H "Authorization: Bearer $ATAILA_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1d2c8e-0b7a-4f0e-9a51-3c2b9d7e1a40" \
  -d '{"short_name": "EXAMPLE", "long_name": "Example Holdings Ltd",
       "gitlab_group": "example", "primary_contact_email": "it@example.com",
       "primary_contact_name": "Example IT Desk"}'

Versioning and stability

The path carries the major version, /api/v1. The full version is semantic: info.version in the contract and api_version from GET /api/v1/meta, which also reports the platform release as platform_version.

Within v1 the contract only grows. Operations, optional request parameters, optional response members, new enum values and new error codes can be added at any time, so treat unknown values and codes as unknown, not as errors. Removals and type changes need 12 months' notice from the announcement, which is made on the changelog, in the contract (deprecated: true) and with Deprecation and Sunset headers. The provider follows semantic versioning on its own.

The promise holds from API 1.0.0 and provider 1.0.0, published on 2026-10-02. Details on the changelog.

Conventions

Formats

JSON in and out. Ids are strings. Timestamps are RFC 3339 in UTC. PATCH bodies are JSON Merge Patch (RFC 7396), sent as application/merge-patch+json or application/json.

Pagination

Lists answer {"items": [...], "next_cursor": "..."}. Pass next_cursor back as cursor until it is null. limit is 50 by default, 200 at most (500 on the audit log).

Idempotent requests

Every POST that creates or starts something (all of them except POST /mcp, which only reads) and the node-cache PUT and DELETE accept an Idempotency-Key header, declared on each in the reference. For at least 24 hours the same key with the same request replays the stored answer (Idempotent-Replayed: true); with a different request it is 409 idempotency_key_reused; while the first request is still running it is 429 idempotency_request_in_progress with Retry-After. A key that is not 1 to 255 printable characters is 400 invalid_idempotency_key.

Long-running work

Provisioning, release promotions and model caching answer 202 Accepted with an operation and its Location. Poll GET /api/v1/operations/{id} until it is succeeded or failed. On the way it may be pending, running, awaiting_approval (a production release waiting for a person) or awaiting_operator (a step waiting for an operator).

Deletes

A delete answers 204 with no body. The one delete that starts long-running work (removing a model from a node) answers 202 with an operation, and accepts an Idempotency-Key like a create.

Rate limit

600 requests per minute per token. Above that the answer is 429 rate_limited with Retry-After. Over MCP, a tool call costs two requests.

Warnings

A request that succeeded can still carry warnings: a later step that did not complete, each with a code and a message. The change stands; show the warnings to whoever made it.

Licence

A request the installation's licence does not allow is refused with 403 and a code starting licence_, naming the remedy. Retrying will not help.

Errors

Every error is an RFC 9457 problem document (application/problem+json). Branch on code, which is stable; title and detail are for people. Some answers add members: a 422 names the first offending member in field, and a 409 refusing a destroy lists what blocks it in blockers. The codes any operation can answer (authentication, permissions, licence, idempotency, validation, rate limit) are listed once, in the error catalogue of the reference; each operation adds its own codes in its responses.

request_id is also the X-Request-ID header every response carries. To report a problem, write to support@ataila.com and quote it.

example
HTTP/1.1 429 Too Many Requests
Content-Type: application/problem+json
Retry-After: 12

{
  "type": "urn:ataila:api:problem:rate_limited",
  "title": "Too Many Requests",
  "status": 429,
  "detail": "…",
  "code": "rate_limited",
  "instance": "/api/v1/customers",
  "request_id": "…"
}

What the API never does

Some things stay in the portal, with a person, by design. They are not missing features.

It never wipes or tears down.
Destroy archives a customer, deletes a tenant only when it is empty, deactivates a user (people are never deleted) and retires a project, whose name, index and domain stay reserved. Nothing on the infrastructure is removed.
It never approves a production release.
A promotion to production is a request. A person approves or rejects it in the portal. The API cannot approve, reject, skip the approval or copy data between environments.
It never issues or signs a licence.
It installs a licence bundle issued for the installation and reports the licence state. Licences are issued in the ATAILA partner portal, not through this API.
It never reveals a stored secret.
An AI gateway key's value is returned once, by the request that created or rotated it, and only when that request asked for it. API tokens are shown once, in the portal. Passwords are never set or returned: a new person signs in after a password reset.
It does not touch the platform itself.
The projects the platform runs on are read-only. The API does not pull model weights, load or unload a model, or power AI nodes on and off: the AI Center is read-only. (Copying weights the platform already holds onto a node's disk is offered.)

Start from the reference

Every operation, parameter and schema of API 1.0.0, and the contract as a file for your own client generator.