# API keys tools

> MCP tools for API keys: api_keys_create, api_keys_get, api_keys_list, api_keys_revoke, api_keys_update.

- `api_keys_create`: Create an API key
- `api_keys_get`: Get an API key
- `api_keys_list`: List API keys
- `api_keys_revoke`: Revoke an API key
- `api_keys_update`: Rename an API key

## `api_keys_create`

**Create an API key.** Operation [`api_keys.create`](https://ownrecord.io/docs/reference/api-keys-create.md) · Organization level · permission `keys:admin` · risk tier `security` · since M1

Creates an API key for a brand (the default), a legal entity, or the whole organization (an org management
key), in the mode of the request. The response contains the key in `secret`. **It is shown only once**: only a
hash is stored, so copy it to your secret manager now. `prefix` (the first 12 characters) identifies the key
later in the console and request logs.

- `kind: "brand"` needs `brandId` (or a brand-scoped request); the key acts on that brand only.
- `kind: "entity"` needs `entityId`; the key acts on the entity's brands and its ledger, filings,
  registrations and tasks.
- `kind: "org"` acts on the whole organization, including management operations such as members, brands and
  API keys. It needs an organization-level request.

`scopes` are permission scopes such as `customers:read`, `subscriptions:write` or `usage:write` (a restricted
key for usage writers). A key can only carry scopes its kind can use, and **never more than you could do
yourself** at that brand, entity or organization in that mode.

Live keys (`or_live_…`) need a live brand (go-live checklist), a plan with live mode and an active
organization; otherwise the call fails with `live_keys_unavailable`. Creating keys needs a sign-in within the
last 15 minutes in the console, and approval for agents (security tier).

Annotations: `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: false` · `openWorldHint: false`

### Arguments

| Argument          | Type                                                                 | Required | Description                                                                                                                                                                                              |
| ----------------- | -------------------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`            | string                                                               | yes      | (1–100 characters)                                                                                                                                                                                       |
| `kind`            | one of `org`, `entity`, `brand`                                      | no       | (default `"brand"`)                                                                                                                                                                                      |
| `entityId`        | ID (`ent_…`)                                                         | no       | –                                                                                                                                                                                                        |
| `brandId`         | ID (`brd_…`)                                                         | no       | –                                                                                                                                                                                                        |
| `scopes`          | array of one of 98 values, e.g. `org:read`, `org:write`, `org:admin` | yes      | (1–100 items)                                                                                                                                                                                            |
| `expiresAt`       | date-time (ISO 8601)                                                 | no       | –                                                                                                                                                                                                        |
| `entity_id`       | ID (`ent_…`)                                                         | no       | Optional: act inside this legal entity (ent\_…) instead of the whole organization.                                                                                                                       |
| `brand_id`        | ID (`brd_…`)                                                         | no       | Optional: act inside this brand (brd\_…) instead of the whole organization.                                                                                                                              |
| `idempotency_key` | string                                                               | no       | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–255 characters) |
| `dry_run`         | boolean                                                              | no       | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default `false`)                                                                                 |

- **Scope:** the whole organization, or one legal entity or brand of it with `entity_id` or `brand_id`.
- **Idempotency:** the result returns the `idempotency_key` used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
- **Dry run:** supported. `dry_run: true` checks permissions and rules and computes the result, then rolls everything back.

### Errors

- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): A field is missing or invalid, e.g. a brand key without brandId, or expiresAt in the past.
- [`api_key_scope_invalid`](https://ownrecord.io/docs/reference/errors.md#api_key_scope_invalid): A scope isn't a permission, or this kind of key can't use it.
- [`api_key_scope_not_allowed`](https://ownrecord.io/docs/reference/errors.md#api_key_scope_not_allowed): A scope is broader than what you may do at the key's brand, entity or organization.
- [`live_keys_unavailable`](https://ownrecord.io/docs/reference/errors.md#live_keys_unavailable): A live key is requested before a brand in its reach is live, or the plan or organization status doesn't allow live mode.
- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): The brand or entity doesn't exist or is outside your scope.
- [`scope_mismatch`](https://ownrecord.io/docs/reference/errors.md#scope_mismatch): An entity or org key is requested from a narrower (brand or entity) context.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't manage API keys at this target.
- [`livemode_not_allowed`](https://ownrecord.io/docs/reference/errors.md#livemode_not_allowed): You have no live-mode access, so you can't create live keys.
- [`fresh_auth_required`](https://ownrecord.io/docs/reference/errors.md#fresh_auth_required): Your last sign-in is older than 15 minutes.

### Examples

**A brand key for a billing backend**

Arguments:

```json
{
  "name": "Billing backend",
  "brandId": "brd_01m4ttwd3veh9apbewxheq5xh5",
  "scopes": ["customers:write", "subscriptions:write", "usage:write"],
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "key_01m4ttwd3tesktp0hc6bpxwkpd",
  "object": "api_key",
  "kind": "brand",
  "livemode": false,
  "name": "Billing backend",
  "prefix": "or_test_acme",
  "scopes": ["customers:write", "subscriptions:write", "usage:write"],
  "status": "active",
  "orgId": "org_01m4ttwd3veh9apbewxheq5xh3",
  "entityId": "ent_01m4ttwd3veh9apbewxheq5xh4",
  "brandId": "brd_01m4ttwd3veh9apbewxheq5xh5",
  "createdBy": "usr_01m4ttwd3veh9apbewxheq5xh6",
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-13T12:00:00.000Z",
  "expiresAt": null,
  "lastUsedAt": null,
  "revokedAt": null,
  "secret": "or_test_acme-app_ExampleOnlyNotARealKey000000000000000000000"
}
```

**A restricted usage writer that expires**

High-frequency usage writers only need `usage:write`.

Arguments:

```json
{
  "name": "Usage ingest",
  "kind": "brand",
  "brandId": "brd_01m4ttwd3veh9apbewxheq5xh5",
  "scopes": ["usage:write"],
  "expiresAt": "2026-10-20T09:30:00.000Z",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "key_01m4ttwd3tesktp0hc6bpxwkpd",
  "object": "api_key",
  "kind": "brand",
  "livemode": false,
  "name": "Usage ingest",
  "prefix": "or_test_acme",
  "scopes": ["usage:write"],
  "status": "active",
  "orgId": "org_01m4ttwd3veh9apbewxheq5xh3",
  "entityId": "ent_01m4ttwd3veh9apbewxheq5xh4",
  "brandId": "brd_01m4ttwd3veh9apbewxheq5xh5",
  "createdBy": "usr_01m4ttwd3veh9apbewxheq5xh6",
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-13T12:00:00.000Z",
  "expiresAt": "2026-10-20T09:30:00.000Z",
  "lastUsedAt": null,
  "revokedAt": null,
  "secret": "or_test_acme-app_ExampleOnlyNotARealKey000000000000000000000"
}
```

## `api_keys_get`

**Get an API key.** Operation [`api_keys.get`](https://ownrecord.io/docs/reference/api-keys-get.md) · Organization level · permission `keys:read` · risk tier `read` · since M1

Returns one API key by its ID (`key_…`): name, kind, binding, scopes, status, and when it was last used.
Never returns the secret or its hash. Keys of another mode, or outside the brand or entity of the request,
are `resource_not_found`.

Annotations: `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument    | Type         | Required | Description                                                                        |
| ----------- | ------------ | -------- | ---------------------------------------------------------------------------------- |
| `id`        | ID (`key_…`) | yes      | –                                                                                  |
| `entity_id` | ID (`ent_…`) | no       | Optional: act inside this legal entity (ent\_…) instead of the whole organization. |
| `brand_id`  | ID (`brd_…`) | no       | Optional: act inside this brand (brd\_…) instead of the whole organization.        |

- **Scope:** the whole organization, or one legal entity or brand of it with `entity_id` or `brand_id`.
- **Read-only:** changes nothing; `idempotency_key` is ignored.

### Errors

- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): No key with this ID is visible in the scope and mode of the request.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't see API keys here.

