---
title: Quickstart
path: quickstart
status: published
---

# Quickstart

Five steps from zero to a working ScaiKey access token. We'll use the `client_credentials` grant because it's the shortest path: no user, no browser, two `curl` calls.

If you need an interactive user-login flow instead (browser redirect, login page, consent), see [Tutorials → Integrate a web app](/docs/scaikey/tutorials).

## Prerequisites

- A ScaiKey instance you can talk to. The public ScaiLabs deployment is at `https://scaikey.scailabs.ai`. We'll use `$SCAIKEY` as a placeholder.
- A super_admin in the ScaiKey admin UI (or a partner_admin if you're scoped to a partner).

## 1. Register an application

Log in to the admin UI (`$SCAIKEY/admin/`) and create a new application:

- **Type:** `SERVICE` (machine-to-machine; uses a client secret).
- **Scope:** `GLOBAL` if it's cross-tenant, otherwise `TENANT` and pick the tenant.
- **Allowed scopes:** add at least `openid`. If this service will call ScaiKey's admin API, also add `admin:read` (and `admin:write` if it will modify resources).

Save it. The UI shows you the `client_id` and a one-time `client_secret`. Copy both.

```bash
export SCAIKEY="https://scaikey.scailabs.ai"
export CLIENT_ID="<paste here>"
export CLIENT_SECRET="<paste here>"
```

## 2. Request an access token

```bash
curl -X POST "$SCAIKEY/api/v1/platform/oauth/token" \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d "grant_type=client_credentials" \
  -d "scope=admin:read"
```

Use `/api/v1/platform/oauth/token` for `GLOBAL` apps; use `/api/v1/auth/tenants/{slug}/oauth/token` for `TENANT`-scoped apps.

Response:

```json
{
  "access_token": "eyJhbGciOi...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "admin:read"
}
```

## 3. Decode the token (optional but recommended)

```bash
# Split out the payload segment and base64-decode it
echo "$ACCESS_TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | python3 -m json.tool
```

You should see something like:

```json
{
  "iss": "https://scaikey.scailabs.ai/platform",
  "aud": "<your client_id>",
  "sub": "<your client_id>",
  "scope": "admin:read",
  "token_type": "client_credentials",
  "exp": 1747584000,
  "iat": 1747580400
}
```

`sub` equals `client_id` for service tokens — this is how downstream services identify the calling principal.

## 4. Call a protected endpoint

```bash
curl "$SCAIKEY/api/v1/admin/tenants" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

You should see your tenants. If you get `403 Platform token requires admin:read or admin:write scope`, your token doesn't have the scope — go back to step 1 and add it to the application's allowed scopes.

## 5. Tokens expire — refresh by re-requesting

`client_credentials` tokens don't come with a refresh token (intentional). When `expires_in` runs out, just repeat step 2. Most clients cache the token in memory and re-request a few seconds before expiry.

## Where to go from here

- [Concepts → OAuth and OIDC](/docs/scaikey/concepts/oauth-and-oidc) — every grant we support and when to use each.
- [Concepts → Tokens and scopes](/docs/scaikey/concepts/tokens-and-scopes) — what's in the JWT, how scopes work.
- [Reference → OAuth endpoints](/docs/scaikey/reference/oauth-endpoints) — every endpoint, every parameter.
- [Troubleshooting](/docs/scaikey/troubleshooting) — common errors and what they mean.
