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.
/api/v1/app_installations/{id}/agentsCalled 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-Keyrequired - Modes: Test and live
- Authentication: API key with the
agents:adminscope - 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)
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):
{
"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 | 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 | 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 | 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 | 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 | 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 | 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 | 409 | The app was disabled before the approved run. | Register a new app, or create a new agent. |
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: api_version_unsupported, invalid_json, unsupported_media_type, authentication_required, api_key_invalid, idempotency_key_required, idempotency_key_reused, idempotency_in_progress, approval_rejected, approval_expired, stale_approval, approval_policy_denied, rate_limited, internal_error.