---
title: "Suspend OAuth app"
method: POST
path: "/oauth-clients/{appId}/suspend"
tags: ["OAuth Apps"]
---

# Suspend OAuth app

`POST /oauth-clients/{appId}/suspend`

## Request

- Base URL: `https://app.pipeshub.com/api/v1`
- URL: `https://app.pipeshub.com/api/v1/oauth-clients/{appId}/suspend`
- Auth: HTTP bearer

Moves an OAuth app to `status: "suspended"`, blocking new token issuance at `POST /oauth2/token` and the authorization-code consent flow. Tokens that have already been issued remain valid until their natural expiry — call `POST /oauth-clients/{appId}/revoke-all-tokens` immediately afterwards if you need an immediate lockout.

Use this for temporary suspensions where you intend to reactivate later. For permanent removal, use `DELETE /oauth-clients/{appId}`. Suspending an app that is already suspended returns `400`.

Creator-only.

## Path parameters

- `appId` string, required

## Response `200`

OAuth app suspended

- SuspendOAuthAppResponse: Response body for `POST /oauth-clients/{appId}/suspend` (`oauth.app.controller.ts` `suspendApp`). Suspended app (never includes `clientSecret`) is nested under `app`.
  - `message` string, required
  - `app` OAuthAppResponse, required: OAuth app details (without secret). Fields under `required:` always appear in `toAppResponse` (`oauth.app.service.ts`); optional URL/description fields are only present when set by the caller.
    - `id` string, required: App ID
    - `slug` string, required: URL-friendly app slug
    - `clientId` string, required: OAuth client ID
    - `name` string, required: App name
    - `description` string: App description
    - `redirectUris` string[], required: Allowed redirect URIs (always returned; may be empty)
    - `allowedGrantTypes` string[], required: Allowed grant types
    - `allowedScopes` string[], required: Allowed scopes
    - `status` 'active' | 'suspended' | 'revoked', required: App status
    - `homepageUrl` string, uri: App homepage
    - `privacyPolicyUrl` string, uri: Privacy policy URL
    - `termsOfServiceUrl` string, uri: Terms of service URL
    - `isConfidential` boolean, required: Whether app is a confidential client
    - `accessTokenLifetime` integer, required: Access token lifetime in seconds
    - `refreshTokenLifetime` integer, required: Refresh token lifetime in seconds
    - `knowledgeLimit` KnowledgeLimit: What a connected app may see: an OAuth app's limit is picked by its creator and holds for every user who connects it; a personal access token's by its owner. Search, chat, agent chat and every record read by id stay inside it, and inside the connecting user's own permissions. Omit it for everything the user can see. Sent, it must name at least one id and at most 50 in all, since it is signed into every token the app gets.
      - `apps` string[]: Whole connectors and collections.
      - `recordGroups` string[]: Record groups (a drive, space or project), with the groups nested in them.
      - `records` string[]: Folders or records, with everything under them.
    - `tokenIdentityUserId` string: The service account this app's own `client_credentials` tokens act as. Absent means the app has no service account and its tokens act as whoever created it. Apps registered since a service account became required always have one; an older app may not, and those are the ones an administrator still has to move.
    - `createdAt` string, date-time, required: Creation timestamp
    - `updatedAt` string, date-time, required: Last update timestamp

## Other responses

- `400`: The app is already suspended, or `appId` is not a 24-character hex id (`VALIDATION_ERROR` naming `params.appId`) Also returned, before authentication, when a query value, or any string in a JSON or form-encoded body, looks like markup to the app-wide XSS filter: an HTML tag, a script tag, `<iframe`, `<object`, `<embed` or `<svg` tag, a word starting with `on` followed by `=` (meant for inline event handlers such as `onerror=`, it also catches plain text such as `one=1`), a `javascript:` or `data:text/html` URL, an encoded script tag (`&lt;script`, `%3cscript`), or any string longer than 100,000 characters (`HTTP_BAD_REQUEST`, "HTML tags, scripts, and XSS content are not allowed in ...", naming the field or query parameter; a string over 100,000 characters is refused as too long instead). A JSON body that does not parse is refused the same way, also before authentication (`HTTP_BAD_REQUEST`, "The request body is not valid JSON.").
- `401`: Unauthorized
- `403`: Bearer token is an OAuth access token or personal access token rather than a user session, or the caller is a service account. An app the caller did not create is 404, not 403.
- `404`: OAuth app not found or not visible to this caller (each user only sees apps they created)
- `429`: Rate limit exceeded
- `500`: Internal server error. A JSON body larger than 10 MB, or a form-encoded body larger than 100 KB or with more than 1000 fields, is answered `413` by the app-wide body parsers instead, also before authentication.

## Changes

- **2026-10-28** `dbb5e203b445`: 5 info
  - added the non-success response with the status `500`
  - added the optional property `error/requestId` to the response with the `400` status
  - added the optional property `error/requestId` to the response with the `401` status
  - added the optional property `error/requestId` to the response with the `403` status
  - …1 more
- **2026-10-26** `fc76aa045994`: 2 info
  - added the optional property `app/knowledgeLimit` to the response with the `200` status
  - added the optional property `app/tokenIdentityUserId` to the response with the `200` status

[Operation history](https://skmtc.dev/pipeshub-ai/apis/pipeshub-api/changes/oauth-clients/:appId/suspend/post.md)

---

[API](https://skmtc.dev/pipeshub-ai/apis/pipeshub-api.md) - [All operations](https://skmtc.dev/pipeshub-ai/apis/pipeshub-api/llms.txt) - [OpenAPI document](https://skmtc.dev/pipeshub-ai/apis/pipeshub-api/revisions/dbb5e203b445?raw)
