Outcome
Create a credential scoped to one organization, application, permission set, and optional resources.
Before you start
-
Verify the credential with topolo whoami --json and inspect the exact published action. Stop on runtime authorization denial; do not widen permissions or switch contexts automatically.
-
Discover the target application and use the organization carried by the credential. A request organizationId is not a context override.
-
Use api_keys.options for the exact application and active organization. Select resource bindings first, then the allowed scope intersection; general resource discovery alone is not key eligibility.
-
Validate and plan the exact request before execution. --confirm is a transport guard; the intended change must already be authorized.
-
The sample HTTP secret is synthetic and unusable. Local CLI and stdio MCP use protected opaque references. Preserve those references unchanged; never extract plaintext into files, commands or logs.
Discover the target
topolo actions get app_topolo_auth.api_keys.create --detail --json
topolo apps --json
topolo whoami --json
Inspect app_topolo_auth.api_keys.options before preparing credential creation or changes.
Input fields
| Field | Required | Meaning |
|---|---|---|
organizationId | No | Active organization identifier from credential identity. It does not override the credential context; conflicting organization selections are refused. |
appId | Yes | Exact target application identifier from credential-scoped discovery. Key grants and workspace policy are evaluated for this application. |
name | Yes | Human-readable key label; updating it does not rotate the secret. |
scopes | Yes | Exact scope names permitted by api_keys.options. Without bindings select organizationWideScopes; with bindings select the intersection of allowedScopes across every selected resource. |
resourceBindings | No | Authorized resourceType/resourceId pairs from api_keys.options. General resource discovery alone is not the key-management authorization decision. |
expiresInDays | No | Non-negative lifetime in days. Creation defaults to 90 days. Zero means no expiry; an omitted update leaves expiry unchanged. Positive updates calculate expiry from the update time. |
Effects
-
Creates a new application credential with the reviewed scopes, resource bindings and lifetime.
-
The target HTTP response contains the one-time secret. Local CLI and stdio MCP protect credential values with opaque references; preserve those references instead of extracting or logging plaintext.
Interpret the response
data.key contains the allocated id, normalized scopes/bindings, status and expiry. expiresAt is epoch seconds or null; it is distinct from the ISO response timestamp. The fixture secret is deliberately synthetic. Local CLI/stdio MCP credential references may replace the HTTP secret shown in a handler fixture.
Worked examples
These are raw target-handler responses, not gateway or CLI envelopes. They were captured in a local test with simulated dependencies. Fixture identifiers and credentials are illustrative and cannot authorize a live call. Discover your own organization, application, workspace and key ids. Local CLI/stdio MCP may replace credential strings with protected references; preserve the reference and do not extract the underlying secret.
Minimal example
Input · payload.json:
{
"appId": "app_topolo_docs",
"name": "Documentation read key",
"scopes": [
"docs:read"
]
}
CLI:
topolo actions validate app_topolo_auth.api_keys.create --data @payload.json --json
topolo actions plan app_topolo_auth.api_keys.create --data @payload.json --json
topolo actions call app_topolo_auth.api_keys.create --data @payload.json --confirm --json
MCP:
{
"tool": "topolo_call_action",
"arguments": {
"actionId": "app_topolo_auth.api_keys.create",
"input": {
"appId": "app_topolo_docs",
"name": "Documentation read key",
"scopes": [
"docs:read"
]
},
"confirm": true
}
}
Captured local handler response · HTTP 201:
{
"success": true,
"message": "API key created successfully",
"data": {
"key": {
"id": "key_minimal_fixture",
"name": "Documentation read key",
"organizationId": "org_fixture",
"organizationSlug": "fixture",
"appId": "app_topolo_docs",
"scopes": [
"docs:read"
],
"resourceBindings": [],
"expiresAt": 1798728496,
"createdAt": 1,
"status": "active",
"secret": "topo_fixture_key_minimal_fixture.fixturesecretonly"
}
},
"timestamp": "2026-10-02T14:48:16.388Z"
}
data.key contains the allocated id, normalized scopes/bindings, status and expiry. expiresAt is epoch seconds or null; it is distinct from the ISO response timestamp. The fixture secret is deliberately synthetic. Local CLI/stdio MCP credential references may replace the HTTP secret shown in a handler fixture.
Workflow example
Input · payload.json:
{
"appId": "app_topolo_docs",
"name": "Documentation read key",
"scopes": [
"docs:read"
],
"organizationId": "org_fixture",
"expiresInDays": 30,
"resourceBindings": [
{
"resourceType": "workspace",
"resourceId": "ws_created_workflow"
}
]
}
CLI:
topolo actions validate app_topolo_auth.api_keys.create --data @payload.json --json
topolo actions plan app_topolo_auth.api_keys.create --data @payload.json --json
topolo actions call app_topolo_auth.api_keys.create --data @payload.json --confirm --json
MCP:
{
"tool": "topolo_call_action",
"arguments": {
"actionId": "app_topolo_auth.api_keys.create",
"input": {
"appId": "app_topolo_docs",
"name": "Documentation read key",
"scopes": [
"docs:read"
],
"organizationId": "org_fixture",
"expiresInDays": 30,
"resourceBindings": [
{
"resourceType": "workspace",
"resourceId": "ws_created_workflow"
}
]
},
"confirm": true
}
}
Captured local handler response · HTTP 201:
{
"success": true,
"message": "API key created successfully",
"data": {
"key": {
"id": "key_workflow_fixture",
"name": "Documentation read key",
"organizationId": "org_fixture",
"organizationSlug": "fixture",
"appId": "app_topolo_docs",
"scopes": [
"docs:read"
],
"resourceBindings": [
{
"resourceType": "workspace",
"resourceId": "ws_created_workflow"
}
],
"expiresAt": 1793544496,
"createdAt": 1,
"status": "active",
"secret": "topo_fixture_key_workflow_fixture.fixturesecretonly"
}
},
"timestamp": "2026-10-02T14:48:16.405Z"
}
data.key contains the allocated id, normalized scopes/bindings, status and expiry. expiresAt is epoch seconds or null; it is distinct from the ISO response timestamp. The fixture secret is deliberately synthetic. Local CLI/stdio MCP credential references may replace the HTTP secret shown in a handler fixture.
Verify the result
-
Read api_keys.list with the original authorized credential and match data.key.id, application, organization, scope set, bindings, status and expiry.
-
Use the intended protected credential-store integration for credential handoff. A metadata read-back does not prove downstream execution or entitlement parity; do not extract a protected reference to invent that proof.
Errors and recovery
Captured local refusal · HTTP 403:
{
"input": {
"appId": "app_topolo_docs",
"name": "Denied key",
"scopes": [
"docs:publish"
]
},
"response": {
"success": false,
"error": {
"message": "Fixture selection policy refused the requested scopes",
"code": "FORBIDDEN",
"details": null,
"timestamp": "2026-10-02T14:48:16.394Z"
}
}
}
If this key was created in error, plan revocation for its exact id and verify status revoked. Local reference deletion is a separate cleanup step and does not revoke the upstream key.
Evidence boundary
The handler test proves response shape and control flow under the listed simulated dependencies. It does not prove real grant evaluation, durable SQL storage, protected credential-store handoff, target application synchronization or downstream use of a key.