# Buddo Platform — Agent Entry Point

Machine-readable guide for AI agents building on the Buddo platform.

## API

- **Base URL:** `https://api.buddo.xyz`
- **OpenAPI Spec:** `https://docs.buddo.xyz/openapi.json`
- **Local spec:** `./openapi.json`
- **Documentation site:** `https://docs.buddocloud.com`
- **API version:** v3.0.0 (OpenAPI spec v3.0.0, S195)


## Platform Overview & Business Model

Buddo is a white-label loyalty and engagement platform that enables businesses (known as **operators**) to build reward ecosystems for their users. Rather than building a loyalty system from scratch, operators integrate with Buddo via API or CLI to issue, track, and monetize points through their own applications.

**What operators get:**
- A loyalty point economy for their users (award, spend, transfer points)
- OAuth 2.0 authorization server — users connect their Buddo accounts to operator apps
- Container hosting via the Deploy API (deploy:manage scope, admin-vetted)
- Ad campaign infrastructure — serve ads to users, treasury covers payouts

**Revenue model:**
- **Ad campaign rake:** When you create an ad campaign with a budget (e.g., 1,000 points), Buddo deducts a 10% rake (100 points). The remaining 900 points fund ad payouts to users.
- **Points spend:** When users spend points via your app (`POST /api/external/points/spend`), those points credit your operator account balance directly.

**Why users engage:** Users earn loyalty points for watching ads (funded by the Buddo treasury at no cost to the operator). Users spend those points in your app, which credits your operator balance.

**The deploy:manage approval requirement:** The `deploy:manage` scope requires manual admin approval before it is active. This is intentional — Buddo vets operators before granting infrastructure access. Request the scope when registering your OAuth app; approval typically takes 1 business day.

