Getting Started
Register a Client

Register a Client

Before your app can log users in, it needs an OAuth client registered in Camel Accounts: a client_id, one or more exact redirect_uris, and an allowed scope list.

There are two ways to create one:

  1. Developer Console UI — sign in at https://console.camelcreatives.com and use the projects/clients screens (recommended)
  2. Console API — everything below can also be done over HTTP with a session cookie

Both are the same backend. This page shows the API path so you can script it.

Client types: public vs confidential

publicconfidential
Has a secret (cask_…)No — PKCE is the only protectionYes, required at /oauth/token
Typical useSPAs, mobile apps (custom-scheme redirects)Server-rendered apps / backends that can keep a secret
Token endpoint authclient_id + code_verifier only+ client_secret
Created via console API"client_type": "public""client_type": "confidential"

All current first-party clients (Bajeti/Simamia) are public. If you build a traditional server-side app with a private backend, choose confidential and store the secret in your server's environment — never in frontend code.

1. Create a project

Clients live inside projects. Each owner may hold up to 25 projects.

curl -s -b cookies.txt -X POST "$ISSUER/console/projects" \
  -H "Content-Type: application/json" \
  -d '{"name":"Bajeti"}'
{
  "success": true,
  "data": {
    "id": "9f8e7d6c-5b4a-3210-fedc-ba9876543210",
    "name": "Bajeti",
    "slug": "bajeti",
    "created_at": "2026-08-26T09:30:00Z",
    "archived": false
  }
}

The slug becomes part of every client ID you create in this project.

2. Create the client

curl -s -b cookies.txt -X POST "$ISSUER/console/projects/$PROJECT_ID/clients" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Bajeti Web",
    "client_type": "public",
    "redirect_uris": ["https://bajeti.cameltech.co/callback"],
    "allowed_scopes": ["openid", "profile", "email"]
  }'
{
  "success": true,
  "data": {
    "client": {
      "id": "bajeti-web-x7k2p9",
      "name": "Bajeti Web",
      "type": "public",
      "status": "testing",
      "redirect_uris": ["https://bajeti.cameltech.co/callback"],
      "allowed_scopes": ["openid", "profile", "email"],
      "has_secret": false,
      "created_at": "2026-08-26T09:31:12Z"
    },
    "client_secret": null
  }
}

Notes:

  • The id is generated for you as <project-slug>-<6 random chars> (e.g. bajeti-web-x7k2p9) — this is your client_id.
  • New clients start in status testing (see publishing below).
  • Up to 25 clients per project, each with 1–20 redirect URIs.

Confidential? Copy the secret NOW

For "client_type": "confidential" the same call returns the plaintext secret exactly once:

{
  "client": { "id": "bajeti-api-m3v8qk", "...": "..." },
  "client_secret": "cask_9tX4kPqWmE7rJ2nF6hD0sY5uLbGzA1cV8oN4iK3xMwT"
}

The value is shown once, then only stored as a SHA-256 hash. Lose it → rotate it.

3. Redirect URI rules

Validation is strict — these rules are enforced at creation time by the console API:

RuleDetail
Count1–20 URIs
Length≤ 512 characters each
FormatAbsolute URI, scheme://…
Allowed schemeshttps, http (dev), or a custom app scheme like bajeti (lowercase letters/digits/dots/dashes)
WildcardsForbidden — no * or ? anywhere

Matching at authorization time is exact string equality against this list. https://app.example.com/callback/ does not match https://app.example.com/callback.

Platform conventions:

PlatformRedirect URI styleExample
AndroidCustom schemebajeti://oauth/callback
Web SPAHTTPS URLhttps://bajeti.cameltech.co/callback
React widget (SDK)Issuer widget callbackhttps://accounts.camelcreatives.com/widget-callback

Register one client per platform (your-app-android, your-app-web) even if it's conceptually the same app — different schemes need different registrations, and it lets users revoke just their mobile connection.

4. Scopes

Pick from the supported set: openid, profile, email, phone. Whatever you allow here bounds what the app may request at /oauth/authorize; requesting anything outside allowed_scopes is rejected.

See Scopes & UserInfo for what each scope returns.

5. Publish to production

Clients start in testing. Status transitions:

testing ──► production ──► disabled
   │ ▲                        │
   ▼ │                        │
testing ◄──────────────── disabled
  • testing → production requires at least one https:// redirect URI and a homepage_url — the API rejects otherwise.
  • disabled clients are refused at /authorize, consent re-check, and token exchange.
curl -s -b cookies.txt -X PATCH "$ISSUER/console/projects/$PROJECT_ID/clients/$CLIENT_ID" \
  -H "Content-Type: application/json" \
  -d '{"status":"production","homepage_url":"https://bajeti.cameltech.co"}'

Rotate a secret

curl -s -b cookies.txt -X POST \
  "$ISSUER/console/projects/$PROJECT_ID/clients/$CLIENT_ID/rotate-secret"
{
  "success": true,
  "data": {
    "client": { "id": "bajeti-api-m3v8qk", "...": "..." },
    "client_secret": "cask_NewValue43charsBase64url…",
    "note": "copy it now — it will not be shown again; the previous secret is already invalid"
  }
}

Rotation invalidates the old secret immediately — plan a coordinated deploy.

Housekeeping

  • Archive a project: DELETE /console/projects/{project_id} — soft-archive that instantly disables every client inside it.
  • Delete a client: DELETE …/clients/{client_id} — cascades: all refresh-token families, grants, and live access tokens for that client die instantly.
  • Audit trail: every mutation is appended to GET /console/projects/{project_id}/audit-logs.
  • Quotas: 25 projects per owner, 25 clients per project.

Full endpoint details: Developer Console API.