---
title: "Update OAuth app"
method: PUT
path: "/oauth-clients/{appId}"
tags: ["OAuth Apps"]
---

# Update OAuth app

`PUT /oauth-clients/{appId}`

Update an OAuth app's configuration. All body fields are optional — supply only what should change. URL fields (`homepageUrl`, `privacyPolicyUrl`, `termsOfServiceUrl`) accept `null` to clear them.

Creator-only: even org admins cannot edit apps owned by other users.

When modifying `allowedScopes`, the new set must remain a subset of the caller's role-aware scope list (same rule as `GET /oauth-clients/scopes`). When adding `authorization_code` to `allowedGrantTypes`, `redirectUris` becomes required and must contain at least one URI; otherwise the request is rejected with `400` by the Zod refine on `updateAppSchema`.

This endpoint never rotates the client secret — use `POST /oauth-clients/{appId}/regenerate-secret` for that.

## Path parameters

- `appId` string, required

## Request body

- UpdateOAuthAppRequest — Request to update an OAuth app (`updateAppSchema` in `oauth.validators.ts`). All fields are optional — include only fields that should change. URL fields (`homepageUrl`, `privacyPolicyUrl`, `termsOfServiceUrl`) accept `null` to clear them (nullable in Zod). **Redirect rule (Zod refine):** If `allowedGrantTypes` includes `authorization_code` and `redirectUris` is present in the body, `redirectUris` must contain at least one URI.
  - `name` string — App name
  - `description` string — App description
  - `redirectUris` string[] — Allowed redirect URIs (up to 10). Required when `authorization_code` grant type is enabled. Preserved in the database even if `authorization_code` is removed from grant types.
  - `allowedGrantTypes` string[]
  - `allowedScopes` string[]
  - `homepageUrl` string, uri, nullable
  - `privacyPolicyUrl` string, uri, nullable
  - `termsOfServiceUrl` string, uri, nullable
  - `accessTokenLifetime` integer
  - `refreshTokenLifetime` integer

## Response `200`

OAuth app updated

- UpdateOAuthAppResponse — Response body for `PUT /oauth-clients/{appId}` (`oauth.app.controller.ts` `updateApp`). Updated 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
    - `createdAt` string, date-time, required — Creation timestamp
    - `updatedAt` string, date-time, required — Last update timestamp

## Other responses

- `400` — Validation error
- `401` — Unauthorized
- `403` — Forbidden — caller cannot access this OAuth app (creator-only; see OAuth Apps tag).
- `404` — OAuth app not found or not visible to this caller (each user only sees apps they created)
- `429` — Rate limit exceeded

---

[API](https://skmtc.net/pipeshub-ai/apis/pipeshub-api.md) · [All operations](https://skmtc.net/pipeshub-ai/apis/pipeshub-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/pipeshub-ai/pipeshub-api/versions/abd27cfefc73/schema)
