Suspend an app everywhere
The platform.apps.suspend operation: POST /api/v1/platform/apps/{id}/suspend in the REST API and the platform_apps_suspend tool in the MCP server.
/api/v1/platform/apps/{id}/suspendPlatform admins: suspends a registered app in every organization at once, for abuse or a compromise, with a
reason. In one transaction:
- every access and refresh token, authorization code and grant the app holds is deleted, in every organization, and so are the tokens of the agents it owns;
- every installation that isn't uninstalled is paused (
pausedReason: platform_suspended); its organization can't resume it while the suspension lasts (app_suspended); - from then on the OAuth server issues the app nothing (authorization code, refresh, client credentials, with or without DPoP), introspection reports its tokens inactive, the agents it owns are refused, and nothing installs it.
The organization that registered the app and every organization with it installed get an app.suspended event,
and their owners and admins an email with the reason (they all read it; the admin isn't named). The platform
audit chain records the app, the reason and every organization concerned; each organization's own audit log
records its installation's pause. Suspending a suspended app changes nothing. platform.apps.reinstate lifts it.
Security tier; needs a sign-in within the last 15 minutes.
At a glance
- REST:
POST /api/v1/platform/apps/{id}/suspend - MCP tool:
platform_apps_suspend - Operation ID:
platform.apps.suspend - Scope: Platform
- Permission:
platform:admin - Risk tier:
security - Dry run: Supported (send
Ownrecord-Dry-Run: true) - Idempotency:
Idempotency-Keyrequired - Modes: Test and live
- Authentication: API key with the
platform:adminscope - Fresh sign-in: In the console, a sign-in within the last 15 minutes
- Since: M13
Request
Path parameters
| Field | Type | Description |
|---|---|---|
id | ocl_… ID |
Body
A JSON object (Content-Type: application/json):
| Field | Type | Required | Description |
|---|---|---|---|
reason | string | Yes | Why, in plain words. The app's organization and every organization that installed it read it. |
Response
200 OK with a JSON object:
| Field | Type | Description |
|---|---|---|
id | ocl_… ID | |
object | "platform_app" | |
name | string | |
publisher | string | |
org | object | |
org.id | org_… ID | |
org.slug | string | |
org.name | string | |
visibility | one of private, listed | |
listed | boolean | |
reviewStatus | one of not_submitted, pending, changes_requested, approved, rejected | |
status | one of active, disabled | |
suspension | object or null | |
createdAt | date-time | |
updatedAt | date-time | |
logoUrl | string or null | |
websiteUrl | string or null | |
installations | object | |
installations.organizations | integer | |
installations.active | integer | |
installations.paused | integer | |
installations.pausedBySuspension | integer | |
installations.revoked | integer | |
history | array of object | |
history[].action | one of suspended, reinstated | |
history[].at | date-time | |
history[].by | usr_… ID | |
history[].reason | string | |
history[].organizations | integer | |
history[].installationsPaused | integer or null | |
version | integer |
Examples
Suspend an app whose secret leaked
POST /api/v1/platform/apps/ocl_01m4ttwd3veh9apbewxheq5xha/suspend
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json
{
"reason": "Its client secret was published in a public repository; tokens were used from unknown networks."
}
Response (200):
{
"id": "ocl_01m4ttwd3veh9apbewxheq5xha",
"object": "platform_app",
"name": "Ledgerly AI",
"publisher": "Ledgerly AI B.V.",
"org": {
"id": "org_01m4ttwd3veh9apbewxheq5xhb",
"slug": "ledgerly",
"name": "Ledgerly"
},
"visibility": "listed",
"listed": true,
"reviewStatus": "approved",
"status": "active",
"suspension": {
"status": "suspended",
"reason": "Its client secret was published in a public repository; tokens were used from unknown networks.",
"suspendedAt": "2026-11-01T08:30:00.000Z",
"reinstatedAt": null,
"reinstateReason": null,
"suspendedBy": "usr_01m4ttwd3veh9apbewxheq5xhg",
"reinstatedBy": null
},
"createdAt": "2026-10-25T09:00:00.000Z",
"updatedAt": "2026-11-01T08:30:00.000Z",
"logoUrl": null,
"websiteUrl": "https://ledgerly.example",
"installations": {
"organizations": 12,
"active": 0,
"paused": 12,
"pausedBySuspension": 12,
"revoked": 2
},
"history": [
{
"action": "suspended",
"at": "2026-11-01T08:30:00.000Z",
"by": "usr_01m4ttwd3veh9apbewxheq5xhg",
"reason": "Its client secret was published in a public repository; tokens were used from unknown networks.",
"organizations": 12,
"installationsPaused": 12
}
],
"version": 5
}
Errors
| Code | HTTP status | When | What to do |
|---|---|---|---|
fresh_auth_required | 403 | Your last sign-in is older than 15 minutes: sign in again first. | Sign in again, then repeat the action within 15 minutes. |
validation_failed | 400 | No reason, or one longer than 2,000 characters. | Fix the fields listed in details.issues and send the request again. |
resource_not_found | 404 | No registered app has this ID (agents' and MCP clients aren't apps). | Check the ID and that your credentials are scoped to the right organization, entity, brand and mode. |
version_conflict | 409 | The app or one of its installations changed at the same moment; retry. | Fetch the object again and retry the change. |
permission_denied | 403 | You aren't a platform 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, rate_limited, internal_error.