# Management API

> Automate ACOG administration with the REST API behind the console: connections, keys, budgets, webhooks, usage and savings.

Product: AI Cost Optimizer Gateway (ACOG). Web version: https://tessarac.com/docs/acog/api/management-api. Last updated: 2026-10-16.

Everything the console does, it does through the management API, and a gateway key with the right scopes can do the same.

## Base URL and authentication

| Deployment | Base URL |
|---|---|
| Hosted | `https://acog.tessarac.com/api` |
| Self-hosted | `https://<your-acog-host>/api` |

Authenticate with a gateway key whose scopes include the permission each endpoint needs:

```bash
curl -s https://acog.tessarac.com/api/v1/orgs/$ORG_ID/providers \
  -H "Authorization: Bearer $ACOG_ADMIN_KEY"
```

`x-acog-key` works too. A key's scopes are permissions (see [Roles and permissions](https://tessarac.com/docs/acog/reference/roles-and-permissions.md)); a key can never hold more than the person who created it, and seeing the Activity page's conversations (`audit:conversations`) is never available to a key.

Your organization ID (`org_…`) is on **Admin → Organization**. Routes under `/v1/orgs/{org}` act only on your own organization: another organization's ID answers `404`, as if it did not exist.

Requests and responses are JSON. Creating returns the created object; deleting returns `204 No Content`. Errors use standard status codes: `400` for an invalid body, `401` without a valid principal, `403` without the permission (the message names the permission), `404` for not found, `409` for a conflict.

## Endpoints

### Provider connections

| Method | Path | Permission |
|---|---|---|
| `GET` | `/v1/orgs/{org}/providers` | `admin:providers` |
| `POST` | `/v1/orgs/{org}/providers` | `admin:providers` |
| `DELETE` | `/v1/orgs/{org}/providers/{id}` | `admin:providers` |
| `GET` | `/v1/orgs/{org}/providers/bedrock-external-id` | `admin:providers` |
| `GET` | `/v1/orgs/{org}/providers/workload-identity` | `admin:providers` |
| `GET` | `/v1/orgs/{org}/channels` | `proxy` |

A connection is created with:

```json
{
  "channel": "vertex",
  "provider": "gemini",
  "display_name": "Vertex Gemini",
  "public": {
    "project_id": "my-project",
    "region": "us-central1",
    "auth_mode": "workload_identity",
    "workload_identity_provider": "projects/123456789/locations/global/workloadIdentityPools/acog/providers/acog-gateway",
    "models": ["gemini-3.5-flash", "gemini-3.1-flash-lite"],
    "default_model": "gemini-3.5-flash"
  },
  "secret": {}
}
```

`public` is stored and readable; `secret` is encrypted, write-only and never returned. Each provider page lists its fields. Posting a channel and family that already has a connection replaces it. `GET …/providers/workload-identity` returns your organization's issuer URL, subject and key set for [keyless connections](https://tessarac.com/docs/acog/providers/keyless-connections.md).

### API keys and Bedrock credentials

| Method | Path | Permission |
|---|---|---|
| `GET` | `/v1/orgs/{org}/api-keys` | any principal (your own keys; all keys with `admin:keys`) |
| `POST` | `/v1/orgs/{org}/api-keys` | any principal |
| `DELETE` | `/v1/orgs/{org}/api-keys/{id}` | any principal (your own keys; any key with `admin:keys`) |
| `GET`, `POST` | `/v1/orgs/{org}/bedrock-credentials` | `admin:keys` |
| `DELETE` | `/v1/orgs/{org}/bedrock-credentials/{id}` | `admin:keys` |

```bash
curl -s -X POST https://acog.tessarac.com/api/v1/orgs/$ORG_ID/api-keys \
  -H "Authorization: Bearer $ACOG_ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "billing-service", "scopes": ["proxy"], "allowed_cidrs": ["203.0.113.0/24"]}'
```

The response's `key` field is the only time the key is shown. `DELETE` disables an active key; `DELETE …/api-keys/{id}?purge=true` then removes the disabled key permanently. See [API keys](https://tessarac.com/docs/acog/administer/api-keys.md).

### Users, roles and invitations

