DocumentationAPI ReferenceREST & OAuth Endpoints

REST & OAuth Endpoints

Complete reference with curl examples. All endpoints are on cpod-backend at https://api.yourdomain.com. The Control Plane (:8080) is internal — cpod-backend proxies to it; you never call it directly.

For the gRPC surface (sidecar-internal) see the SDK Reference.


OAuth — Token Issuance

POST /v1/oauth/token — client_credentials

curl -X POST https://api.yourdomain.com/v1/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials&client_id=app-myservice&client_secret=cs_xxxx&scope=jobs.read+files.write"
{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "jobs.read files.write"
}

POST /v1/oauth/token — authorization_code

curl -X POST https://api.yourdomain.com/v1/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code&client_id=app-myapp&code=AUTH_CODE&redirect_uri=https://myapp.example.com/callback&code_verifier=VERIFIER"
{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "rt_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "scope": "jobs.read files.read"
}

POST /v1/oauth/token — refresh_token

curl -X POST https://api.yourdomain.com/v1/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=refresh_token&client_id=app-myapp&refresh_token=rt_xxxx"

Returns a new access_token and a new refresh_token. The old refresh token is immediately revoked.


OAuth — Authorization Code + PKCE

GET /oauth/authorize — Start PKCE Flow

Redirect the user’s browser to this URL. cpod-backend renders the consent screen.

GET https://api.yourdomain.com/oauth/authorize
  ?client_id=app-myapp
  &response_type=code
  &redirect_uri=https://myapp.example.com/callback
  &scope=jobs.read+files.read
  &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
  &code_challenge_method=S256
  &state=xyzABC123

After consent, cpod-backend redirects to:

https://myapp.example.com/callback?code=AUTH_CODE&state=xyzABC123
⚠️

Exchange the code server-side immediately — codes expire in 60 seconds and are single-use. Never exchange auth codes in browser JavaScript.


OAuth — Token Inspection & Revocation

POST /v1/oauth/introspect — Inspect Token (RFC 7662)

curl -X POST https://api.yourdomain.com/v1/oauth/introspect \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "token=ACCESS_TOKEN&client_id=app-myservice&client_secret=cs_xxxx"

Active token:

{
  "active": true,
  "client_id": "app-myservice",
  "scope": "jobs.read files.write",
  "sub": "app-myservice",
  "iss": "https://api.yourdomain.com",
  "aud": "api.yourdomain.com",
  "iat": 1748820000,
  "exp": 1748823600,
  "jti": "jti_a1b2c3d4"
}

Expired or revoked:

{ "active": false }

POST /v1/oauth/revoke — Revoke Token (RFC 7009)

curl -X POST https://api.yourdomain.com/v1/oauth/revoke \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "token=ACCESS_TOKEN&client_id=app-myservice&client_secret=cs_xxxx"

Returns 200 OK for both valid and already-expired tokens (per RFC 7009). JTI is added to the sidecar deny-list and propagated within 30 s.


App Management

All app management endpoints require an admin token: Authorization: Bearer $CPOD_ADMIN_TOKEN

POST /v1/oauth/apps — Register App

curl -X POST https://api.yourdomain.com/v1/oauth/apps \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $CPOD_ADMIN_TOKEN" \
  -d '{
    "client_id": "app-myservice",
    "name": "My Backend Service",
    "declared_scopes": ["jobs.read", "jobs.write", "files.read"],
    "app_type": "service"
  }'
{
  "client_id": "app-myservice",
  "client_secret": "cs_xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "name": "My Backend Service",
  "declared_scopes": ["jobs.read", "jobs.write", "files.read"],
  "app_type": "service",
  "created_at": "2026-05-21T00:00:00Z"
}
⚠️

client_secret is returned once only — stored argon2-hashed. Store it in your secrets manager immediately.

app_type values:

ValueGrant types allowedUse case
serviceclient_credentialsBackend services, workers, CI
webauthorization_code + refresh_tokenServer-rendered web apps
spaauthorization_code + PKCE (no secret)Single-page apps
cliauthorization_code + PKCECLI tools

GET /v1/oauth/apps — List Apps

curl https://api.yourdomain.com/v1/oauth/apps \
  -H "Authorization: Bearer $CPOD_ADMIN_TOKEN"

DELETE /v1/oauth/apps/:id — Delete App

curl -X DELETE https://api.yourdomain.com/v1/oauth/apps/app-myservice \
  -H "Authorization: Bearer $CPOD_ADMIN_TOKEN"

Returns 204 No Content. All active tokens for the app are revoked immediately.

POST /v1/oauth/apps/:id/rotate-secret — Rotate Secret

curl -X POST https://api.yourdomain.com/v1/oauth/apps/app-myservice/rotate-secret \
  -H "Authorization: Bearer $CPOD_ADMIN_TOKEN"
{
  "client_id": "app-myservice",
  "client_secret": "cs_yyyyyyyyyyyyyyyyyyyyyyyyyyyy",
  "rotated_at": "2026-05-21T12:00:00Z"
}

Old secret is invalidated immediately. For zero-downtime rotation: deploy new secret to your app first, then call rotate.


JWKS

GET /v1/jwks — Public Key Set

No authentication required. Returns the RS256 public keys so external services can verify CoreSDK-issued JWTs without calling the sidecar.

curl https://api.yourdomain.com/v1/jwks
{
  "keys": [
    {
      "kty": "RSA",
      "use": "sig",
      "alg": "RS256",
      "kid": "key-2026-05-21",
      "n": "...",
      "e": "AQAB"
    }
  ]
}

Health

curl https://api.yourdomain.com/health
# {"status":"ok","sidecar":"connected","version":"0.9.0"}