# Ask for an agent the app owns

> The app_installations.create_agent operation: POST /api/v1/app_installations/{id}/agents in the REST API and the app_installations_create_agent tool in the MCP server.

Called by an installed app with its own access token: asks for an **agent the app owns** in the organization,
for work that runs without the app's user session (for example a filing agent). It is a security operation, so
the call always answers `202` with an approval request: an owner or admin of the organization (not the person
who installed the app) approves it in the console, and the approved run creates the agent exactly as asked.
Poll the approval (`approvals.get`) or watch `approval.resolved`; its `resultRef` is the new agent's ID, and
`agents.get` returns its `clientId`.

The agent holds one `role` at a `scope` like a member, works in test mode unless `livemodeAccess` is asked for
and the installer may use live mode, and its token `scopes` stay within the scopes the app was granted (default:
everything the role can use among them). It authenticates with `private_key_jwt` only: give its public keys in
`jwks`. Its calls are attributed to the agent, the app's installation and the person who installed the app; it
stops when the app is paused or uninstalled, or the installer leaves the organization. `id` is your own
installation (`ains_…`); an installation limited to some entities or brands can't ask for agents.

## At a glance

- **REST:** `POST /api/v1/app_installations/{id}/agents`
- **MCP tool:** `app_installations_create_agent`
- **Operation ID:** `app_installations.create_agent`
- **Scope:** Organization
- **Permission:** `agents:admin`
- **Risk tier:** `security`
- **Dry run:** Supported (send `Ownrecord-Dry-Run: true`)
- **Idempotency:** `Idempotency-Key` required
- **Modes:** Test and live
- **Authentication:** API key with the `agents:admin` scope
- **Since:** M13

## Request

### Path parameters

| Field | Type        | Description                                          |
| ----- | ----------- | ---------------------------------------------------- |
| `id`  | `ains_…` ID | The calling app's installation in this organization. |

### Body

A JSON object (`Content-Type: application/json`):