| Method | Path | Permission |
|---|---|---|
| `GET` | `/v1/orgs/{org}/users` | `read:metrics` |
| `PUT`, `DELETE` | `/v1/orgs/{org}/users/{id}` | `admin:users` |
| `GET`, `POST` | `/v1/orgs/{org}/roles` | `admin:users` |
| `PUT`, `DELETE` | `/v1/orgs/{org}/roles/{id}` | `admin:users` |
| `GET`, `POST` | `/v1/orgs/{org}/invites` | `admin:users` (hosted) |
| `DELETE` | `/v1/orgs/{org}/invites/{id}` | `admin:users` (hosted) |
| `POST` | `/v1/orgs/{org}/users` | `admin:users` (self-hosted: returns a one-time initial password) |

### Budgets

| Method | Path | Permission |
|---|---|---|
| `GET` | `/v1/orgs/{org}/budgets` | `read:billing` |
| `PUT` | `/v1/orgs/{org}/budgets/{scope}/{entityID}` | `admin:budgets` |
| `DELETE` | `/v1/orgs/{org}/budgets/{scope}/{entityID}` | `admin:budgets` |
| `GET` | `/v1/budgets/usage` | `read:billing` |

```bash
curl -s -X PUT https://acog.tessarac.com/api/v1/orgs/$ORG_ID/budgets/org/$ORG_ID \
  -H "Authorization: Bearer $ACOG_ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"limit_usd": 5000, "window": "month"}'
```

`scope` is `org`, `group` (a team) or `user`, and `entityID` is the organization, team or user ID. See [Budgets](https://tessarac.com/docs/acog/govern/budgets.md).

### Webhooks

| Method | Path | Permission |
|---|---|---|
| `GET` | `/v1/webhook-events` | any principal |
| `GET`, `POST` | `/v1/orgs/{org}/webhooks` | `admin:org` |
| `PUT`, `DELETE` | `/v1/orgs/{org}/webhooks/{id}` | `admin:org` |
| `POST` | `/v1/orgs/{org}/webhooks/{id}/rotate-secret` | `admin:org` |
| `POST` | `/v1/orgs/{org}/webhooks/{id}/ping` | `admin:org` |
| `GET` | `/v1/orgs/{org}/webhooks/{id}/deliveries` | `admin:org` |

See [Webhooks](https://tessarac.com/docs/acog/administer/webhooks.md) for events, payloads and signature verification.

### Usage and savings

| Method | Path | Permission |
|---|---|---|
| `GET` | `/v1/orgs/{org}/dashboard/summary?window=30d` | `read:metrics` |
| `GET` | `/v1/orgs/{org}/dashboard/timeseries?window=30d&bucket=day` | `read:metrics` |
| `GET` | `/v1/orgs/{org}/dashboard/breakdown?dimension=model&window=30d` | `read:metrics` |

| Field | Meaning |
|---|---|
| `request_count` | Requests in the window |
| `spent_usd` | What the traffic cost through ACOG |
| `would_have_spent_usd` | The same traffic at your providers' list prices with no optimization |
| `saved_usd` | The sum of each request's saving |
| `gainshare_usd`, `net_saved_usd` | The share billed on the saving (hosted), and the saving after it |
| `savings_pct` | The saving as a share of `would_have_spent_usd` |
| `by_disposition` | Requests and saving per [disposition](https://tessarac.com/docs/acog/get-started/key-concepts.md#request-outcome-disposition) |

`breakdown` takes `dimension` = `model`, `channel`, `endpoint`, `key_id` or `user`.

### Billing (read only)

| Method | Path | Permission |
|---|---|---|
| `GET` | `/v1/orgs/{org}/billing` | `read:billing` |
| `GET` | `/v1/orgs/{org}/billing/invoices` | `read:billing` |

### Organization and settings

| Method | Path | Permission |
|---|---|---|
| `GET` | `/v1/orgs/{org}` | `read:metrics` |
| `PUT` | `/v1/orgs/{org}` | `admin:org` |
| `GET`, `PUT` | `/v1/orgs/{org}/settings` | `admin:org` |

### Licence (self-hosted)

| Method | Path | Permission |
|---|---|---|
| `GET` | `/v1/license` | `read:billing` |
| `POST` | `/v1/license/load` | `admin:org` |
| `DELETE` | `/v1/license` | `admin:org` |
| `GET` | `/v1/license/usage-export` | `admin:org` |

See [Licensing](https://tessarac.com/docs/acog/self-hosted/licensing.md).
