OAuth API Reference
OAuth 2.0 endpoints for app registration, authorization, token exchange, and introspection. Buddo uses Authorization Code with PKCE (S256) — plain code challenges are not supported.
8 endpoints
Register a new OAuth app
Creates a new OAuth app. Returns the client_secret — this is the only time it is shown. Rate limited to 3 requests per hour. **Requires email verification.** This endpoint is behind the `email_verified` pipeline. Your account email must be verified before you can register OAuth apps. If your email is not verified, this endpoint returns 403. To verify your email: call POST /api/auth/send-verification (requires JWT) to receive a verification code, then call POST /api/auth/verify-email with the code. After verification, retry this request with your JWT bearer token.
Authentication
JWT Bearer Token
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
requested_scopes |
array<string> |
optional | |
app_description |
string |
optional | |
app_name |
string |
required | |
logo_url |
string |
optional | |
redirect_uris |
array<string> |
required | |
website_url |
string |
optional |
Response (Success)
| Name | Type | Required | Description |
|---|---|---|---|
app |
object |
required |
Example Request
curl -X POST "https://api.buddo.xyz/api/oauth/apps" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"requested_scopes": [
"string"
],
"app_description": "string",
"app_name": "string",
"logo_url": "string",
"redirect_uris": [
"string"
],
"website_url": "string"
}'
Example Response
{
"app": {
"allowed_scopes": [
"string"
],
"app_description": "string",
"app_name": "string",
"client_id": "string",
"id": "00000000-0000-0000-0000-000000000000",
"inserted_at": "2026-01-01T00:00:00Z",
"is_approved": false,
"logo_url": {},
"redirect_uris": [
"string"
],
"website_url": {},
"client_secret": "string"
}
}
Get public app metadata by client_id
Returns public app information. No authentication required.
Authentication
None
Path Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
client_id |
path | string |
required | The OAuth app's client_id |
Response (Success)
| Name | Type | Required | Description |
|---|---|---|---|
client_id |
string |
required | |
description |
string |
optional | |
name |
string |
required |
Example Request
curl -X GET "https://api.buddo.xyz/api/oauth/apps/:client_id"
Example Response
{
"client_id": "string",
"description": "string",
"name": "string"
}
Request an authorization code
Issues an authorization code for the given client_id. Requires PKCE with S256 method. The code is valid for 600 seconds.
Authentication
JWT Bearer Token
Query Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
client_id |
query | string |
required | The OAuth app's client_id |
response_type |
query | "code" |
required | Must be 'code' |
redirect_uri |
query | string |
optional | Must match one of the app's registered redirect_uris |
state |
query | string |
optional | Opaque value for CSRF protection — returned unchanged |
scope |
query | string |
optional | Space-separated scopes (must be subset of app's allowed_scopes) |
code_challenge |
query | string |
required | PKCE code challenge (BASE64URL(SHA256(code_verifier))) |
code_challenge_method |
query | "S256" |
required | Must be 'S256' — plain is not supported |
Response (Success)
| Name | Type | Required | Description |
|---|---|---|---|
code |
string |
required | |
expires_in |
integer |
required | Always 600 seconds |
redirect_uri |
string |
required |
Example Request
curl -X GET "https://api.buddo.xyz/api/oauth/authorize?client_id=VALUE&response_type=VALUE&code_challenge=VALUE&code_challenge_method=VALUE" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Example Response
{
"code": "string",
"expires_in": 0,
"redirect_uri": "string"
}
List OAuth apps owned by the authenticated user
Authentication
JWT Bearer Token
Example Request
curl -X GET "https://api.buddo.xyz/api/oauth/my-apps" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Example Response
[
{
"allowed_scopes": [
"string"
],
"app_description": "string",
"app_name": "string",
"client_id": "string",
"id": "00000000-0000-0000-0000-000000000000",
"inserted_at": "2026-01-01T00:00:00Z",
"is_approved": false,
"logo_url": {},
"redirect_uris": [
"string"
],
"website_url": {}
}
]
Update an OAuth app owned by the authenticated user
Authentication
JWT Bearer Token
Path Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id |
path | string (uuid) |
required | The app's internal ID |
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
allowed_scopes |
array<string> |
optional | |
app_description |
string |
optional | |
app_name |
string |
optional | |
logo_url |
string |
optional | |
redirect_uris |
array<string> |
optional | |
website_url |
string |
optional |
Response (Success)
| Name | Type | Required | Description |
|---|---|---|---|
allowed_scopes |
array<string> |
required | |
app_description |
string |
optional | |
app_name |
string |
required | |
client_id |
string |
required | |
id |
string (uuid) |
required | |
inserted_at |
string (date-time) |
optional | |
is_approved |
boolean |
required | |
logo_url |
string | null |
optional | |
redirect_uris |
array<string> |
required | |
website_url |
string | null |
optional |
Example Request
curl -X PUT "https://api.buddo.xyz/api/oauth/my-apps/:id" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"allowed_scopes": [
"string"
],
"app_description": "string",
"app_name": "string",
"logo_url": "string",
"redirect_uris": [
"string"
],
"website_url": "string"
}'
Example Response
{
"allowed_scopes": [
"string"
],
"app_description": "string",
"app_name": "string",
"client_id": "string",
"id": "00000000-0000-0000-0000-000000000000",
"inserted_at": "2026-01-01T00:00:00Z",
"is_approved": false,
"logo_url": {},
"redirect_uris": [
"string"
],
"website_url": {}
}
Request additional scopes for an owned app
Submits a request to expand the app's allowed scopes. Requires admin approval. **Manual approval process:** Scope requests are reviewed manually by a Buddo admin — there is no automated approval. Approval times vary and there is no webhook or callback; poll GET /api/oauth/my-apps/{id}/scope-requests to check status. A `pending` status means the request is awaiting review. **deploy:manage is not auto-approved.** This scope grants full deployment management and requires explicit admin sign-off. Submit the request, then contact Buddo support to expedite review.
Authentication
JWT Bearer Token
Path Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id |
path | string (uuid) |
required | The app's internal ID |
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
requested_scopes |
array<string> |
required | Scopes to add to the app's allowed_scopes |
Response (Success)
| Name | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) |
required | |
inserted_at |
string (date-time) |
required | |
requested_scopes |
array<string> |
required | |
status |
string |
required |
Example Request
curl -X POST "https://api.buddo.xyz/api/oauth/my-apps/:id/scope-request" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"requested_scopes": [
"string"
]
}'
Example Response
{
"id": "00000000-0000-0000-0000-000000000000",
"inserted_at": "2026-01-01T00:00:00Z",
"requested_scopes": [
"string"
],
"status": "string"
}
Exchange an authorization code or refresh token for access tokens
Supports two grant types: 'authorization_code' (with PKCE code_verifier for public clients or client_secret for confidential clients) and 'refresh_token'. Authorization code grants also return a session_token for lightweight session maintenance.
Authentication
None
Request Body
One of the following:
Authorization Code Grant
| Name | Type | Required | Description |
|---|---|---|---|
client_id |
string |
required | |
client_secret |
string |
optional | Required for confidential clients (alternative to code_verifier) |
code |
string |
required | The authorization code received from /api/oauth/authorize |
code_verifier |
string |
optional | PKCE code verifier — required for public clients (no client_secret) |
grant_type |
"authorization_code" |
required | |
redirect_uri |
string |
required | Must match the redirect_uri used in the authorize request |
Refresh Token Grant
| Name | Type | Required | Description |
|---|---|---|---|
client_id |
string |
required | |
client_secret |
string |
required | |
grant_type |
"refresh_token" |
required | |
refresh_token |
string |
required |
Response (Success)
| Name | Type | Required | Description |
|---|---|---|---|
access_token |
string |
required | |
expires_in |
integer |
required | |
refresh_token |
string |
required | |
session_token |
string |
optional | Short-lived session token — only present on authorization_code grants |
session_token_expires_at |
string (date-time) |
optional | Expiry of the session token — only present on authorization_code grants |
token_type |
"Bearer" |
required |
Example Request
curl -X POST "https://api.buddo.xyz/api/oauth/token" \
-H "Content-Type: application/json" \
-d '{
"client_id": "string",
"client_secret": "string",
"code": "string",
"code_verifier": "string",
"grant_type": "authorization_code",
"redirect_uri": "string"
}'
Example Response
{
"access_token": "string",
"expires_in": 0,
"refresh_token": "string",
"session_token": "string",
"session_token_expires_at": "2026-01-01T00:00:00Z",
"token_type": "Bearer"
}
Introspect an access token
Returns token validity and metadata. Always returns 200 — check the 'active' field. No authentication required.
Authentication
None
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
token |
string |
required | The access token to verify |
Response (Success)
| Name | Type | Required | Description |
|---|---|---|---|
active |
boolean |
required | |
app_id |
string (uuid) |
optional | |
expires_at |
string (date-time) |
optional | |
reason |
string |
optional | Present when active is false (e.g. 'expired') |
scopes |
array<string> |
optional | |
user_id |
string (uuid) |
optional |
Example Request
curl -X POST "https://api.buddo.xyz/api/oauth/token/verify" \
-H "Content-Type: application/json" \
-d '{
"token": "string"
}'
Example Response
{
"active": false,
"app_id": "00000000-0000-0000-0000-000000000000",
"expires_at": "2026-01-01T00:00:00Z",
"reason": "string",
"scopes": [
"string"
],
"user_id": "00000000-0000-0000-0000-000000000000"
}