---
title: "Create OAuth app"
method: POST
path: "/oauth-clients"
tags: ["OAuth Apps"]
---

# Create OAuth app

`POST /oauth-clients`

Register a new OAuth app for the organization. Any authenticated org member may create apps; the creator is recorded as the app's owner and is the only user who can subsequently read, update, suspend, activate, regenerate the secret of, or delete it.

The `clientSecret` is returned in this response **only** — it is stored hashed server-side and cannot be retrieved later. Persist it before exiting the create flow; if it is ever lost, rotate via `POST /oauth-clients/{appId}/regenerate-secret`.

`allowedScopes` is validated against the caller's role-aware scope set (see `GET /oauth-clients/scopes`). Org admins may include admin-only scopes; non-admins requesting a restricted scope receive `400`.

All `/oauth-clients/*` routes share a per-user rate limiter (default 1000 req/min, configurable via the `MAX_OAUTH_CLIENT_REQUESTS_PER_MINUTE` env var).

## Request body

- CreateOAuthAppRequest — Request to create a new OAuth app (`createAppSchema` in `oauth.validators.ts`). **Required:** `name`, `allowedScopes`. **Optional:** `description`, `redirectUris`, `allowedGrantTypes`, `homepageUrl`, `privacyPolicyUrl`, `termsOfServiceUrl`, `isConfidential`, `accessTokenLifetime`, `refreshTokenLifetime`. **Redirect rule (Zod refine):** If the effective grant list includes `authorization_code` (including when `allowedGrantTypes` is omitted — defaults to `authorization_code` + `refresh_token`), at least one redirect URI is required. If grants exclude `authorization_code`, `redirectUris` may be omitted.
  - `name` string, required — App name (displayed to users during authorization)
  - `description` string — App description
  - `redirectUris` string[] — Allowed redirect URIs (max 10). Required when an effective grant list includes `authorization_code` (including the default when `allowedGrantTypes` is omitted).
  - `allowedGrantTypes` string[] — Allowed grant types. Defaults to `["authorization_code", "refresh_token"]` if omitted (applied by the service, not Zod).
  - `allowedScopes` string[], required — Scopes the app can request (non-empty)
  - `homepageUrl` string, uri — App homepage URL (shown during authorization)
  - `privacyPolicyUrl` string, uri — Privacy policy URL
  - `termsOfServiceUrl` string, uri — Terms of service URL
  - `isConfidential` boolean — Whether the app can securely store secrets. - `true`: Server-side app (secret required for token requests) - `false`: Browser/mobile app (must use PKCE)
  - `accessTokenLifetime` integer — Access token lifetime in seconds (300–86400)
  - `refreshTokenLifetime` integer — Refresh token lifetime in seconds (3600–31536000)

## Response `201`

OAuth app created successfully

- CreateOAuthAppResponse — Response body for `POST /oauth-clients` (`oauth.app.controller.ts` `createApp`). The new app (including one-time `clientSecret`) is nested under `app`.
  - `message` string, required
  - `app` OAuthAppWithSecret, 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
    - `clientSecret` string, required — Client secret (only shown on creation and secret regeneration). Store this securely - it cannot be retrieved later.

## Other responses

- `400` — Invalid request (validation error)
- `401` — Unauthorized
- `403` — Forbidden — insufficient workspace permission to create OAuth apps
- `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)