### Example

**A brand key that was used recently**

Arguments:

```json
{
  "id": "key_01m4ttwd3tesktp0hc6bpxwkpd"
}
```

Result `output`:

```json
{
  "id": "key_01m4ttwd3tesktp0hc6bpxwkpd",
  "object": "api_key",
  "kind": "brand",
  "livemode": false,
  "name": "Billing backend",
  "prefix": "or_test_acme",
  "scopes": ["customers:write", "subscriptions:write", "usage:write"],
  "status": "active",
  "orgId": "org_01m4ttwd3veh9apbewxheq5xh3",
  "entityId": "ent_01m4ttwd3veh9apbewxheq5xh4",
  "brandId": "brd_01m4ttwd3veh9apbewxheq5xh5",
  "createdBy": "usr_01m4ttwd3veh9apbewxheq5xh6",
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-13T12:00:00.000Z",
  "expiresAt": null,
  "lastUsedAt": "2026-10-20T09:30:00.000Z",
  "revokedAt": null
}
```

## `api_keys_list`

**List API keys.** Operation [`api_keys.list`](https://ownrecord.io/docs/reference/api-keys-list.md) · Organization level · permission `keys:read` · risk tier `read` · since M1

Lists the API keys you can see, newest first, in the mode of the request. Never returns secrets or hashes:
keys are recognised by `prefix` (their first 12 characters) and `name`.

An organization-level request sees every key of the organization (org, entity and brand keys); an entity
request sees the entity's keys and its brands' keys; a brand request sees only that brand's keys. Filter by
`kind`, `entityId`, `brandId` and `status` (`active`, `expired`, `revoked`). Paginate with `limit` (1–100,
default 10) and `starting_after` (the `next_cursor` of the previous page).

Annotations: `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument         | Type                                  | Required | Description                                                                        |
| ---------------- | ------------------------------------- | -------- | ---------------------------------------------------------------------------------- |
| `limit`          | integer                               | no       | (1–100)                                                                            |
| `starting_after` | ID (`key_…`)                          | no       | –                                                                                  |
| `kind`           | one of `org`, `entity`, `brand`       | no       | –                                                                                  |
| `entityId`       | ID (`ent_…`)                          | no       | –                                                                                  |
| `brandId`        | ID (`brd_…`)                          | no       | –                                                                                  |
| `status`         | one of `active`, `expired`, `revoked` | no       | –                                                                                  |
| `entity_id`      | ID (`ent_…`)                          | no       | Optional: act inside this legal entity (ent\_…) instead of the whole organization. |
| `brand_id`       | ID (`brd_…`)                          | no       | Optional: act inside this brand (brd\_…) instead of the whole organization.        |

- **Scope:** the whole organization, or one legal entity or brand of it with `entity_id` or `brand_id`.
- **Read-only:** changes nothing; `idempotency_key` is ignored.

### Errors

- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): A filter or pagination value is invalid.
- [`scope_mismatch`](https://ownrecord.io/docs/reference/errors.md#scope_mismatch): A filter names an entity or brand outside the scope of the request.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't see API keys here.

### Examples

**Active keys of a brand**

Arguments:

```json
{
  "brandId": "brd_01m4ttwd3veh9apbewxheq5xh5",
  "status": "active"
}
```

Result `output`:

```json
{
  "object": "list",
  "data": [
    {
      "id": "key_01m4ttwd3tesktp0hc6bpxwkpd",
      "object": "api_key",
      "kind": "brand",
      "livemode": false,
      "name": "Billing backend",
      "prefix": "or_test_acme",
      "scopes": ["customers:write", "subscriptions:write", "usage:write"],
      "status": "active",
      "orgId": "org_01m4ttwd3veh9apbewxheq5xh3",
      "entityId": "ent_01m4ttwd3veh9apbewxheq5xh4",
      "brandId": "brd_01m4ttwd3veh9apbewxheq5xh5",
      "createdBy": "usr_01m4ttwd3veh9apbewxheq5xh6",
      "createdAt": "2026-10-13T12:00:00.000Z",
      "updatedAt": "2026-10-13T12:00:00.000Z",
      "expiresAt": null,
      "lastUsedAt": "2026-10-20T09:30:00.000Z",
      "revokedAt": null
    }
  ],
  "has_more": false,
  "next_cursor": null
}
```

**Two keys per page**

Arguments:

```json
{
  "limit": 2
}
```

Result `output`:

```json
{
  "object": "list",
  "data": [
    {
      "id": "key_01m4ttwd3veh9apbewxheq5xh1",
      "object": "api_key",
      "kind": "org",
      "livemode": false,
      "name": "Automation",
      "prefix": "or_test_acme",
      "scopes": ["customers:write", "subscriptions:write", "usage:write"],
      "status": "active",
      "orgId": "org_01m4ttwd3veh9apbewxheq5xh3",
      "entityId": null,
      "brandId": null,
      "createdBy": "usr_01m4ttwd3veh9apbewxheq5xh6",
      "createdAt": "2026-10-13T12:00:00.000Z",
      "updatedAt": "2026-10-13T12:00:00.000Z",
      "expiresAt": null,
      "lastUsedAt": null,
      "revokedAt": null
    },
    {
      "id": "key_01m4ttwd3tesktp0hc6bpxwkpd",
      "object": "api_key",
      "kind": "brand",
      "livemode": false,
      "name": "Billing backend",
      "prefix": "or_test_acme",
      "scopes": ["customers:write", "subscriptions:write", "usage:write"],
      "status": "active",
      "orgId": "org_01m4ttwd3veh9apbewxheq5xh3",
      "entityId": "ent_01m4ttwd3veh9apbewxheq5xh4",
      "brandId": "brd_01m4ttwd3veh9apbewxheq5xh5",
      "createdBy": "usr_01m4ttwd3veh9apbewxheq5xh6",
      "createdAt": "2026-10-13T12:00:00.000Z",
      "updatedAt": "2026-10-13T12:00:00.000Z",
      "expiresAt": null,
      "lastUsedAt": null,
      "revokedAt": null
    }
  ],
  "has_more": true,
  "next_cursor": "key_01m4ttwd3tesktp0hc6bpxwkpd"
}
```

## `api_keys_revoke`

**Revoke an API key.** Operation [`api_keys.revoke`](https://ownrecord.io/docs/reference/api-keys-revoke.md) · Organization level · permission `keys:admin` · risk tier `security` · since M1

Revokes an API key. It stops working immediately: every later request with it fails with `api_key_invalid`.
Revocation is permanent; create a new key to replace it. Revoking a key that is already revoked changes
nothing and returns it (the original `revokedAt` stays).

Annotations: `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument          | Type         | Required | Description                                                                                                                                                                                              |
| ----------------- | ------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`              | ID (`key_…`) | yes      | –                                                                                                                                                                                                        |
| `entity_id`       | ID (`ent_…`) | no       | Optional: act inside this legal entity (ent\_…) instead of the whole organization.                                                                                                                       |
| `brand_id`        | ID (`brd_…`) | no       | Optional: act inside this brand (brd\_…) instead of the whole organization.                                                                                                                              |
| `idempotency_key` | string       | no       | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–255 characters) |
| `dry_run`         | boolean      | no       | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default `false`)                                                                                 |

- **Scope:** the whole organization, or one legal entity or brand of it with `entity_id` or `brand_id`.
- **Idempotency:** the result returns the `idempotency_key` used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
- **Dry run:** supported. `dry_run: true` checks permissions and rules and computes the result, then rolls everything back.

### Errors

- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): No key with this ID is visible in the scope and mode of the request.
- [`version_conflict`](https://ownrecord.io/docs/reference/errors.md#version_conflict): The key changed at the same moment; retry.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't manage API keys here.

### Example

**Revoke a leaked key**

Arguments:

```json
{
  "id": "key_01m4ttwd3tesktp0hc6bpxwkpd",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "key_01m4ttwd3tesktp0hc6bpxwkpd",
  "object": "api_key",
  "kind": "brand",
  "livemode": false,
  "name": "Billing backend",
  "prefix": "or_test_acme",
  "scopes": ["customers:write", "subscriptions:write", "usage:write"],
  "status": "revoked",
  "orgId": "org_01m4ttwd3veh9apbewxheq5xh3",
  "entityId": "ent_01m4ttwd3veh9apbewxheq5xh4",
  "brandId": "brd_01m4ttwd3veh9apbewxheq5xh5",
  "createdBy": "usr_01m4ttwd3veh9apbewxheq5xh6",
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-20T09:30:00.000Z",
  "expiresAt": null,
  "lastUsedAt": null,
  "revokedAt": "2026-10-20T09:30:00.000Z"
}
```

## `api_keys_update`

**Rename an API key.** Operation [`api_keys.update`](https://ownrecord.io/docs/reference/api-keys-update.md) · Organization level · permission `keys:admin` · risk tier `security` · since M1

Changes an API key's `name`, the label you see in the console and request logs. Nothing else about a key
changes after creation: to change its scopes, brand or mode, create a new key and revoke the old one.
Revoked keys can't be renamed.

Annotations: `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument          | Type         | Required | Description                                                                                                                                                                                              |
| ----------------- | ------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`              | ID (`key_…`) | yes      | –                                                                                                                                                                                                        |
| `name`            | string       | yes      | (1–100 characters)                                                                                                                                                                                       |
| `entity_id`       | ID (`ent_…`) | no       | Optional: act inside this legal entity (ent\_…) instead of the whole organization.                                                                                                                       |
| `brand_id`        | ID (`brd_…`) | no       | Optional: act inside this brand (brd\_…) instead of the whole organization.                                                                                                                              |
| `idempotency_key` | string       | no       | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–255 characters) |
| `dry_run`         | boolean      | no       | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default `false`)                                                                                 |

- **Scope:** the whole organization, or one legal entity or brand of it with `entity_id` or `brand_id`.
- **Idempotency:** the result returns the `idempotency_key` used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
- **Dry run:** supported. `dry_run: true` checks permissions and rules and computes the result, then rolls everything back.

### Errors

- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): The name is empty or longer than 100 characters.
- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): No key with this ID is visible in the scope and mode of the request.
- [`api_key_revoked`](https://ownrecord.io/docs/reference/errors.md#api_key_revoked): The key is revoked.
- [`version_conflict`](https://ownrecord.io/docs/reference/errors.md#version_conflict): The key changed at the same moment; retry.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't manage API keys here.

### Example

**Rename a key**

Arguments:

```json
{
  "id": "key_01m4ttwd3tesktp0hc6bpxwkpd",
  "name": "Billing backend (eu-west)",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "key_01m4ttwd3tesktp0hc6bpxwkpd",
  "object": "api_key",
  "kind": "brand",
  "livemode": false,
  "name": "Billing backend (eu-west)",
  "prefix": "or_test_acme",
  "scopes": ["customers:write", "subscriptions:write", "usage:write"],
  "status": "active",
  "orgId": "org_01m4ttwd3veh9apbewxheq5xh3",
  "entityId": "ent_01m4ttwd3veh9apbewxheq5xh4",
  "brandId": "brd_01m4ttwd3veh9apbewxheq5xh5",
  "createdBy": "usr_01m4ttwd3veh9apbewxheq5xh6",
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-20T09:30:00.000Z",
  "expiresAt": null,
  "lastUsedAt": null,
  "revokedAt": null
}
```
