Console

Service accounts

A service account is a machine identity for the control-plane API: your scripts and services exchange its credential for a short-lived token and act with real, scoped permissions.

Using one is three steps, and only the first happens in a browser:

  1. Create the account in the Console and store its credential.
  2. Exchange the credential for a short-lived access token.
  3. Call api.upgreat.ai with that token as a bearer.

Step 1 — Create the account

In the Console, go to Access → Service accounts → New service account. Name it after the system that will hold the secret (a CI pipeline, a backend service, an integration), then pick its scope:

  • Organisation-scoped — acts across the organisation with a role, exactly like a member. Any role except owner; start with member (read-only) and attach policies if it needs specific write access.
  • Project-scoped — confined to one project. Read-only can inspect everything in the project; full accesscan also create and manage the project's resources (inference keys, chat instances, buckets). Neither can see the rest of the organisation, and neither can ever manage service accounts.

Creation shows three values:

  • client_id — the account's public identifier (e.g. sa-acme-x7k2mp). Not a secret; visible in the list later.
  • client_secret — the credential itself. Shown once — copy it into your secret manager before leaving the page.
  • token endpoint — the URL you exchange the credential at (step 2).

Treat the secret like a password

The secret is the account's only credential. Never commit it to source control or expose it in client-side code. If it leaks, rotate it in the Console — the old secret stops working immediately.

Step 2 — Get a token

Exchange the credential with the OAuth2 client_credentials grant — a single POST, no browser, no redirect. The response contains an access_token valid for about 5 minutes (expires_in is in seconds). Fetch a fresh one per run, or cache it and refresh when it expires.

export UPGREAT_CLIENT_ID="sa-acme-x7k2mp"
export UPGREAT_CLIENT_SECRET="…"   # from the create screen

TOKEN=$(curl -s -X POST \
  https://identity.upgreat.io/realms/customers/protocol/openid-connect/token \
  -d grant_type=client_credentials \
  -d client_id="$UPGREAT_CLIENT_ID" \
  -d client_secret="$UPGREAT_CLIENT_SECRET" | jq -r .access_token)

Step 3 — Call the API

Send the token in the Authorization header. Every endpoint in the API reference works this way — what the account may do is decided server-side from its role and policies.

First call
curl -s https://api.upgreat.ai/v1/orgs \
  -H "Authorization: Bearer $TOKEN"
Response
{
  "data": [
    {
      "id": "bdaca4bc-…",
      "name": "Acme",
      "slug": "acme",
      "role": "member",
      "created_at": "2026-06-14T12:53:19.165Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}

What scoping looks like in practice

Authorisation is enforced per request, and a refusal is always an explicit 403 naming the missing permission — useful when deciding which scope or role an account needs:

A project-scoped account reading org settings → 403
{
  "error": {
    "type": "forbidden",
    "code": "forbidden",
    "message": "not authorized to organization:read",
    "request_id": "req-s"
  }
}
  • An org-scoped account with the member role can list projects, keys, members and usage — and gets 403 on anything that changes state.
  • A project-scoped account can call project endpoints (e.g. /v1/projects/{projectId}/inference/keys) but gets 403on everything org-level — including reading the organisation itself. Copy the project's id from the Console when configuring it.

Rotating and deleting

  • Rotate regenerates the secret. The old secret stops working immediately; tokens already issued survive only until they expire (a few minutes).
  • Deleteremoves the credential and the account's membership. Authorisation is revoked immediately — even a token that hasn't expired yet is refused.

Troubleshooting

  • 401 invalid_client from the token endpoint — wrong or rotated secret, or the account was deleted. Rotate in the Console to get a fresh secret.
  • 401 invalid token from the API — the access token expired. Fetch a new one; they only live a few minutes by design.
  • 403 forbidden — the account lacks that permission. The message names the missing action; widen the role, attach a policy, or use an account with the right scope.

Three different credentials

Humans sign in to the Console with UPGREAT Identity. Machines call api.upgreat.ai with a service account. The LLM API (llm.upgreat.ai) uses a project inference key. They are not interchangeable.