# V3 prototype guide (not live yet)

Use this guide to plan a future V3 integration against the contract prototype.
Everything else — the full reference, error registry, PII rules — is in the
[Business Management v3](/en/b2b-v3/openapi) page.

> **Coming soon — October 2026.** V3 is not ready: its endpoints are not deployed,
and the calls below cannot return data today. This page documents a prototype for
implementation planning; paths and schemas may still change before release.
Working with **v1 or v2** instead? Those use a partner token and a combined `Bearer <partner>, User <user>` header — see [How to Get API Keys (v1 and v2)](/en/get-api-key). V3 does not accept that header.


## 1. Pick your integration type

This one choice decides everything that follows.

| You are building | Credential | Flow |
|  --- | --- | --- |
| **Your own script for your own business** — internal automation, a private MCP server, a report job | **Restricted key**, created in your Altegio account | None. The key *is* the credential |
| **A product other businesses install** — a marketplace app with a backend that runs unattended | Marketplace application + **confidential** OAuth client (`client_id` + `client_secret`) | Client Credentials (planned for the second delivery) |
| **An agent or desktop/CLI tool acting as a specific employee** — Claude, Cursor, Codex, your own native app | OAuth **public** client — pre-registered, or self-registered via DCR/CIMD (planned) | Authorization Code + PKCE |


Two things that surprise people:

- **Registering an OAuth client grants you nothing.** It creates an identity, not access. Data still requires either a business installing your app, or a person consenting.
- **Public clients cannot use Client Credentials.** A public client acts on behalf of a user — there is no unattended path without a marketplace application.


## 2. Get your credential

**Restricted key** — create it in your account. It is shown once; store it like a password. Choose its scopes and its Locations at creation time.

**Marketplace app** — register the application, then create a confidential OAuth client for it. You get `client_id` and `client_secret`; the secret is shown once and can be rotated with overlap.

**Agent / native client** — pre-register and receive a `client_id`. Self-registration is planned; once it ships, it will look like this:

```bash
curl -X POST https://api.alteg.io/oauth/register \
  -H 'Content-Type: application/json' \
  -d '{
    "client_name": "Example MCP Host",
    "redirect_uris": ["http://127.0.0.1/callback"],
    "grant_types": ["authorization_code", "refresh_token"],
    "token_endpoint_auth_method": "none"
  }'
```

Loopback redirects are accepted for native and CLI clients and the port is dynamic — you do not register every port you might bind. The literal `127.0.0.1` or `[::1]` is preferred; `localhost` is accepted because widely used hosts cannot change it.

## 3. Get a token

**Restricted key — skip this step.** The key is sent directly as the Bearer credential.

**Marketplace backend** (Client Credentials is planned for the second delivery). You need the `location_id` you are acting in: you receive it when the business installs your app, as `salon_id` on your registration redirect. One token is bound to **one** Location — an app installed in twenty Locations requests twenty tokens.

```bash
curl -X POST https://api.alteg.io/oauth/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=client_credentials \
  -d location_id=777 \
  -d 'scope=clients:read appointments:read'
```

No refresh token comes back — request a new access token when the current one expires (access tokens last one hour).

**Agent acting as an employee.** Send the user to `GET /oauth/authorize` with PKCE, let them log in and approve the access on the consent screen, then exchange the code:

```bash
curl -X POST https://api.alteg.io/oauth/token \
  -d grant_type=authorization_code \
  -d client_id="$CLIENT_ID" \
  -d code="$CODE" \
  -d code_verifier="$VERIFIER" \
  -d redirect_uri=http://127.0.0.1:53682/callback
```

This flow returns a rotating refresh token. Each use rotates it; replaying an old one kills the whole family, so always store the newest.

## 4. See what you can reach

```bash
curl https://api.alteg.io/oauth/accessible-resources \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

Returns the Locations and chains this token can act in, with the scopes granted on each. Use it to learn which `location_id` to put in a path — it describes the token you already hold, so it is a *check*, not a way to discover your first Location.

## 5. Make the call

Resource paths sit under **`/api/v3`**. OAuth and discovery stay on the host root (no `/api/v3`).

```bash
curl "https://api.alteg.io/api/v3/locations/777/clients?limit=25" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

Every resource lives under a Location. Lists are cursor-paginated: pass the returned `next_cursor` back as `cursor`.

Errors are `application/problem+json` with a stable `code` — branch on that, never on the message:

```json
{
  "type": "https://developers.alteg.io/problems/missing-scope",
  "title": "Missing scope",
  "status": 403,
  "code": "missing_scope",
  "detail": "The token does not carry clients:read.",
  "instance": "urn:altegio:request:req_a1b2"
}
```

`404` on a Location means "not yours or not there" — the two are deliberately indistinguishable, so do not treat it as "deleted".

## Two complete examples

**Agent host (Codex, Cursor, Claude).** Self-register → `client_id`, no secret · Authorization Code + PKCE with a `127.0.0.1` redirect · the employee approves the access on the consent screen · call `accessible-resources` to see which Locations the token covers · then act. Rotate the refresh token on every use.

**Personal server-to-server script.** Create a restricted key in your account, scoped to the Locations you need · send it as `Authorization: Bearer <key>` · that is the whole setup. No OAuth, no token endpoint, no refresh.

## What not to expect

- **No account-wide token.** Every marketplace Client Credentials token is bound to one Location. User-delegated tokens and restricted keys may cover multiple Locations and chains — call `GET /oauth/accessible-resources` to inspect the effective grants.
- **No refresh for marketplace backends.** Re-authenticate with your client credentials instead.
- **`client_id` is not an application id.** The word "client" also means the business's customer in `/clients` — the OAuth one is always written as `client_id` and looks like `ag_client_…` or a CIMD URL.
- **Scopes differ per credential class.** Client Credentials cannot be granted chain-wide scopes or `locations:create`, and `team_members:manage_access` is granted to no credential in this iteration. `GET /oauth/authorize` drops the scopes a flow cannot grant and ignores unknown codes; a request whose known scopes all fall outside the flow is refused with `invalid_scope`. A call without the scope it needs gets `403 missing_scope`.
- **Resource base is `/api/v3`.** Example: `https://api.alteg.io/api/v3/locations/{location_id}/…`. Token, register, revoke, and `accessible-resources` stay at `https://api.alteg.io/oauth/…`.