| Field                               | Type                                                                                               | Required | Description                                                        |
| ----------------------------------- | -------------------------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------ |
| `name`                              | string                                                                                             | Yes      |                                                                    |
| `description`                       | string                                                                                             | No       |                                                                    |
| `contact`                           | string                                                                                             | No       | Who to contact about the agent (default: none).                    |
| `vendor`                            | string                                                                                             | No       | Model or vendor description (default: the app's publisher).        |
| `role`                              | one of `org_admin`, `billing_admin`, `finance`, `tax_manager`, `support`, `developer`, `viewer`    | Yes      |                                                                    |
| `scope`                             | object                                                                                             | No       |                                                                    |
| `livemodeAccess`                    | boolean                                                                                            | No       |                                                                    |
| `scopes`                            | array of one of 84 values, e.g. `org:read`, `org:write`, `org:admin` (all in the OpenAPI document) | No       |                                                                    |
| `jwks`                              | object                                                                                             | Yes      | The agent's public keys for private_key_jwt client authentication. |
| `jwks.keys`                         | array of object                                                                                    | Yes      |                                                                    |
| `jwks.keys[].kty`                   | one of `RSA`, `EC`, `OKP`                                                                          | Yes      |                                                                    |
| `jwks.keys[].kid`                   | string                                                                                             | No       |                                                                    |
| `jwks.keys[].use`                   | `"sig"`                                                                                            | No       |                                                                    |
| `jwks.keys[].alg`                   | string                                                                                             | No       |                                                                    |
| `limits`                            | object                                                                                             | No       |                                                                    |
| `limits.rps`                        | integer (1 to 10000) or null                                                                       | No       |                                                                    |
| `limits.mutationsPerDay`            | integer (0 to 10000000) or null                                                                    | No       |                                                                    |
| `limits.amountLimits`               | array of object                                                                                    | No       |                                                                    |
| `limits.amountLimits[].operationId` | string                                                                                             | Yes      |                                                                    |
| `limits.amountLimits[].perCall`     | money or null                                                                                      | No       |                                                                    |
| `limits.amountLimits[].perDay`      | money or null                                                                                      | No       |                                                                    |
| `toolSets`                          | array of string                                                                                    | No       |                                                                    |

## Response

`201 Created` with a JSON object:

| Field                                     | Type                                                                                               | Description |
| ----------------------------------------- | -------------------------------------------------------------------------------------------------- | ----------- |
| `agent`                                   | object                                                                                             |             |
| `agent.id`                                | `agt_…` ID                                                                                         |             |
| `agent.object`                            | `"agent"`                                                                                          |             |
| `agent.orgId`                             | `org_…` ID                                                                                         |             |
| `agent.name`                              | string                                                                                             |             |
| `agent.description`                       | string or null                                                                                     |             |
| `agent.contact`                           | string or null                                                                                     |             |
| `agent.vendor`                            | string or null                                                                                     |             |
| `agent.ownerType`                         | one of `org`, `app`                                                                                |             |
| `agent.appInstallationId`                 | `ains_…` ID or null                                                                                |             |
| `agent.role`                              | one of `org_admin`, `billing_admin`, `finance`, `tax_manager`, `support`, `developer`, `viewer`    |             |
| `agent.scope`                             | object                                                                                             |             |
| `agent.livemodeAccess`                    | boolean                                                                                            |             |
| `agent.limits`                            | object                                                                                             |             |
| `agent.limits.rps`                        | integer or null                                                                                    |             |
| `agent.limits.mutationsPerDay`            | integer or null                                                                                    |             |
| `agent.limits.amountLimits`               | array of object                                                                                    |             |
| `agent.limits.amountLimits[].operationId` | string                                                                                             |             |
| `agent.limits.amountLimits[].perCall`     | money or null                                                                                      |             |
| `agent.limits.amountLimits[].perDay`      | money or null                                                                                      |             |
| `agent.toolSets`                          | array of string                                                                                    |             |
| `agent.status`                            | one of `active`, `paused`, `revoked`                                                               |             |
| `agent.clientId`                          | `ocl_…` ID                                                                                         |             |
| `agent.authMethod`                        | one of `client_secret_basic`, `client_secret_post`, `private_key_jwt`                              |             |
| `agent.secretPrefix`                      | string or null                                                                                     |             |
| `agent.dpopBoundAccessTokens`             | boolean                                                                                            |             |
| `agent.scopes`                            | array of one of 84 values, e.g. `org:read`, `org:write`, `org:admin` (all in the OpenAPI document) |             |
| `agent.createdBy`                         | string                                                                                             |             |
| `agent.createdAt`                         | string                                                                                             |             |
| `agent.updatedAt`                         | string                                                                                             |             |
| `agent.pausedAt`                          | string or null                                                                                     |             |
| `agent.revokedAt`                         | string or null                                                                                     |             |
| `credentials`                             | object                                                                                             |             |
| `credentials.clientId`                    | `ocl_…` ID                                                                                         |             |
| `credentials.clientSecret`                | string or null                                                                                     |             |
| `credentials.tokenEndpoint`               | string                                                                                             |             |
| `credentials.authMethod`                  | one of `none`, `client_secret_basic`, `client_secret_post`, `private_key_jwt`                      |             |

## Examples

### A filing agent for the whole organization (after approval)

```http
POST /api/v1/app_installations/ains_01m4ttwd3veh9apbewxheq5xhc/agents
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json

{
  "name": "Ledgerly Filing Agent",
  "description": "Prepares and files the quarterly returns",
  "contact": "filings@ledgerly.example",
  "role": "finance",
  "scopes": [
    "filings:read",
    "filings:write",
    "tasks:read",
    "tasks:write"
  ],
  "jwks": {
    "keys": [
      {
        "kty": "EC",
        "crv": "P-256",
        "kid": "ledgerly-filing-1",
        "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU",
        "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0"
      }
    ]
  }
}
```

Response (`201`):

```json
{
  "agent": {
    "id": "agt_01m4ttwd3veh9apbewxheq5xh8",
    "object": "agent",
    "orgId": "org_01m4ttwd3veh9apbewxheq5xh3",
    "name": "Ledgerly Filing Agent",
    "description": "Prepares and files the quarterly returns",
    "contact": "filings@ledgerly.example",
    "vendor": "Ledgerly AI B.V.",
    "ownerType": "app",
    "appInstallationId": "ains_01m4ttwd3veh9apbewxheq5xhc",
    "role": "finance",
    "scope": {
      "type": "org"
    },
    "livemodeAccess": false,
    "limits": {
      "rps": null,
      "mutationsPerDay": null,
      "amountLimits": []
    },
    "toolSets": [],
    "status": "active",
    "clientId": "ocl_01m4ttwd3veh9apbewxheq5xh9",
    "authMethod": "private_key_jwt",
    "secretPrefix": null,
    "dpopBoundAccessTokens": false,
    "scopes": ["filings:read", "filings:write", "tasks:read", "tasks:write"],
    "createdBy": "usr_01m4ttwd3veh9apbewxheq5xh6",
    "createdAt": "2026-10-25T09:00:00.000Z",
    "updatedAt": "2026-10-25T09:00:00.000Z",
    "pausedAt": null,
    "revokedAt": null
  },
  "credentials": {
    "clientId": "ocl_01m4ttwd3veh9apbewxheq5xh9",
    "clientSecret": null,
    "tokenEndpoint": "https://ownrecord.io/oauth/token",
    "authMethod": "private_key_jwt"
  }
}
```

## Errors

| Code                                                                                                         | HTTP status | When                                                                                                | What to do                                                                                                                                                                                                                                                                            |
| ------------------------------------------------------------------------------------------------------------ | ----------- | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`approval_required`](https://ownrecord.io/docs/reference/errors.md#approval_required)                       | 202         | Always for the app's own call: an owner or admin approves it in the console.                        | Nothing ran yet. An approver reviews `approval.summary` and the dry-run result in the console (`approval.approveUrl`). Poll `GET /approvals/{id}` or watch for the `approval.resolved` event; sending the same request again with the same Idempotency-Key returns the same approval. |
| [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed)                       | 400         | A field is invalid, e.g. a scope the app wasn't granted or the role can't use.                      | Fix the fields listed in `details.issues` and send the request again.                                                                                                                                                                                                                 |
| [`agent_role_invalid`](https://ownrecord.io/docs/reference/errors.md#agent_role_invalid)                     | 400         | The role is org_owner, or isn't allowed at the scope's type.                                        | Choose a role other than org_owner, at a scope the role allows (org, entity or brand; see the role table in the docs).                                                                                                                                                                |
| [`agent_delegation_not_allowed`](https://ownrecord.io/docs/reference/errors.md#agent_delegation_not_allowed) | 403         | The agent would get a permission (or live access) the app doesn't have at its scope.                | Choose a narrower role, scope or token scopes, turn off live mode for the agent, or ask an organization owner to do it.                                                                                                                                                               |
| [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found)                     | 404         | The installation isn't the calling app's, or the scope's entity or brand doesn't exist.             | Check the ID and that your credentials are scoped to the right organization, entity, brand and mode.                                                                                                                                                                                  |
| [`app_installation_paused`](https://ownrecord.io/docs/reference/errors.md#app_installation_paused)           | 403         | The installation was paused before the approved run.                                                | An organization owner or admin can resume it under Organization → Agents & apps. If the member who installed it left, an owner must re-assign it first; calls then resume with the same tokens.                                                                                       |
| [`oauth_client_disabled`](https://ownrecord.io/docs/reference/errors.md#oauth_client_disabled)               | 409         | The app was disabled before the approved run.                                                       | Register a new app, or create a new agent.                                                                                                                                                                                                                                            |
| [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied)                       | 403         | The caller isn't an installed app (people create agents with agents.create), or lacks agents:admin. | Ask an organization owner or admin for a role or scope that allows this action.                                                                                                                                                                                                       |

It can also return these [common errors](https://ownrecord.io/docs/reference.md#common-errors): [`api_version_unsupported`](https://ownrecord.io/docs/reference/errors.md#api_version_unsupported), [`invalid_json`](https://ownrecord.io/docs/reference/errors.md#invalid_json), [`unsupported_media_type`](https://ownrecord.io/docs/reference/errors.md#unsupported_media_type), [`authentication_required`](https://ownrecord.io/docs/reference/errors.md#authentication_required), [`api_key_invalid`](https://ownrecord.io/docs/reference/errors.md#api_key_invalid), [`idempotency_key_required`](https://ownrecord.io/docs/reference/errors.md#idempotency_key_required), [`idempotency_key_reused`](https://ownrecord.io/docs/reference/errors.md#idempotency_key_reused), [`idempotency_in_progress`](https://ownrecord.io/docs/reference/errors.md#idempotency_in_progress), [`approval_rejected`](https://ownrecord.io/docs/reference/errors.md#approval_rejected), [`approval_expired`](https://ownrecord.io/docs/reference/errors.md#approval_expired), [`stale_approval`](https://ownrecord.io/docs/reference/errors.md#stale_approval), [`approval_policy_denied`](https://ownrecord.io/docs/reference/errors.md#approval_policy_denied), [`rate_limited`](https://ownrecord.io/docs/reference/errors.md#rate_limited), [`internal_error`](https://ownrecord.io/docs/reference/errors.md#internal_error).