Ready to start? See [Authentication](#authentication) below.

## Authentication

Two authentication methods exist. Choose based on your role:

### Operator Auth (JWT — for platform operators managing their own account)

```
POST /api/auth/register        {email, password, username}  → 201 {user}
POST /api/auth/login           {email, password}             → 200 {token, user}
POST /api/auth/self-verify-email                             → 200  (requires JWT; no request body needed)
```

Use the returned `token` as: `Authorization: Bearer <token>`

The login response returns `token` (not `access_token` or `session_token`). The `token` field is the JWT to use for operator-authenticated requests.

JWT gives access to: auth management, social features, education, OAuth app management, and user account endpoints.

### OAuth 2.0 PKCE (for operator apps acting on behalf of users)

```
1. Register an OAuth app:  POST /api/oauth/apps {app_name, redirect_uris: [...], allowed_scopes: [...]}  → client_id + client_secret
2. Authorization code:     GET  /api/oauth/authorize ?client_id=...&response_type=code&code_challenge=...&code_challenge_method=S256&state=<random>
3. Exchange for tokens:    POST /api/oauth/token {grant_type: "authorization_code", code, redirect_uri, client_id, code_verifier}
4. Use access token:       Authorization: Bearer <access_token>
5. Refresh:                POST /api/oauth/token {grant_type: "refresh_token", refresh_token, client_id, client_secret}
```

PKCE with S256 is mandatory for public clients. Plain is not supported.

**Guides:**
- `https://docs.buddocloud.com/quickstart/` — Full walkthrough
- `https://docs.buddocloud.com/guides/jwt-vs-oauth.html` — When to use which
- `https://docs.buddocloud.com/guides/oauth-pkce.html` — PKCE implementation details

## OAuth Scopes

| Scope | Description |
|---|---|
| `profile:read` | Read user profile and session information |
| `points:read` | Read user point balance |
| `points:spend` | Spend user points (credits operator account) |
| `points:transfer` | Transfer points between users |
| `points:award` | Deprecated — award is admin-only. Retained for existing token compatibility |
| `deploy:manage` | Deploy and manage hosted applications |
| `app:balance:read` | Read the operator app's point balance |
| `campaign:write` | Create and manage ad campaigns at `/api/external/campaigns/*`. Requires admin approval. |
| `campaigns:manage` | Manage operator ad campaigns via Ad Marketplace v2 (`/api/v1/ads/*`). Note: use `app:balance:read` to create campaigns; `campaigns:manage` controls pause/resume/analytics. **Not in live `scopes_supported` discovery; used internally for external campaign pause/resume.** |

## Capabilities

The Buddo platform provides:

- **App Hosting (Deploy):** Container-based deployment with tiered pricing, health checks, custom domains, audit logs
- **Loyalty Points:** Award, spend, transfer, and read point balances for users
- **OAuth Provider:** Full OAuth 2.0 authorization server with PKCE, token introspection, scope requests
- **Social Features:** Friends, presence tracking, chat channels/messages, user search, blocking
- **Ad Marketplace:** Serve ads (banner/video/interstitial/native/rewarded), record impressions/clicks, treasury-funded payouts; v2 marketplace with self-serve campaign creation, targeting, analytics, and bulk purchasing (`/api/v1/ads/*`)
- **External Campaigns:** Operator-funded campaign CRUD with 10% rake — create, update, cancel campaigns via `/api/external/campaigns/*`
- **Education (Bitcoin Learning):** Learning paths, modules, step completion with rewards
- **Operator Analytics:** User counts, spend/award totals, app management
- **User Management:** Connected apps, profile, app revocation

## First Steps (for agents)

1. Fetch the OpenAPI spec: `GET https://docs.buddo.xyz/openapi.json`
2. Register: `POST /api/auth/register` with `{email, password, username}`
3. Login: `POST /api/auth/login` with `{email, password}` — returns JWT
4. **Verify email:** `POST /api/auth/self-verify-email` with `Authorization: Bearer <jwt>` (no body) — required before creating OAuth apps or using protected endpoints
5. Check profile: `GET /api/auth/me` with `Authorization: Bearer <jwt>`
6. Create an OAuth app: `POST /api/oauth/apps` with `{app_name, redirect_uris, scopes: ["deploy:manage"]}`
7. Explore endpoints by domain (see Documentation Map below)


## Essential Endpoints: Start Here

Out of 102 API endpoints, a typical integration needs only **7**. Here they are in integration order:

### Operator Setup (use your JWT from login)

| Step | Method | Endpoint | Purpose |
|------|--------|----------|---------|
| 1 | POST | `/api/auth/register` | Create your operator account |
| 2 | POST | `/api/auth/login` | Authenticate — response contains `token` (your JWT) |
| 3 | POST | `/api/auth/self-verify-email` | **Required** — verify email instantly (JWT header, no body) |
| 4 | GET/PUT | `/api/operator/app` | Configure your operator app name and settings |
| 5 | POST | `/api/oauth/apps` | Register an OAuth app so your users can connect |

> **Key:** Your operator JWT goes in `Authorization: Bearer <token>` for all `/api/auth/*`, `/api/operator/*`, and `/api/oauth/*` calls.

### User Operations (use your USER's OAuth access token)

| Step | Method | Endpoint | Purpose |
|------|--------|----------|---------|
| 6 | POST | `/api/external/points/spend` | Spend a user's points (credits your operator account) |
| 7 | GET | `/api/external/ads/serve` | Get an ad to display to a user |
| 7b | POST | `/api/external/ads/event` | Report the ad view (required for payout, pass `impression_token`) |

> **Critical distinction:** Operator JWT -> `/api/operator/*` and `/api/oauth/*`. User OAuth token -> `/api/external/*`. These are different tokens. Mixing them causes 401/403 errors.

**Complete integration flow:** Register your operator account and verify your email. Configure your app via `/api/operator/app`. Create an OAuth app with `POST /api/oauth/apps` to get a `client_id`. Implement the OAuth PKCE flow so your users can connect their Buddo accounts — you receive a user-scoped OAuth token per user. Use that token with `/api/external/points/spend` to let users spend points (crediting your account), and with `/api/external/ads/serve` + `/api/external/ads/event` to serve ads and earn campaign revenue.

## Deploy Flow (for agents — complete sequence)

This is the full sequence to deploy a container application on buddocloud. All steps are executable from curl alone; no CLI required.

### 1. Register and authenticate

```
POST /api/auth/register        {email, password, username}  → 201 {user}
POST /api/auth/login           {email, password}             → 200 {token, user}
POST /api/auth/self-verify-email                             → 200  (JWT required, no body — verifies email instantly)
```

Save `token` as your JWT. Use it as `Authorization: Bearer <token>` for all subsequent requests.

**Email verification is required** before creating OAuth apps. Call `self-verify-email` immediately after login — it verifies your account instantly without needing inbox access.

### 2. Create an OAuth app with deploy:manage scope

```
POST /api/oauth/apps
Authorization: Bearer <jwt>
Body: {
  "app_name": "My App",
  "redirect_uris": ["http://localhost:9876/callback"],
  "allowed_scopes": ["deploy:manage"]
}
→ 201 { "app": { "client_id": "...", "client_secret": "...", "allowed_scopes": ["deploy:manage"] } }
```

Save `client_id` and `client_secret`. The `client_secret` is shown only once.

**Public clients:** Omit `client_secret` from the token exchange — `code_verifier` replaces it. The Buddo CLI is a public client (`client_secret` = NULL in DB); it works with PKCE only and never sends a secret.

**`allowed_scopes` field naming:** The registration endpoint uses `allowed_scopes` (both in the request body and the response). At registration time, non-auto-approved scopes are pending admin review — they are requested scopes, not yet permitted scopes. The operator endpoint returns `scopes` for the live approved set.

> **deploy:manage — Two-Phase Approval Lifecycle**
> After OAuth app registration, the `deploy:manage` scope is **PENDING** approval. Your token is valid but `/api/deploy/*` returns **403 Forbidden** until an admin approves the scope (typically 1 business day). Other scopes in the same token (`profile:read`, etc.) work immediately.
> Check approval status: `GET /api/oauth/my-apps` — look for `deploy:manage` in the scope approval fields.
> A 403 on deploy endpoints means scope-not-yet-approved, not an invalid token — do not re-register.

**Note:** Deploy endpoints require an OAuth access token, not a JWT. Use PKCE (see [OAuth PKCE guide](https://docs.buddocloud.com/guides/oauth-pkce/)) to exchange your app credentials for an access token with `deploy:manage` scope, then use that token for all `/api/deploy/*` calls.

### 3. List available deploy tiers

```
GET /api/deploy/tiers
Authorization: Bearer <oauth_access_token>
→ 200 { "tiers": [ { "slug": "free", "payment_method": "buddo_points", ... } ] }
```

Pick a tier `slug` for the next step. Available tiers:
- `free` — 512MB RAM, 0.5 CPU, 1 deployment max, buddo_points billing
- `basic` — 1GB RAM, 1 CPU, 3 deployments max, buddo_points billing

### 4. Create a deployment

```
POST /api/deploy/apps
Authorization: Bearer <oauth_access_token>
Body: {
  "name": "my-app",
  "image": "docker.io/library/nginx:latest",
  "tier": "free",
  "port": 80
}
→ 201 { "deployment": { "id": "...", "status": "pending", "subdomain": "my-app" } }
```

Save the deployment `id`.

**Image whitelist** — `image` must start with one of:
- `harbor.buddo.xyz:4443/` (Buddo private registry)
- `docker.io/library/` (official Docker Hub images)
- `ghcr.io/` (GitHub Container Registry)

**Harbor image registry:** Agents without Harbor credentials should pull from `docker.io/library/` or `ghcr.io/`. To push images to `harbor.buddo.xyz:4443/`, valid Harbor credentials are required (contact the platform admin).

**Name rules** — 1–32 chars, lowercase alphanumeric and hyphens, must start and end with alphanumeric. Your app will be live at `https://<name>.buddocloud.com`.

**Port note** — If using `nginx:latest` (which runs on port 80 as root), prefer `nginx:unprivileged` and set `"port": 8080`. The platform runs containers as non-root; unprivileged images avoid port-binding issues.

### 5. Poll status until running

```
GET /api/deploy/apps/{id}/status
Authorization: Bearer <oauth_access_token>
→ 200 { "id": "...", "status": "running", "health_status": "healthy", "last_health_check": "..." }
```

Status progression: `pending` → `deploying` → `running`

Poll every 5–10 seconds. Rate limit: 30 requests/minute. Deployment typically reaches `running` within 30–60 seconds.

### 6. Verify the deployment is live

Once status is `running`, your app is reachable at `https://<name>.buddocloud.com`.

```
GET /api/deploy/apps/{id}/logs
Authorization: Bearer <oauth_access_token>
→ 200 { "logs": [ { "action": "create", "status": "completed" }, { "action": "deploy", "status": "completed" } ] }
```

### 7. Stop or destroy when done

```
# Stop (preserves name/subdomain, can restart later)
POST /api/deploy/apps/{id}/stop
Authorization: Bearer <oauth_access_token>
→ 200 { "deployment": { "status": "stopped" } }

# Destroy permanently (cannot be undone)
DELETE /api/deploy/apps/{id}
Authorization: Bearer <oauth_access_token>
→ 200 { "message": "Deployment destroyed", "id": "..." }
```

### Deploy error reference

| Status | Cause |
|---|---|
| `401` | Missing or invalid token |
| `404` | Tier or deployment not found |
| `422` | Invalid name, unsupported image registry, or unknown tier |
| `429` | Deployment limit reached or rate limit hit |


## Error Handling

All Buddo API errors return a consistent JSON body:

```json
{
  "error": "string description",
  "details": "optional additional info"
}
```

### HTTP Status Codes

| Status | Name | Common Causes | Action |
|--------|------|---------------|--------|
| 400 | Bad Request | Missing required fields, malformed JSON | Check request body against endpoint schema |
| 401 | Unauthorized | Missing, expired, or invalid token | Re-authenticate and get a new token |
| 403 | Forbidden | Token lacks required scope; admin-only endpoint | Verify OAuth scopes; check endpoint auth requirements |
| 404 | Not Found | Invalid endpoint path; resource does not exist | Confirm resource ID and URL path |
| 422 | Unprocessable Entity | Validation failure (invalid name format, unsupported image registry, missing impression_token) | Read `details` field for the specific validation error |
| 429 | Too Many Requests | Rate limit exceeded | Wait for `Retry-After` header duration; use exponential backoff |
| 500 | Internal Server Error | Transient server error | Retry after a short delay |

### Auth Error Recovery

- **401:** Token is expired or invalid. Re-call `POST /api/auth/login` to get a new JWT, or restart the OAuth flow to get a new user access token.
- **403:** Token is valid but lacks the required scope. Check that your OAuth app was registered with the correct `allowed_scopes`. For `deploy:manage`, confirm admin approval has been granted.
- **422 on deploy:** Usually means invalid app name (1-32 chars, lowercase alphanumeric and hyphens), unsupported image registry, or unknown tier slug.
- **422 on ads:** Most commonly caused by missing `impression_token` in `POST /api/external/ads/event`. Always capture and pass `impression_token` from the serve response.

### Rate Limits & Backoff

On a **429** response: stop immediately, wait for the `Retry-After` header (in seconds), then retry. Implement exponential backoff — on each subsequent 429, double the wait time, capped at 60 seconds.

Key rate limits: register 5/hr/IP, login 10/min/IP, deploy status poll 30/min.

### Full guide

`https://docs.buddocloud.com/guides/quickstart/` — step-by-step operator quickstart with curl examples for every step above.

## Endpoint Summary (102 endpoints)

### Auth (11 endpoints) — JWT authentication
| Method | Path | Summary |
|---|---|---|
| POST | `/api/auth/register` | Register a new user account (`email`, `password`, `username` required; `referral_code` optional) |
| POST | `/api/auth/login` | Authenticate and receive a JWT — response: `{token, user}` |
| GET | `/api/auth/me` | Get the authenticated user's full profile |
| POST | `/api/auth/verify-email` | Verify a user's email address with a token |
| POST | `/api/auth/send-verification` | Resend the email verification link |
| POST | `/api/auth/forgot-password` | Request a password reset link |
| POST | `/api/auth/reset-password` | Reset password using a reset token |
| POST | `/api/auth/totp/setup` | Generate a TOTP secret and provisioning URI |
| POST | `/api/auth/totp/enable` | Enable TOTP by confirming a valid code |
| POST | `/api/auth/totp/verify` | Verify a TOTP or recovery code |
| POST | `/api/auth/self-verify-email` | Trust-verify email without OTP — call this immediately after login when `email_not_verified` is returned. Requires JWT, no request body. Verifies your account instantly so you can create OAuth apps and use protected endpoints. |

### OAuth (8 endpoints) — App registration and token management
| Method | Path | Summary |
|---|---|---|
| POST | `/api/oauth/apps` | Register a new OAuth app |
| GET | `/api/oauth/apps/{client_id}` | Get public app metadata by client_id |
| GET | `/api/oauth/authorize` | Request an authorization code |
| POST | `/api/oauth/token` | Exchange authorization code or refresh token for access tokens |
| POST | `/api/oauth/token/verify` | Introspect an access token |
| GET | `/api/oauth/my-apps` | List OAuth apps owned by the authenticated user |
| PUT | `/api/oauth/my-apps/{id}` | Update an OAuth app owned by the authenticated user |
| POST | `/api/oauth/my-apps/{id}/scope-request` | Request additional scopes for an owned app |

### Deploy (10 endpoints) — Container hosting
| Method | Path | Summary |
|---|---|---|
| GET | `/api/deploy/tiers` | List available deployment tiers |
| GET | `/api/deploy/apps` | List operator's deployments |
| POST | `/api/deploy/apps` | Create a new deployment |
| GET | `/api/deploy/apps/{id}` | Get a single deployment |
| DELETE | `/api/deploy/apps/{id}` | Destroy a deployment |
| GET | `/api/deploy/apps/{id}/status` | Lightweight deployment status check |
| GET | `/api/deploy/apps/{id}/logs` | Get deployment action logs |
| POST | `/api/deploy/apps/{id}/stop` | Stop a running deployment |
| POST | `/api/deploy/apps/{id}/restart` | Restart a deployment |
| GET | `/api/deploy/sandbox-status` | Check sandbox/trial deployment status |

### Social (22 endpoints) — Friends, presence, chat, blocking, avatars, cosmetics
| Method | Path | Summary |
|---|---|---|
| GET | `/api/social/users/search` | Search users by username |
| GET | `/api/social/friends` | List current user's friends |
| POST | `/api/social/friends` | Send a friend request |
| GET | `/api/social/friends/pending` | List pending friend requests |
| PUT | `/api/social/friends/{id}` | Accept or reject a friend request |
| DELETE | `/api/social/friends/{id}` | Remove a friendship |
| POST | `/api/social/block` | Block a user |
| DELETE | `/api/social/block/{user_id}` | Unblock a user |
| GET | `/api/social/presence` | Get friends' online presence |
| POST | `/api/social/presence` | Track user presence |
| DELETE | `/api/social/presence` | Untrack user presence |
| GET | `/api/social/presence/table` | Get presence for a game table |
| POST | `/api/social/chat/channels` | Create or ensure a chat channel |
| GET | `/api/social/chat/{channel_id}/messages` | Get messages from a chat channel |
| POST | `/api/social/chat/{channel_id}/messages` | Send a message to a chat channel |
| GET | `/api/social/avatar` | Get current user's avatar (auto-created on first call) |
| PUT | `/api/social/avatar` | Update avatar appearance or display name |
| PUT | `/api/social/avatar/equip` | Equip or unequip a cosmetic item |
| GET | `/api/social/avatar/{user_id}` | Get a user's public avatar |
| GET | `/api/social/cosmetics` | Browse the cosmetic catalogue (filter by category/rarity; no auth required) |
| GET | `/api/social/cosmetics/inventory` | Get the current user's cosmetic inventory |
| POST | `/api/social/cosmetics/{id}/purchase` | Purchase a cosmetic item |

### Operator (4 endpoints) — App and analytics management
| Method | Path | Summary |
|---|---|---|
| GET | `/api/operator/app` | Get operator's app details |
| PUT | `/api/operator/app` | Update operator's app |
| GET | `/api/operator/analytics` | Get operator analytics |
| GET | `/api/operator/billing/history` | Get paginated billing transaction history (page, page_size params; max 100/page) |

### User (2 endpoints) — User account and connected apps
| Method | Path | Summary |
|---|---|---|
| GET | `/api/external/user` | Get authenticated user profile |
| GET | `/api/user/connected-apps` | List user's connected OAuth apps |

### Points (4 endpoints) — Loyalty point operations
| Method | Path | Summary |
|---|---|---|
| GET | `/api/external/points` | Get user point balance |
| POST | `/api/external/points/spend` | Spend user points, crediting the operator account |
| POST | `/api/external/points/transfer` | Transfer points from the authenticated user to another user |
| POST | `/api/external/points/award` | **Deprecated.** Award points to the authenticated user (admin-only; retained for token compatibility) |

### Sessions (3 endpoints) — Session lifecycle
| Method | Path | Summary |
|---|---|---|
| GET | `/api/external/session/status` | Check whether the user has an active session |
| POST | `/api/external/session/end` | End the user's active session |
| POST | `/api/session/refresh` | Refresh a session token |

### Ads (3 endpoints) — Ad serving
| Method | Path | Summary |
|---|---|---|
| GET | `/api/external/ads/serve` | Serve an ad campaign for a given surface type |
| POST | `/api/external/ads/event` | Record an ad impression or click event |
| GET | `/api/external/ads/serve/v2` | Placement-aware serving with auto-impression recording; falls back to surface_type if no placement matches |

**impression_token (required for event recording):** `GET /api/external/ads/serve` returns both a `campaign` object and an `impression_token` string. You MUST pass `impression_token` in the body of `POST /api/external/ads/event` — omitting it returns `422 missing_impression_token`. Tokens are single-use and short-lived.

**Flow:** serve → capture `impression_token` → display ad → record event with `impression_token`.

**Creating campaigns:** Use `POST /api/external/campaigns` with OAuth token + `campaign:write` scope (admin approval required). Body: `{name, surface_type, budget, payout_rate}`. Budget is gross points; 10% rake deducted. See [External Campaigns](#external-campaigns-7-endpoints----operator-funded-campaigns-with-rake) section below.

**Daily impression cap:** Each campaign has a 10,000 impression/day cap per operator. Once exhausted, `/api/external/ads/serve` returns `404 no_campaign_available` until the cap resets at midnight UTC. The v2 endpoint (`/api/external/ads/serve/v2`) auto-records impressions — do not call `/api/external/ads/event` for impressions when using v2.

### Ad Marketplace v2 (17 endpoints) — Self-serve campaign management
Requires OAuth access token with `app:balance:read` scope. Base prefix: `/api/v1/ads/`

**To create a campaign:** `POST /api/v1/ads/campaigns` with `{name, budget, surface_type, payout_rate}`. Budget is in points; 10% rake applies. Surface types: `banner`, `video`, `interstitial`, `native`, `rewarded`.
| Method | Path | Summary |
|---|---|---|
| GET | `/api/v1/ads/campaigns` | List operator's campaigns |
| POST | `/api/v1/ads/campaigns` | Create a campaign (rake model) |
| PUT | `/api/v1/ads/campaigns/{id}` | Update a campaign |
| POST | `/api/v1/ads/campaigns/{id}/pause` | Pause a campaign |
| POST | `/api/v1/ads/campaigns/{id}/resume` | Resume a campaign |
| DELETE | `/api/v1/ads/campaigns/{id}` | Delete a campaign |
| GET | `/api/v1/ads/segments` | List targeting segments |
| GET | `/api/v1/ads/campaigns/{id}/targeting` | Get campaign targeting config |
| POST | `/api/v1/ads/campaigns/{id}/targeting` | Set campaign targeting config |
| GET | `/api/v1/ads/analytics/overview` | Overview analytics for operator |
| GET | `/api/v1/ads/analytics/campaign/{id}` | Per-campaign analytics |
| GET | `/api/v1/ads/analytics/placement/{id}` | Per-placement analytics |
| GET | `/api/v1/ads/pricing/rates` | List all pricing rates |
| GET | `/api/v1/ads/pricing/rates/{placement_id}` | Get rate for a specific placement |
| POST | `/api/v1/ads/pricing/bulk-quote` | Get bulk pricing quote |
| POST | `/api/v1/ads/purchases/bulk` | Atomic bulk purchase (debits app account) |

### External Campaigns (7 endpoints) — Operator-funded campaigns with rake
Requires OAuth access token. Base prefix: `/api/external/campaigns/`
| Method | Path | Summary |
|---|---|---|
| GET | `/api/external/campaigns` | List operator's own campaigns |
| POST | `/api/external/campaigns` | Create an operator campaign (`budget` field = gross points; 10% rake deducted) |
| GET | `/api/external/campaigns/{id}` | Get a single campaign |
| PUT | `/api/external/campaigns/{id}` | Update a campaign |
| DELETE | `/api/external/campaigns/{id}` | Cancel/end a campaign |
| POST | `/api/external/campaigns/{id}/pause` | Pause an active campaign (scope: campaigns:manage) |
| POST | `/api/external/campaigns/{id}/resume` | Resume a paused campaign; exhausted campaigns cannot be resumed |

### App (1 endpoint) — Operator app balance
| Method | Path | Summary |
|---|---|---|
| GET | `/api/external/app/balance` | Get the operator app's earned-point balance |

### Education (5 endpoints) — Bitcoin learning
| Method | Path | Summary |
|---|---|---|
| POST | `/api/btc-progress/learning-path` | Set learning path |
| GET | `/api/btc-progress/available-modules` | List available modules |
| GET | `/api/btc-progress/module/{module_number}/status` | Get module progress |
| POST | `/api/btc-progress/module/{module_number}/reset` | Reset a completed module |
| POST | `/api/btc-progress/{step_id}/complete` | Complete a learning step |

### Public (1 endpoint) — No auth required
| Method | Path | Summary |
|---|---|---|
| GET | `/api/public/operators` | List approved public operators (paginated: `{data, total, page, page_size}`) |

### Infrastructure (4 endpoints) — Health and discovery
| Method | Path | Summary |
|---|---|---|
| GET | `/health` | Health check — returns `{status, version}` |
| GET | `/.well-known/openapi.json` | Fetch the OpenAPI spec (no auth required) |
| GET | `/.well-known/oauth-protected-resource` | RFC 9728 OAuth Protected Resource discovery — returns scopes, capabilities, token endpoint |
| GET | `/llms.txt` | LLM agent discovery file — plain text API description for AI agents |

## Documentation Map

| Page | URL |
|---|---|
| Landing Page | `https://docs.buddocloud.com/` |
| Quickstart Guide | `https://docs.buddocloud.com/quickstart/` |
| API Reference Index | `https://docs.buddocloud.com/reference/` |
| Auth Reference | `https://docs.buddocloud.com/reference/auth.html` |
| OAuth Reference | `https://docs.buddocloud.com/reference/oauth.html` |
| Deploy Reference | `https://docs.buddocloud.com/reference/deploy.html` |
| Social Reference | `https://docs.buddocloud.com/reference/social.html` |
| Operator Reference | `https://docs.buddocloud.com/reference/operator.html` |
| User Reference | `https://docs.buddocloud.com/reference/user.html` |
| Public Reference | `https://docs.buddocloud.com/reference/public.html` |
| Education Reference | `https://docs.buddocloud.com/reference/education.html` |
| Infrastructure Reference | `https://docs.buddocloud.com/reference/infrastructure.html` |
| Guide: Operator Quickstart | `https://docs.buddocloud.com/guides/quickstart/` |
| Guide: OAuth PKCE | `https://docs.buddocloud.com/guides/oauth-pkce.html` |
| Guide: JWT vs OAuth | `https://docs.buddocloud.com/guides/jwt-vs-oauth.html` |
| Guide: Deploy Lifecycle | `https://docs.buddocloud.com/guides/deploy-lifecycle.html` |
| Guide: Social Integration | `https://docs.buddocloud.com/guides/social-integration.html` |
| Guide: Ad Marketplace | `https://docs.buddocloud.com/guides/ad-marketplace.html` |
| Guide: Ad Marketplace v2 | `https://docs.buddocloud.com/guides/ad-marketplace-v2.html` |
| Guide: External Campaigns | `https://docs.buddocloud.com/guides/external-campaigns.html` |
| Guide: Operator Onboarding | `https://docs.buddocloud.com/guides/operator-onboarding/` |
| OpenAPI Spec (JSON) | `https://docs.buddo.xyz/openapi.json` |
| Agent Entry Point | `https://docs.buddocloud.com/AGENTS.md` |

## Rate Limits

Key rate limits to respect:
- **Register:** 5 requests/hour/IP
- **Login:** 10 requests/minute/IP
- **OAuth app creation:** 3 requests/hour
- **Session refresh:** 20 requests/minute/IP
- **Deploy operations:** 10–30 requests/minute depending on endpoint
- **Password reset:** 5 requests/hour/IP


## Monetization & Ads

### Revenue Stream 1: Points Spend

When your users spend points in your app via `POST /api/external/points/spend`, those points transfer to your operator account balance. You keep 100% of points spent — this is your primary recurring revenue tied to user engagement.

**Example:** A user spends 500 points on a premium feature. Your operator account receives 500 points. Check your balance: `GET /api/external/app/balance`

### Revenue Stream 2: Ad Campaigns

Run ad campaigns to promote content and earn additional revenue. Campaign economics:

- Create a campaign with a budget (e.g., 10,000 points)
- Buddo deducts a **10% rake** (1,000 points) — net budget available for payouts is 9,000 points
- The Buddo treasury funds user payouts when they view ads — no additional cost to you. (The treasury funds users only when no operator campaign is active; your campaign budget pays your users directly when your campaign is running.)

**Why users watch ads:** Users receive loyalty points for each completed ad view, funded by the treasury. This creates a closed loop — users watch ads, earn points, spend points in your app, crediting your account.

### The Ad Serving Flow

```
1. Create campaign: POST /api/external/campaigns
   Body: {name, surface_type, budget, payout_rate}
   Requires campaign:write scope + admin approval
   Response: campaign.id

2. Serve ad:        GET /api/external/ads/serve?surface_type=banner
   Authorization: Bearer <user_oauth_token>
   Response: {campaign: {...}, impression_token: "..."}

3. Display ad to user

4. Record view:     POST /api/external/ads/event
   Body: {impression_token: "...", event_type: "impression"}
   Response: 204 (treasury credits user points + records impression)
```

**impression_token:** Returned by `/ads/serve`. Single-use, short-lived. Must be passed to `/ads/event` — omitting it returns `422 missing_impression_token`.

### Ad Marketplace v2

For self-serve campaign management with targeting and analytics: use `POST /api/v1/ads/campaigns` (requires `app:balance:read` scope). Supports bulk purchasing, segment targeting, and per-campaign analytics. 10% rake applies.

## Key Concepts

- **Operator:** A business that registers an OAuth app to integrate with Buddo. Operators use the external OAuth API only.
- **User:** An end user who signs up on Buddo and authorizes operator apps via OAuth.
- **Points:** Buddo's loyalty currency. Operators can read, spend (crediting their own account), and transfer points. Award is admin-only.
- **Deploy Tiers:** Two tiers — `free`/`starter` (512MB/0.5CPU, 1 app max) and `basic` (1GB/1CPU, 3 apps max). Both use `buddo_points` billing. Check `GET /api/deploy/tiers` for the exact slug names before deploying. Each tier defines CPU, memory, domain, and stipend limits.
- **Sessions:** Lightweight presence tracking per-app. Refresh tokens rotate atomically.
- **Ad Surfaces:** `banner`, `video`, `interstitial`, `native`, `rewarded`. Treasury pays both user and operator for ad views.
- **Ad Marketplace v2:** Self-serve campaign platform at `/api/v1/ads/*`. Operators create campaigns, set targeting, buy placements in bulk, and track analytics. 10% rake on all ad spend.
- **External Campaigns:** Operator-funded campaigns at `/api/external/campaigns/*`. CRUD lifecycle with cancel action. Revenue flows through Buddo treasury.
- **Admin Domain:** `https://admin.buddo.xyz` — Buddo admin portal (JWT + admin role required; not for operators).
﻿

## CLI (buddo v2.0.0)

The Buddo CLI provides a terminal interface for all operator operations. It is the
recommended interface for agents and automated workflows.

- **Install docs:** `https://docs.buddocloud.com/guides/cli-quickstart/`
- **Full command reference:** `https://docs.buddocloud.com/guides/cli-reference/`
- **Agent automation guide:** `https://docs.buddocloud.com/guides/agent-automation/`
- **Version:** v2.0.0
- **API target:** `https://api.buddo.xyz` (override with `--api-url`)

### Installation

```bash
# macOS (arm64)
curl -L https://docs.buddocloud.com/cli/buddo-darwin-arm64 -o buddo
chmod +x buddo && sudo mv buddo /usr/local/bin/

# macOS (amd64)
curl -L https://docs.buddocloud.com/cli/buddo-darwin-amd64 -o buddo
chmod +x buddo && sudo mv buddo /usr/local/bin/

# Linux (amd64)
curl -L https://docs.buddocloud.com/cli/buddo-linux-amd64 -o buddo
chmod +x buddo && sudo mv buddo /usr/local/bin/

# Windows (PowerShell)
Invoke-WebRequest https://docs.buddocloud.com/cli/buddo-windows-amd64.exe -OutFile buddo.exe
```

### Authentication

```bash
buddo login                      # browser PKCE flow (opens browser)
buddo login --token <JWT>        # headless/CI — authenticate with pre-existing JWT
```

Credentials saved to `~/.config/buddo/credentials.json` and auto-refreshed.

### Command Summary

| Command | Description |
|---|---|
| `buddo login` | Authenticate (browser PKCE or `--token <JWT>`) |
| `buddo apps list` | List registered operator apps |
| `buddo apps create --name <n> [--scopes ...] [--redirect-uris ...]` | Register a new OAuth app |
| `buddo deploy [--name n] [--image i] [--port p]` | Deploy a container (reads `buddo.json` or flags) |
| `buddo deploy stop <app-id> [--force]` | Stop a running deployment |
| `buddo deploy restart <app-id>` | Restart a deployment |
| `buddo deploy logs <app-id> [-f]` | Fetch deployment logs |
| `buddo deploy destroy <app-id> [--force]` | Permanently remove a deployment |
| `buddo campaigns list` | List all ad campaigns |
| `buddo campaigns create --name n --budget pts --surface type [--payout-rate n]` | Create a campaign |
| `buddo campaigns pause <id>` | Pause an active campaign |
| `buddo campaigns resume <id>` | Resume a paused campaign |
| `buddo campaigns cancel <id>` | Cancel a campaign (unspent budget refunded) |
| `buddo analytics impressions [campaign-id] [--since] [--until]` | Impression data |
| `buddo analytics clicks [campaign-id] [--since] [--until]` | Click data |
| `buddo analytics spend [campaign-id] [--since] [--until]` | Budget/spend breakdown |
| `buddo billing balance` | Current balance + runway estimate |
| `buddo billing history [--limit n]` | Recent transactions |
| `buddo billing apps` | Per-app billing status |
| `buddo status` | Operator app + all deployments |
| `buddo version` | CLI version |

**Global flags:** `--json` (machine-readable output), `--api-url` (override API base)

### Quick Agent Workflow

```bash
# 1. Authenticate
buddo login --token "$JWT"

# 2. Register app
buddo apps create --name "My App" \
  --scopes "profile:read,points:read,deploy:manage,campaigns:manage,app:balance:read"

# 3. Deploy
buddo deploy --name my-app --image myregistry/my-app:latest --port 3000

# 4. Create campaign
buddo campaigns create --name "Launch" --budget 50000 --surface banner

# 5. Monitor
buddo analytics spend --json | jq '.totals'
buddo billing balance --json | jq '.runway_days'

# 6. Wind down
buddo campaigns cancel $CAMPAIGN_ID
buddo deploy destroy $APP_ID --force
```

### CLI Error Codes

| Exit code | Meaning |
|---|---|
| `0` | Success |
| `1` | Error (auth failure, API error, validation error) |
| `2` | Feature not yet available on this server version |

### buddo.json (deploy config file)

```json
{
  "app": { "name": "my-app" },
  "deploy": {
    "image": "myregistry/my-app:latest",
    "port": 3000,
    "env": { "KEY": "value" }
  }
}
```

Place in the project root and run `buddo deploy` — flags override config values.