Skip to content

REST API

Everything the console does goes through the same API that is open to you. There is no privileged API reserved for the interface.

The API lives under /api/v1. The contract is described in OpenAPI and it is stable: changes are additive.

A disabled feature answers 501 feature_disabled, never 404. This is not an implementation detail, it is what makes an integration durable:

  • your integration code tells “not available on this install” from “you got the URL wrong”;
  • it does not break the day the feature is enabled.

The body of a 501 response names the disabled feature and indicates not to retry.

Two headers, on every request:

Authorization: Bearer <token>
X-Tenant: <your-tenant>

X-Tenant is what selects the authentication realm, not the URL. The token comes from your installation’s OpenID Connect issuer:

Fenêtre de terminal
curl -s -X POST "https://identity.example.com/realms/$TENANT/protocol/openid-connect/token" \
-d grant_type=client_credentials \
-d "client_id=$APP_ID" -d "client_secret=$APP_SECRET"

For a script or an integration, use an application’s credentials, not your own (see roles and permissions).

Each resource has its own: application/vnd.graal.systems.v1.<type>+json, where <type> is the exact resource name in the contract.

project · job · run · identity · assignment · library · blobmetadata · instance-type

Editing: JSON Patch, never a full replacement

Section titled “Editing: JSON Patch, never a full replacement”

Edits use JSON Patch (RFC 6902), with the media type application/json-patch+json;charset=UTF-8:

Fenêtre de terminal
curl -s -X PATCH "$GRAAL_API/jobs/$JOB" \
-H "Authorization: Bearer $TOKEN" -H "X-Tenant: $TENANT" \
-H 'Content-Type: application/json-patch+json;charset=UTF-8' \
-d '[{"op":"replace","path":"/schedule","value":"0 6 * * *"}]'

A full PUT would overwrite fields someone else changed between your read and your write. That is why there is none.

Lists are paginated by page and size, and the response carries the total:

Fenêtre de terminal
curl -s "$GRAAL_API/projects?page=0&size=50" \
-H "Authorization: Bearer $TOKEN" -H "X-Tenant: $TENANT"

Logs are the exception: they are paginated by cursor, because you follow them while they are being written. The response carries an X-Cursor header to send back on the next call to get the continuation — and only the continuation.

{ "code": "JOB_NOT_FOUND", "message": "…", "errors": [] }

code is stable and testable; message is for humans and may change. errors carries the field-by-field detail of a validation error.

A few codes you meet early:

Code What it means
feature_disabled (501) Feature disabled on this installation. Do not retry
TENANT_CONFIGURATION_ERROR The tenant lacks required configuration (often: the storage’s internal address)
MISSING_OWNER The created resource needs an explicit owner
JOB_NOT_FOUND The identifier does not exist in this tenant
  • Identity — tenants, users, groups, roles, assignments, identity providers, applications (tokens), audit.
  • Workloads — projects, jobs, runs, logs, workflows, secrets, libraries, runtimes, instance types, quotas, editor graphs.
  • Data — buckets, catalog (layers, databases, tables, fields), warehouses.
  • ML — experiments, models.
  • Operations — usage and costs, notifications, tickets, search.

A facade compatible with the MLflow client for experiment tracking is served under /mlflow, on the same host as the console and the API. It implements the subset of MLflow’s REST API 2.0 devoted to experiment tracking and the registry: see the machine learning page.

A token carries the rights of a service account, subject to the same per-project access control as a user, and is revoked the same way. It is the technical support of the MCP server: each agent acts under a service account with limited rights.

The full contract is an OpenAPI file: clients are generated from it, the console’s included. In the console, the “Copy as…” menu gives the equivalent call as curl or as an MCP tool call.