Buddo Business API

Buddo Business API

Base URL: https://api.buddo.xyz Authentication: OAuth 2.0 Authorization Code + PKCE Content-Type: application/json (all requests and responses)

Buddo Business gives you everything you need to build apps inside the Buddo points economy. Your users already have Buddo accounts and point balances. Through this API, your app can read profiles, check balances, let users spend points, serve ads, and track engagement. Users earn points across the ecosystem and spend them in your app; your app earns points from ad impressions and user activity. One API, one economy, infinite possibilities.


Table of Contents

  1. Getting Started
  2. Authentication
  3. Scopes
  4. User API
  5. App Management
  6. Deploy API
  7. Social API
  8. Public Endpoints
  9. Error Reference
  10. Rate Limits
  11. Integration Patterns
  12. Quick Start

1. Getting Started

Step 0: Create and authenticate a Buddo account

Before registering an app, you need a verified Buddo account and a session token. These endpoints are not part of the OAuth flow — they create your developer account.

Register:

curl -X POST https://api.buddo.xyz/api/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "email": "you@example.com",
    "password": "yourpassword",
    "username": "yourhandle"
  }'

Log in:

curl -X POST https://api.buddo.xyz/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "you@example.com",
    "password": "yourpassword"
  }'

The login response returns a JWT token. Use this as your <your-session-token> in all subsequent developer API calls (app registration, listing your apps, etc.).

Email verification: After registering, a verification link is sent to your email address. You must click this link before you can register an OAuth app. For development and testing purposes, contact the platform admin to have your account verified manually.

Step 1: Create a Buddo account

Sign up at buddo.xyz and verify your email address. You need a verified account before you can register an app.

Step 2: Register your app

Once verified, call POST /api/oauth/apps to register your app (see Authentication for details). You will receive a client_id and client_secret. Store these securely. The client_secret is shown only once.

Step 3: Understand approval tiers

Your app’s status depends on which scopes you request:

Recommendation: Start with auto-approved scopes to begin building. Once your integration works, use the scope request endpoint to apply for additional permissions.

Step 4: Store your credentials

You need three values for all subsequent API calls:

Value Where you get it How to store it
client_id Registration response Can be public (embedded in authorize URLs)
client_secret Registration response Server-side only. Never expose in client code or version control.
redirect_uri You provide at registration Must exactly match what you registered (including trailing slashes)

2. Authentication

Buddo Business uses OAuth 2.0 Authorization Code with PKCE (Proof Key for Code Exchange). PKCE prevents authorization code interception attacks and is required for all apps.

2.1 Register Your App

Endpoint: POST /api/oauth/apps Auth: Bearer token (your Buddo session token from logging in) Rate limit: 3 registrations per hour per IP

curl -X POST https://api.buddo.xyz/api/oauth/apps \
  -H "Authorization: Bearer <your-session-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "app_name": "My Rewards App",
    "app_description": "A loyalty program powered by Buddo points",
    "website_url": "https://myapp.example.com",
    "redirect_uris": ["https://myapp.example.com/oauth/callback"],
    "allowed_scopes": ["profile:read", "points:read"]
  }'

Response (201 Created):

{
  "app": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "app_name": "My Rewards App",
    "app_description": "A loyalty program powered by Buddo points",
    "client_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
    "client_secret": "cs_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
    "redirect_uris": ["https://myapp.example.com/oauth/callback"],
    "allowed_scopes": ["profile:read", "points:read"],
    "website_url": "https://myapp.example.com",
    "logo_url": null,
    "is_approved": true
  }
}

Error (401 Unauthorized):

{
  "error": "Invalid or missing session token"
}

Error (422 Unprocessable Entity):

{
  "error": "validation_failed",
  "details": {
    "app_name": ["is required"],
    "redirect_uris": ["is invalid"]
  }
}

2.2 The PKCE Flow, Step by Step

PKCE adds a one-time challenge to the authorization flow. Here is the complete sequence.

Step A: Generate a code verifier and challenge

The code verifier is a random string between 43 and 128 characters. The code challenge is its SHA-256 hash, base64url-encoded.

# Generate a random code verifier (43+ characters, URL-safe)
CODE_VERIFIER=$(openssl rand -base64 32 | tr -d '=' | tr '/+' '_-')

# Create the code challenge (SHA-256 hash, base64url-encoded)
CODE_CHALLENGE=$(echo -n "$CODE_VERIFIER" | openssl dgst -sha256 -binary | openssl base64 | tr -d '=' | tr '/+' '_-')

echo "Verifier: $CODE_VERIFIER"
echo "Challenge: $CODE_CHALLENGE"

Step B: Redirect the user to authorize

Open this URL in the user’s browser. Replace the placeholders with your values.

https://api.buddo.xyz/api/oauth/authorize
  ?client_id=6ba7b810-9dad-11d1-80b4-00c04fd430c8
  &redirect_uri=https://myapp.example.com/oauth/callback
  &response_type=code
  &scope=profile:read+points:read
  &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
  &code_challenge_method=S256

Important: The authorize URL is https://api.buddo.xyz/api/oauth/authorize — not buddo.xyz or any other domain.

Important: The user must be logged into Buddo (have an active session) before being redirected to /authorize. If the user does not have an active Buddo session, the authorize endpoint will return an error. Your app should link users to the Buddo login page at https://buddo.xyz/login first, then redirect them to /authorize once they are authenticated.

The user sees a consent screen showing your app name, description, and requested scopes. After approval, Buddo redirects to your redirect_uri with an authorization code:

https://myapp.example.com/oauth/callback?code=abc123def456

Step C: Exchange the code for tokens

Endpoint: POST /api/oauth/token Auth: None (unauthenticated)

curl -X POST https://api.buddo.xyz/api/oauth/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "authorization_code",
    "code": "abc123def456",
    "client_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
    "client_secret": "cs_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
    "redirect_uri": "https://myapp.example.com/oauth/callback",
    "code_verifier": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
  }'

Response (200 OK):

{
  "access_token": "at_live_x7k9m2p4q8r1s5t3v6w0y2z4a1b3c5d7",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "rt_live_j3h5f7d9b1z3x5c7v9n1m3k5g7e9a1s3",
  "player_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "session_token": "st_live_p2q4r6s8t0u2v4w6x8y0z2a4b6c8d0e2",
  "session_token_expires_at": "2026-05-13T14:30:00Z"
}

Token response fields:

Field Description
access_token Use this in the Authorization: Bearer header for all API calls. Expires in 1 hour.
token_type Always "Bearer".
expires_in Token lifetime in seconds (3600 = 1 hour).
refresh_token Use this to get a new access token without re-authorizing. Expires in 30 days. Rotated on each use.
player_id The UUID of the user who authorized your app. Use this to identify the user.
session_token A session identifier for the authorized session.
session_token_expires_at ISO 8601 expiry time for the session token.

Error (400 Bad Request):

{
  "error": "invalid_grant",
  "error_description": "Authorization code has expired or has already been used"
}

Step D: Use the access token

Include the token in every API request:

curl https://api.buddo.xyz/api/external/user \
  -H "Authorization: Bearer at_live_x7k9m2p4q8r1s5t3v6w0y2z4a1b3c5d7"

2.3 Refreshing Tokens

When an access token expires, use the refresh token to get a new one without requiring the user to re-authorize.

Endpoint: POST /api/oauth/token Auth: None (unauthenticated)

curl -X POST https://api.buddo.xyz/api/oauth/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "refresh_token",
    "refresh_token": "rt_live_j3h5f7d9b1z3x5c7v9n1m3k5g7e9a1s3",
    "client_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
    "client_secret": "cs_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
  }'

Response (200 OK):

{
  "access_token": "at_live_n3w7t0k3n1s5h3r3a1b2c3d4e5f6g7h8",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "rt_live_r0t4t3d9r3fr3sh1a2b3c4d5e6f7g8h9",
  "player_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "session_token": "st_live_p2q4r6s8t0u2v4w6x8y0z2a4b6c8d0e2",
  "session_token_expires_at": "2026-05-13T14:30:00Z"
}

Important: Each refresh returns a new refresh_token. The old one is invalidated immediately. Always store the latest refresh token. If you lose it or use a stale one, the user must re-authorize.

2.4 Verifying Tokens

You can verify an access token is valid without making an API call.

Endpoint: POST /api/oauth/token/verify Auth: None (unauthenticated)

curl -X POST https://api.buddo.xyz/api/oauth/token/verify \
  -H "Content-Type: application/json" \
  -d '{
    "token": "at_live_x7k9m2p4q8r1s5t3v6w0y2z4a1b3c5d7"
  }'

Response (200 OK — valid token):

{
  "active": true,
  "user_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "app_id": "550e8400-e29b-41d4-a716-446655440000",
  "scopes": ["profile:read", "points:read"],
  "expires_at": "2026-05-12T15:30:00Z"
}

Response (200 OK — invalid/expired token):

{
  "active": false
}

2.5 Looking Up App Info

Retrieve public information about any app by its client_id. Useful for displaying app details before starting an OAuth flow.

Endpoint: GET /api/oauth/apps/:client_id Auth: None (unauthenticated)

curl https://api.buddo.xyz/api/oauth/apps/6ba7b810-9dad-11d1-80b4-00c04fd430c8

Response (200 OK):

{
  "app": {
    "app_name": "My Rewards App",
    "app_description": "A loyalty program powered by Buddo points",
    "website_url": "https://myapp.example.com",
    "logo_url": null
  }
}

2.6 Managing Your Registered Apps

List your apps

Endpoint: GET /api/oauth/my-apps Auth: Bearer token (your Buddo session token)

curl https://api.buddo.xyz/api/oauth/my-apps \
  -H "Authorization: Bearer <your-session-token>"

Response (200 OK):

{
  "apps": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "app_name": "My Rewards App",
      "client_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
      "is_approved": true,
      "allowed_scopes": ["profile:read", "points:read"],
      "created_at": "2026-05-10T09:00:00Z"
    }
  ]
}

Update an app

Endpoint: PUT /api/oauth/my-apps/:id Auth: Bearer token (your Buddo session token)

curl -X PUT https://api.buddo.xyz/api/oauth/my-apps/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer <your-session-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "app_name": "My Rewards App v2",
    "app_description": "Updated loyalty program"
  }'

Response (200 OK): Returns the updated app object.

Request additional scopes

Endpoint: POST /api/oauth/my-apps/:id/scope-request Auth: Bearer token (your Buddo session token)

curl -X POST https://api.buddo.xyz/api/oauth/my-apps/550e8400-e29b-41d4-a716-446655440000/scope-request \
  -H "Authorization: Bearer <your-session-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "requested_scopes": ["points:spend", "app:balance:read"]
  }'

Response (200 OK):

{
  "message": "Scope request submitted for review",
  "requested_scopes": ["points:spend", "app:balance:read"]
}

Scope requests require admin review. You will be notified when they are approved.


3. Scopes

Scopes control what your app can do on behalf of a user. Request only the scopes you need.

Scope Description Auto-Approved
profile:read Read user profile information. Also required for session management, ad serving, and ad event recording. Yes
points:read Read the user’s point balance. Yes
points:spend Spend points from the user’s balance. The spent points are credited to your app balance. No (admin review)
points:transfer Transfer points between users or app→user payout. No (admin review)
app:balance:read Read your app’s point balance. No (admin review)
deploy:manage Create, manage, and destroy container deployments on buddocloud hosting. No (admin review)

Approval rules:


4. User API

These endpoints act on behalf of the user who authorized your app. All require an OAuth access token in the Authorization: Bearer header.

Base path: /api/external


4.1 Get User Profile

Read basic profile information for the authorized user.

Endpoint: GET /api/external/user Scope: profile:read Rate limit: 60/min

curl https://api.buddo.xyz/api/external/user \
  -H "Authorization: Bearer at_live_x7k9m2p4q8r1s5t3v6w0y2z4a1b3c5d7"

Response (200 OK):

{
  "user": {
    "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "username": "alice",
    "display_name": "Alice",
    "avatar_url": "https://api.buddo.xyz/avatars/f47ac10b.png",
    "created_at": "2026-01-15T10:30:00Z"
  }
}

Error (401 Unauthorized):

{
  "error": "Invalid or missing OAuth token"
}

Error (403 Forbidden):

{
  "error": "insufficient_scope",
  "required_scope": "profile:read"
}

4.2 Get Point Balance

Read the authorized user’s current point balance.

Endpoint: GET /api/external/points Scope: points:read Rate limit: 60/min

curl https://api.buddo.xyz/api/external/points \
  -H "Authorization: Bearer at_live_x7k9m2p4q8r1s5t3v6w0y2z4a1b3c5d7"

Response (200 OK):

{
  "points": 12500
}

Error (401 Unauthorized):

{
  "error": "Invalid or missing OAuth token"
}

Error (403 Forbidden):

{
  "error": "insufficient_scope",
  "required_scope": "points:read"
}

4.3 Spend Points

Deduct points from the user’s balance. The spent amount is credited to your app balance. Use this when a user purchases something in your app.

Endpoint: POST /api/external/points/spend Scope: points:spend Rate limit: 30/min

curl -X POST https://api.buddo.xyz/api/external/points/spend \
  -H "Authorization: Bearer at_live_x7k9m2p4q8r1s5t3v6w0y2z4a1b3c5d7" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 500,
    "description": "Premium skin unlock"
  }'

Request body:

Field Type Required Description
amount integer Yes Number of points to spend. Must be positive.
description string No Human-readable reason for the transaction.

Response (200 OK):

{
  "success": true,
  "amount_spent": 500,
  "points": 12000
}

Error (422 Unprocessable Entity):

{
  "error": "insufficient_balance"
}

Error (403 Forbidden):

{
  "error": "insufficient_scope",
  "required_scope": "points:spend"
}

4.4 Transfer Points

Transfer points from the authorized user to another user. Both users must have Buddo accounts.

Endpoint: POST /api/external/points/transfer Scope: points:transfer Rate limit: 30/min

curl -X POST https://api.buddo.xyz/api/external/points/transfer \
  -H "Authorization: Bearer at_live_x7k9m2p4q8r1s5t3v6w0y2z4a1b3c5d7" \
  -H "Content-Type: application/json" \
  -d '{
    "to_user_id": "c56a4180-65aa-42ec-a945-5fd21dec0538",
    "amount": 100,
    "description": "Gift to friend"
  }'

Request body:

Field Type Required Description
to_user_id string (UUID) Yes The recipient’s user ID.
amount integer Yes Number of points to transfer. Must be positive.
description string No Human-readable reason for the transfer.

Response (200 OK):

{
  "success": true,
  "amount_transferred": 100,
  "points": 11900
}

Error (422 Unprocessable Entity):

{
  "error": "insufficient_balance"
}

Error (400 Bad Request):

{
  "error": "cannot transfer to self"
}

4.5 Get App Balance

Read your app’s current point balance. Points accumulate here when users spend in your app.

Endpoint: GET /api/external/app/balance Scope: app:balance:read Rate limit: 60/min

curl https://api.buddo.xyz/api/external/app/balance \
  -H "Authorization: Bearer at_live_x7k9m2p4q8r1s5t3v6w0y2z4a1b3c5d7"

Response (200 OK):

{
  "points": 48300
}

Error (403 Forbidden):

{
  "error": "insufficient_scope",
  "required_scope": "app:balance:read"
}

4.6 Get Session Status

Check whether the authorized user’s session is still active.

Endpoint: GET /api/external/session/status Scope: profile:read Rate limit: 60/min

curl https://api.buddo.xyz/api/external/session/status \
  -H "Authorization: Bearer at_live_x7k9m2p4q8r1s5t3v6w0y2z4a1b3c5d7"

Response (200 OK):

{
  "session_active": true,
  "session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "points_earned": 500,
  "last_heartbeat": "2026-05-12T13:45:00Z",
  "is_alive": true
}

4.7 End Session

End the authorized user’s current session with your app.

Endpoint: POST /api/external/session/end Scope: profile:read Rate limit: 30/min

curl -X POST https://api.buddo.xyz/api/external/session/end \
  -H "Authorization: Bearer at_live_x7k9m2p4q8r1s5t3v6w0y2z4a1b3c5d7"

Response (200 OK):

{
  "message": "session ended",
  "session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}

4.8 Serve Ad

Request an ad to display in your app. Buddo selects an appropriate ad based on the user context and your app’s registered surfaces.

Endpoint: GET /api/external/ads/serve Scope: profile:read Rate limit: 60/min

curl "https://api.buddo.xyz/api/external/ads/serve?surface_type=banner" \
  -H "Authorization: Bearer at_live_x7k9m2p4q8r1s5t3v6w0y2z4a1b3c5d7"

Query parameters:

Parameter Type Required Description
surface_type string Yes The type of ad surface. One of: banner, video, interstitial, native, rewarded.

Response (200 OK):

{
  "campaign": {
    "id": "c1d2e3f4-a5b6-7890-cdef-1234567890ab",
    "name": "Summer Promo",
    "surface_type": "banner",
    "payout_rate": 100,
    "bitcoin_tier": "standard",
    "targeting_metadata": {}
  }
}

Response (404 Not Found): No campaign available for the requested surface type.


4.9 Record Ad Event

Report that an ad was viewed or clicked. This is how your app earns points from ad impressions.

Endpoint: POST /api/external/ads/event Scope: profile:read Rate limit: 30/min

curl -X POST https://api.buddo.xyz/api/external/ads/event \
  -H "Authorization: Bearer at_live_x7k9m2p4q8r1s5t3v6w0y2z4a1b3c5d7" \
  -H "Content-Type: application/json" \
  -d '{
    "campaign_id": "c1d2e3f4-a5b6-7890-cdef-1234567890ab",
    "event_type": "impression",
    "surface_type": "banner"
  }'

Request body:

Field Type Required Description
campaign_id string (UUID) Yes The campaign ID from the serve response (campaign.id). Use campaign_id — not ad_id.
event_type string Yes One of: impression, click.
surface_type string No The surface type used when serving the ad (e.g. banner, video, rewarded). Defaults to banner.

Response (201 Created):

{
  "event": {
    "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
    "campaign_id": "c1d2e3f4-a5b6-7890-cdef-1234567890ab",
    "event_type": "impression",
    "payout_rate_snapshot": 100,
    "surface_type": "banner",
    "inserted_at": "2026-05-12T14:30:00Z"
  }
}

Error (400 Bad Request):

{
  "error": "invalid_event_type",
  "message": "event_type must be one of: impression, click"
}

Error (404 Not Found):

{
  "error": "campaign_not_found"
}

5. App Management

These endpoints let you manage your app’s configuration and view analytics. They require an OAuth access token and verify that the token’s user owns the app.

Base path: /api/operator


5.1 Get App Details

Returns metadata for your app, including the count of users who have authorized it.

Endpoint: GET /api/operator/app Auth: Bearer token (must be the app owner)

curl https://api.buddo.xyz/api/operator/app \
  -H "Authorization: Bearer at_live_x7k9m2p4q8r1s5t3v6w0y2z4a1b3c5d7"

Response (200 OK):

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "client_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
  "redirect_uris": ["https://myapp.example.com/oauth/callback"],
  "scopes": ["profile:read", "points:read"],
  "is_approved": true,
  "listed": false,
  "total_users": 42
}

Response fields:

Field Type Description
id UUID Your app’s internal ID.
client_id UUID Your app’s public OAuth client identifier.
redirect_uris string[] Currently registered redirect URIs.
scopes string[] Approved scopes for your app.
is_approved boolean Whether the OAuth flow is active for your app.
listed boolean Whether your app appears in the public app directory.
total_users integer Count of distinct users who have ever authorized your app.

Error (403 Forbidden):

{
  "error": "forbidden"
}

This error means the token does not belong to the owner of this app.

Error (404 Not Found):

{
  "error": "app_not_found"
}

5.2 Update App Details

Update your app’s name, description, website, or redirect URIs. Only the fields you include in the request body are changed.

Endpoint: PUT /api/operator/app Auth: Bearer token (must be the app owner)

curl -X PUT https://api.buddo.xyz/api/operator/app \
  -H "Authorization: Bearer at_live_x7k9m2p4q8r1s5t3v6w0y2z4a1b3c5d7" \
  -H "Content-Type: application/json" \
  -d '{
    "app_name": "My Rewards App v2",
    "app_description": "Updated loyalty program with new features",
    "website_url": "https://v2.myapp.example.com",
    "redirect_uris": [
      "https://myapp.example.com/oauth/callback",
      "https://v2.myapp.example.com/oauth/callback"
    ]
  }'

Updatable fields:

Field Type Description
app_name string Display name shown on the consent screen.
app_description string Short description shown to users during authorization.
website_url string Your app’s homepage URL.
redirect_uris string[] Replaces the entire list of redirect URIs.

Note: You cannot change scopes through this endpoint. Use the scope request endpoint instead.

Response (200 OK): Same format as GET /api/operator/app.

Error (422 Unprocessable Entity):

{
  "error": "validation_failed",
  "details": {
    "redirect_uris": ["is invalid"]
  }
}

5.3 Get Analytics

Returns earnings and engagement data for your app over the last 30 days.

Endpoint: GET /api/operator/analytics Auth: Bearer token (must be the app owner)

curl https://api.buddo.xyz/api/operator/analytics \
  -H "Authorization: Bearer at_live_x7k9m2p4q8r1s5t3v6w0y2z4a1b3c5d7"

Response (200 OK):

{
  "earnings": {
    "total": 15200,
    "by_campaign": [
      {
        "campaign_id": "c1d2e3f4-a5b6-7890-cdef-1234567890ab",
        "earnings": 8400,
        "impressions": 84
      }
    ],
    "by_period": [
      {
        "date": "2026-05-11",
        "earnings": 500,
        "impressions": 5
      },
      {
        "date": "2026-05-12",
        "earnings": 700,
        "impressions": 7
      }
    ],
    "by_surface": [
      {
        "surface_type": "banner",
        "earnings": 15200,
        "impressions": 152
      }
    ]
  },
  "dau": {
    "last_30_days": [
      {
        "date": "2026-05-11",
        "dau": 12
      },
      {
        "date": "2026-05-12",
        "dau": 15
      }
    ],
    "retention": {
      "day_1": 0.65,
      "day_7": 0.32,
      "day_30": 0.12
    }
  }
}

Response fields:

Field Type Description
earnings.total integer Total points earned from ad impressions.
earnings.by_campaign array Earnings and impression counts broken down by campaign.
earnings.by_period array Daily earnings for the last 30 days.
earnings.by_surface array Earnings grouped by ad surface type.
dau.last_30_days array Daily active users for the last 30 days.
dau.retention object User retention rates at day 1, 7, and 30.

6. Deploy API

The Deploy API lets your app provision and manage containers on buddocloud hosting infrastructure. Containers run on *.apps.buddocloud.com by default, with optional custom domain support.

Base path: /api/deploy Auth: OAuth Bearer token with deploy:manage scope (admin review required) Rate limits: Read operations 30/min, write operations 10/min per token


6.1 List Tiers

Returns all available deployment tiers with resource limits and pricing.

Endpoint: GET /api/deploy/tiers Scope: deploy:manage

curl https://api.buddo.xyz/api/deploy/tiers \
  -H "Authorization: Bearer at_live_x7k9m2p4q8r1s5t3v6w0y2z4a1b3c5d7"

Response (200 OK):

{
  "tiers": [
    {
      "id": "uuid",
      "name": "Starter",
      "slug": "starter",
      "payment_method": "points",
      "memory_limit_mb": 256,
      "cpu_limit_millicores": 250,
      "custom_domain_allowed": false,
      "monthly_cost_points": 1000000,
      "monthly_cost_sats": null,
      "monthly_stipend_points": 0,
      "ad_reach_multiplier": 1.0
    }
  ]
}

Tier fields:

Field Type Description
slug string Use this in POST /api/deploy/apps as the tier field.
payment_method string "points" or "bitcoin".
memory_limit_mb integer Container memory ceiling in MB.
cpu_limit_millicores integer CPU allocation in millicores (1000 = 1 vCPU).
custom_domain_allowed boolean Whether you can attach a custom domain.
monthly_cost_points integer Monthly billing in Buddo points (for points tiers).
monthly_cost_sats integer Monthly billing in satoshis (for bitcoin tiers, null otherwise).
monthly_stipend_points integer Points credited to your app each month on this tier.
ad_reach_multiplier float Ad campaign reach multiplier for this tier.

6.2 List Deployments

Returns all deployments owned by your app.

Endpoint: GET /api/deploy/apps Scope: deploy:manage

curl https://api.buddo.xyz/api/deploy/apps \
  -H "Authorization: Bearer at_live_x7k9m2p4q8r1s5t3v6w0y2z4a1b3c5d7"

Response (200 OK):

{
  "deployments": [
    {
      "id": "uuid",
      "app_id": "uuid",
      "deploy_tier_id": "uuid",
      "container_name": "app-<app_id>-myapp",
      "container_image": "harbor.buddo.xyz:4443/myorg/myapp:latest",
      "subdomain": "myapp",
      "custom_domain": null,
      "host_server": "178.105.148.216",
      "status": "running",
      "memory_limit_mb": 256,
      "cpu_limit_millicores": 250,
      "health_status": "healthy",
      "last_health_check": "2026-05-20T12:00:00Z",
      "billing_status": "active",
      "deployed_at": "2026-05-20T10:00:00Z",
      "stopped_at": null,
      "inserted_at": "2026-05-20T09:55:00Z"
    }
  ]
}

6.3 Create Deployment

Provisions a new container. The container starts asynchronously — poll GET /api/deploy/apps/:id/status to track progress.

Endpoint: POST /api/deploy/apps Scope: deploy:manage

curl -X POST https://api.buddo.xyz/api/deploy/apps \
  -H "Authorization: Bearer at_live_x7k9m2p4q8r1s5t3v6w0y2z4a1b3c5d7" \
  -H "Content-Type: application/json" \
  -d '{
    "subdomain": "myapp",
    "image": "harbor.buddo.xyz:4443/myorg/myapp:v1.0.0",
    "tier": "starter",
    "port": 3000,
    "env_vars": {
      "NODE_ENV": "production",
      "DATABASE_URL": "postgres://..."
    }
  }'

Request body:

Field Type Required Description
subdomain string Yes Lowercase letters, digits, hyphens only. Max 32 chars. Cannot be a reserved word (www, api, admin, etc.). Becomes <subdomain>.apps.buddocloud.com.
image string Yes Docker image reference. Must be from an allowed registry: harbor.buddo.xyz:4443/, docker.io/library/, or ghcr.io/.
tier string Yes Tier slug from GET /api/deploy/tiers.
port integer No Container port to expose. Defaults to 3000. Must be 1–65535.
env_vars object No Key-value environment variables. Keys must match [A-Z_][A-Z0-9_]*, max 50 keys, values max 4096 bytes each. The BUDDO_ prefix is reserved.
custom_domain string No Custom domain to attach (requires a tier with custom_domain_allowed: true).

Response (201 Created):

{
  "deployment": {
    "id": "uuid",
    "status": "pending",
    "subdomain": "myapp",
    ...
  }
}

Error (422 Unprocessable Entity):

{
  "error": "subdomain must contain only lowercase letters, digits, and hyphens, and cannot start or end with a hyphen"
}

Error (429 Too Many Requests):

{
  "error": "deployment limit reached: operator cap (5) exceeded"
}

Notes: - Each OAuth app is capped at 5 concurrent deployments. Contact support to increase. - Global capacity limits apply. If the platform is at capacity you will receive global_cap_exceeded. - Deployment is asynchronous. status: "pending" on creation is normal; poll status until "running" or "failed".


6.4 Get Deployment

Returns full details for a single deployment.

Endpoint: GET /api/deploy/apps/:id Scope: deploy:manage

curl https://api.buddo.xyz/api/deploy/apps/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer at_live_x7k9m2p4q8r1s5t3v6w0y2z4a1b3c5d7"

Response (200 OK): Same deployment object as in the list response.

Error (403 Forbidden): The deployment exists but belongs to a different app.

Error (404 Not Found): No deployment with this ID.


6.5 Get Deployment Status

Returns only the status fields — lightweight alternative to the full show endpoint.

Endpoint: GET /api/deploy/apps/:id/status Scope: deploy:manage

curl https://api.buddo.xyz/api/deploy/apps/550e8400-e29b-41d4-a716-446655440000/status \
  -H "Authorization: Bearer at_live_x7k9m2p4q8r1s5t3v6w0y2z4a1b3c5d7"

Response (200 OK):

{
  "id": "uuid",
  "status": "running",
  "health_status": "healthy",
  "last_health_check": "2026-05-20T12:00:00Z",
  "subdomain": "myapp"
}

Status values:

Value Meaning
pending Container creation queued.
deploying Container being pulled and started.
running Container is up and passing health checks.
stopped Container has been manually stopped.
failed Deployment failed. Check logs.

Health status values:

Value Meaning
healthy Health check passing.
unhealthy Health check failing.
unknown Not yet checked.

6.6 Stop Deployment

Stops the container without removing it. The deployment record and subdomain are preserved. Use restart to bring it back up.

Endpoint: POST /api/deploy/apps/:id/stop Scope: deploy:manage

curl -X POST https://api.buddo.xyz/api/deploy/apps/550e8400-e29b-41d4-a716-446655440000/stop \
  -H "Authorization: Bearer at_live_x7k9m2p4q8r1s5t3v6w0y2z4a1b3c5d7"

Response (200 OK): Updated deployment object with status: "stopped".


6.7 Restart Deployment

Stops and restarts the container using the same image and configuration. Useful after config changes.

Endpoint: POST /api/deploy/apps/:id/restart Scope: deploy:manage

curl -X POST https://api.buddo.xyz/api/deploy/apps/550e8400-e29b-41d4-a716-446655440000/restart \
  -H "Authorization: Bearer at_live_x7k9m2p4q8r1s5t3v6w0y2z4a1b3c5d7"

Response (200 OK): Updated deployment object.

Error (422 Unprocessable Entity): Container operation failed. Check details for the reason.


6.8 Delete Deployment

Permanently destroys the container and removes the deployment record. The subdomain is released and can be reused.

Endpoint: DELETE /api/deploy/apps/:id Scope: deploy:manage

curl -X DELETE https://api.buddo.xyz/api/deploy/apps/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer at_live_x7k9m2p4q8r1s5t3v6w0y2z4a1b3c5d7"

Response (200 OK):

{
  "message": "deployment destroyed",
  "id": "uuid"
}

6.9 Get Deployment Logs

Returns the action log for a deployment — a history of create, stop, restart, and destroy operations.

Endpoint: GET /api/deploy/apps/:id/logs Scope: deploy:manage

curl https://api.buddo.xyz/api/deploy/apps/550e8400-e29b-41d4-a716-446655440000/logs \
  -H "Authorization: Bearer at_live_x7k9m2p4q8r1s5t3v6w0y2z4a1b3c5d7"

Response (200 OK):

{
  "logs": [
    {
      "id": "uuid",
      "action": "create",
      "status": "success",
      "details": {"tier": "starter", "image": "harbor.buddo.xyz:4443/myorg/myapp:v1.0.0"},
      "initiated_by": null,
      "inserted_at": "2026-05-20T10:00:00Z"
    }
  ]
}

7. Social API

The Social API provides friends, presence, and chat features. These endpoints are for platform users — they require a Buddo JWT (not an OAuth app token). Your game or app backend calls these on behalf of users who have logged in via Buddo.

Base path: /api/social Auth: Authorization: Bearer <JWT> — the JWT issued by POST /api/auth/login. Not the OAuth access token. Missing token: Returns 401 {"error": "Invalid or missing authentication token"}


7.1 List Friends

Returns the current user’s accepted friends.

Endpoint: GET /api/social/friends Auth: JWT required

curl https://api.buddo.xyz/api/social/friends \
  -H "Authorization: Bearer <jwt-token>"

Response (200 OK):

[
  {
    "id": "uuid",
    "username": "alice",
    "email": "alice@example.com"
  }
]

7.2 List Pending Friend Requests

Returns incoming friend requests where the current user is the recipient.

Endpoint: GET /api/social/friends/pending Auth: JWT required

curl https://api.buddo.xyz/api/social/friends/pending \
  -H "Authorization: Bearer <jwt-token>"

Response (200 OK):

[
  {
    "id": "uuid",
    "friendship_id": "uuid",
    "username": "bob",
    "sent_at": "2026-05-20T09:00:00Z"
  }
]

Note: id is the requester’s user ID. friendship_id is the DB row UUID — pass this to PUT /api/social/friends/:id.


7.3 Send Friend Request

Send a friend request to another user.

Endpoint: POST /api/social/friends Auth: JWT required

curl -X POST https://api.buddo.xyz/api/social/friends \
  -H "Authorization: Bearer <jwt-token>" \
  -H "Content-Type: application/json" \
  -d '{"friend_id": "c56a4180-65aa-42ec-a945-5fd21dec0538"}'

Request body:

Field Type Required Description
friend_id string (UUID) Yes Target user’s UUID.

Response (201 Created):

{
  "friendship_id": "uuid",
  "status": "pending"
}

Error responses:

Status Body Condition
404 {"error": "User not found"} Target user doesn’t exist.
409 {"error": "Already friends"} Friendship already accepted.
409 {"error": "Friend request already sent"} Pending request exists.
403 {"error": "Cannot send request to this user"} Target has blocked caller.

7.4 Accept or Reject Friend Request

Accept or reject an incoming friend request.

Endpoint: PUT /api/social/friends/:id Auth: JWT required — current user must be the recipient

curl -X PUT https://api.buddo.xyz/api/social/friends/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer <jwt-token>" \
  -H "Content-Type: application/json" \
  -d '{"action": "accept"}'

Request body:

Field Type Required Description
action string Yes "accept" or "reject".

Response (200 OK):

{
  "status": "accepted"
}

Error (403 Forbidden): {"error": "Not authorized"} — caller is not the recipient of this request.


7.5 Remove Friend

Remove an existing friendship. Either party can call this.

Endpoint: DELETE /api/social/friends/:id Auth: JWT required — current user must be one of the two parties

curl -X DELETE https://api.buddo.xyz/api/social/friends/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer <jwt-token>"

Response (200 OK):

{
  "status": "removed"
}

7.6 Search Users

Search for Buddo users by username prefix. Use this to let users find and add friends.

Endpoint: GET /api/social/users/search?q=<query> Auth: JWT required

curl "https://api.buddo.xyz/api/social/users/search?q=ali" \
  -H "Authorization: Bearer <jwt-token>"

Query parameters:

Parameter Type Required Description
q string Yes Search query. Minimum 2 characters.

Response (200 OK):

{
  "users": [
    {
      "id": "uuid",
      "username": "alice"
    }
  ]
}

Error (400 Bad Request): {"error": "Query must be at least 2 characters"}


7.7 Block User

Block a user. If a friendship or pending request exists between the two users, it is deleted and replaced with a block record.

Endpoint: POST /api/social/block Auth: JWT required

curl -X POST https://api.buddo.xyz/api/social/block \
  -H "Authorization: Bearer <jwt-token>" \
  -H "Content-Type: application/json" \
  -d '{"user_id": "c56a4180-65aa-42ec-a945-5fd21dec0538"}'

Request body:

Field Type Required Description
user_id string (UUID) Yes UUID of the user to block.

Response (200 OK):

{
  "status": "blocked"
}

Error (409 Conflict): {"error": "User already blocked"}


7.8 Unblock User

Remove a block on a previously blocked user.

Endpoint: DELETE /api/social/block/:user_id Auth: JWT required — must be the blocker

curl -X DELETE https://api.buddo.xyz/api/social/block/c56a4180-65aa-42ec-a945-5fd21dec0538 \
  -H "Authorization: Bearer <jwt-token>"

Response (200 OK):

{
  "status": "unblocked"
}

Error (404 Not Found): {"error": "Block record not found"}


7.9 Get Presence

Two modes: query specific users by ID, or get all online friends.

Endpoint: GET /api/social/presence Auth: JWT required

Mode 1 — Specific users:

curl "https://api.buddo.xyz/api/social/presence?user_ids=uuid1,uuid2,uuid3" \
  -H "Authorization: Bearer <jwt-token>"

Mode 2 — Online friends (no query params):

curl https://api.buddo.xyz/api/social/presence \
  -H "Authorization: Bearer <jwt-token>"

Response (200 OK):

{
  "f47ac10b-58cc-4372-a567-0e02b2c3d479": {
    "status": "online",
    "current_game": "slots",
    "lobby": "main",
    "last_seen": "2026-05-20T12:00:00Z"
  }
}

Notes: - Presence is in-memory (ETS). It resets on server restart. - Entries expire after 15 minutes of no heartbeat. - Offline users are omitted from the response map. - Polling recommended over WebSocket — heartbeat with POST /api/social/presence every 60 seconds.


7.10 Post Presence Heartbeat

Register the current user as online and update their location context.

Endpoint: POST /api/social/presence Auth: JWT required

curl -X POST https://api.buddo.xyz/api/social/presence \
  -H "Authorization: Bearer <jwt-token>" \
  -H "Content-Type: application/json" \
  -d '{"game": "slots", "lobby": "main"}'

Request body (all fields optional):

Field Type Description
game string Current game identifier (e.g. "slots", "bit-battle").
lobby string Lobby or room identifier.

Response (200 OK):

{
  "status": "ok"
}

7.11 Remove Presence

Remove the current user from the presence table (logout or game exit).

Endpoint: DELETE /api/social/presence Auth: JWT required

curl -X DELETE https://api.buddo.xyz/api/social/presence \
  -H "Authorization: Bearer <jwt-token>"

Response (200 OK):

{
  "status": "ok"
}

7.12 Get Table Presence

Returns all users currently present at a specific game table or lobby reference.

Endpoint: GET /api/social/presence/table?game=<game>&ref_id=<id> Auth: JWT required

curl "https://api.buddo.xyz/api/social/presence/table?game=slots&ref_id=table-42" \
  -H "Authorization: Bearer <jwt-token>"

Query parameters:

Parameter Type Required Description
game string Yes Game identifier.
ref_id string Yes Table or room identifier.

Response (200 OK):

{
  "users": [
    {
      "user_id": "uuid",
      "current_game": "slots",
      "lobby": "table-42",
      "last_seen": "2026-05-20T12:00:00Z"
    }
  ]
}

Error (400 Bad Request): {"error": "game and ref_id params are required"}


7.13 Create / Ensure Chat Channel

Create a chat channel or return the existing one for the given type + ref_id combination. This endpoint is idempotent.

Endpoint: POST /api/social/chat/channels Auth: JWT required

curl -X POST https://api.buddo.xyz/api/social/chat/channels \
  -H "Authorization: Bearer <jwt-token>" \
  -H "Content-Type: application/json" \
  -d '{"type": "lobby", "ref_id": "main"}'

Request body:

Field Type Required Description
type string Yes "lobby" or "table".
ref_id string No Table or lobby ID. null means the global channel for this type.

Response (200 OK):

{
  "id": "uuid",
  "type": "lobby",
  "ref_id": "main"
}

Note: Save the returned id — pass it as channel_id to the message endpoints.


7.14 Get Message History

Fetch message history for a channel. Results are sorted newest-first. Default limit is 50.

Endpoint: GET /api/social/chat/:channel_id/messages Auth: JWT required

curl "https://api.buddo.xyz/api/social/chat/550e8400-e29b-41d4-a716-446655440000/messages?limit=20" \
  -H "Authorization: Bearer <jwt-token>"

Query parameters:

Parameter Type Required Description
limit integer No Max messages to return. Defaults to 50.
before ISO8601 datetime No Only return messages sent before this time (for pagination).

Response (200 OK):

[
  {
    "id": "uuid",
    "sender_id": "uuid",
    "sender_username": "alice",
    "filtered_body": "Hello everyone!",
    "sent_at": "2026-05-20T12:00:00Z"
  }
]

Notes: - filtered_body is the word-filtered version — moderate and severe words are replaced with * characters. Raw bodies are never exposed via the API. - For pagination, pass the sent_at of the oldest message in the current batch as the before parameter.


7.15 Send Message

Send a message to a chat channel.

Endpoint: POST /api/social/chat/:channel_id/messages Auth: JWT required

curl -X POST https://api.buddo.xyz/api/social/chat/550e8400-e29b-41d4-a716-446655440000/messages \
  -H "Authorization: Bearer <jwt-token>" \
  -H "Content-Type: application/json" \
  -d '{"body": "Hello everyone!"}'

Request body:

Field Type Required Description
body string Yes Message text. Maximum 1000 characters.

Response (201 Created):

{
  "id": "uuid",
  "sender_id": "uuid",
  "sender_username": "alice",
  "filtered_body": "Hello everyone!",
  "sent_at": "2026-05-20T12:00:00Z"
}

Error (422 Unprocessable Entity): {"error": "Invalid message", "details": {...}} — e.g. body exceeds 1000 characters.


8. Public Endpoints

These endpoints require no authentication.


8.1 List Published Apps

Browse all published Buddo Business apps in the directory.

Endpoint: GET /api/public/operators

curl https://api.buddo.xyz/api/public/operators

Response (200 OK):

{
  "apps": [
    {
      "app_name": "Bit Battle",
      "app_description": "Competitive trivia game with Buddo point prizes",
      "website_url": "https://bitbattle.example.com",
      "logo_url": "https://api.buddo.xyz/logos/bitbattle.png"
    },
    {
      "app_name": "Daily Quiz",
      "app_description": "Learn something new every day and earn points",
      "website_url": "https://dailyquiz.example.com",
      "logo_url": null
    }
  ]
}

8.2 Health Check

Endpoint: GET /health

curl https://api.buddo.xyz/health

Response (200 OK):

{
  "status": "ok"
}

8.3 OAuth Discovery

Endpoint: GET /.well-known/oauth-protected-resource

Returns OAuth metadata including supported scopes and token endpoints. Useful for automated client configuration.

curl https://api.buddo.xyz/.well-known/oauth-protected-resource

8.4 OpenAPI Specification

Endpoint: GET /.well-known/openapi.json

Returns the full OpenAPI 3.0 specification for the Buddo Business API. Use this with code generation tools or API clients.

curl https://api.buddo.xyz/.well-known/openapi.json

9. Error Reference

All errors return JSON with an error field. Some include additional detail fields.

Standard Error Format

{
  "error": "error_code",
  "error_description": "Human-readable explanation"
}

Validation Error Format

{
  "error": "validation_failed",
  "details": {
    "field_name": ["error message"]
  }
}

HTTP Status Codes

Status Meaning When it happens
200 OK Request succeeded.
201 Created Resource created (app registration).
204 No Content Success with no response body (e.g., no ad available).
400 Bad Request Malformed request, invalid parameters, or business logic error (e.g., insufficient balance).
401 Unauthorized Missing, invalid, or expired token.
403 Forbidden Token is valid but lacks the required scope, or you are not the owner of the requested resource.
404 Not Found Resource does not exist.
422 Unprocessable Entity Validation failed on one or more fields. Check the details object.
429 Too Many Requests Rate limit exceeded. Wait before retrying.
500 Internal Server Error Something went wrong on our end. Retry with backoff.

Common Error Codes

Error Code Status Description
Invalid or missing OAuth token 401 The Authorization header is missing, or the token is expired or revoked.
insufficient_scope 403 Your token does not include the scope required for this endpoint.
forbidden 403 You are not the owner of this app.
app_not_found 404 No app exists for the given ID or token.
invalid_grant 400 The authorization code is expired, already used, or does not match the client.
insufficient_balance 422 The user does not have enough points for the requested spend or transfer.
recipient_not_found 404 The to_user_id in a transfer does not match any user.
validation_failed 422 One or more fields failed validation. See details for specifics.
response_type must be 'code' 400 The OAuth authorize request used an unsupported response type.

10. Rate Limits

Rate limits are enforced per IP address. When you exceed a limit, you receive a 429 Too Many Requests response. Wait before retrying.

Endpoint Group Limit Applies To
App registration (POST /api/oauth/apps) 3/hour Per IP
External API reads (GET /api/external/*) 60/min Per IP
External API writes (POST /api/external/*) 30/min Per IP
App Management (/api/operator/*) Reasonable use Per IP
Public endpoints No enforced limit Per IP

Tips for staying within limits:


11. Integration Patterns

Game Integration

Your game awards achievements and lets players spend points on in-game items.

  1. Player launches your game and clicks “Sign in with Buddo”
  2. You redirect to the Buddo authorize URL with profile:read and points:spend scopes
  3. After authorization, you call GET /api/external/user to get the player’s profile
  4. You call GET /api/external/points to show their balance in your game UI
  5. When they buy a skin or power-up, you call POST /api/external/points/spend with the item cost
  6. You serve ads between levels with GET /api/external/ads/serve and record views with POST /api/external/ads/event

Content Platform

Your platform rewards users for reading articles or watching videos.

  1. User connects their Buddo account via OAuth (profile:read, points:read)
  2. You display their point balance as a motivational element
  3. You serve Buddo ads alongside your content using the ads endpoints
  4. Ad impressions earn points for your app, which you can track in the analytics dashboard
  5. Users see their growing balance and are encouraged to engage more

Reward Program

Your business gives loyalty points for customer actions.

  1. Customer links their Buddo account to your loyalty program via OAuth
  2. When they make a purchase or complete an action, you use POST /api/external/points/transfer to send them points from another user (peer-to-peer gifting) or design your flow around the spend/earn mechanics
  3. Customers can check their balance anytime through GET /api/external/points
  4. You track engagement through GET /api/operator/analytics

12. Quick Start

A complete curl walkthrough from zero to your first API call. Run these commands in order.

Prerequisites

# === Configuration ===
SESSION_TOKEN="<your-session-token>"
BASE="https://api.buddo.xyz"

# === Step 1: Register your app ===
REGISTER=$(curl -s -X POST "$BASE/api/oauth/apps" \
  -H "Authorization: Bearer $SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "app_name": "Quick Start Demo",
    "app_description": "Testing the Buddo Business API",
    "website_url": "https://example.com",
    "redirect_uris": ["https://example.com/callback"],
    "allowed_scopes": ["profile:read", "points:read"]
  }')

echo "Registration response:"
echo "$REGISTER" | python3 -m json.tool

# Extract credentials
CLIENT_ID=$(echo "$REGISTER" | python3 -c "import sys,json; print(json.load(sys.stdin)['app']['client_id'])")
CLIENT_SECRET=$(echo "$REGISTER" | python3 -c "import sys,json; print(json.load(sys.stdin)['app']['client_secret'])")

echo "Client ID: $CLIENT_ID"
echo "Client Secret: $CLIENT_SECRET (store this securely!)"

# === Step 2: Generate PKCE challenge ===
CODE_VERIFIER=$(openssl rand -base64 32 | tr -d '=' | tr '/+' '_-')
CODE_CHALLENGE=$(echo -n "$CODE_VERIFIER" | openssl dgst -sha256 -binary | openssl base64 | tr -d '=' | tr '/+' '_-')

echo "Code Verifier: $CODE_VERIFIER"
echo "Code Challenge: $CODE_CHALLENGE"

# === Step 3: Open this URL in a browser ===
echo ""
echo "Open this URL in your browser to authorize:"
echo "${BASE}/api/oauth/authorize?client_id=${CLIENT_ID}&redirect_uri=https://example.com/callback&response_type=code&scope=profile:read+points:read&code_challenge=${CODE_CHALLENGE}&code_challenge_method=S256"
echo ""
echo "After authorizing, copy the 'code' parameter from the redirect URL."

# === Step 4: Paste the authorization code and exchange it ===
read -p "Paste authorization code: " AUTH_CODE

TOKEN_RESP=$(curl -s -X POST "$BASE/api/oauth/token" \
  -H "Content-Type: application/json" \
  -d "{
    \"grant_type\": \"authorization_code\",
    \"code\": \"$AUTH_CODE\",
    \"client_id\": \"$CLIENT_ID\",
    \"client_secret\": \"$CLIENT_SECRET\",
    \"redirect_uri\": \"https://example.com/callback\",
    \"code_verifier\": \"$CODE_VERIFIER\"
  }")

echo "Token response:"
echo "$TOKEN_RESP" | python3 -m json.tool

ACCESS_TOKEN=$(echo "$TOKEN_RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")

# === Step 5: Call the User API ===
echo ""
echo "=== User Profile ==="
curl -s "$BASE/api/external/user" \
  -H "Authorization: Bearer $ACCESS_TOKEN" | python3 -m json.tool

echo ""
echo "=== Point Balance ==="
curl -s "$BASE/api/external/points" \
  -H "Authorization: Bearer $ACCESS_TOKEN" | python3 -m json.tool

echo ""
echo "=== Session Status ==="
curl -s "$BASE/api/external/session/status" \
  -H "Authorization: Bearer $ACCESS_TOKEN" | python3 -m json.tool

# === Step 6: Check your app's management endpoints ===
echo ""
echo "=== App Details ==="
curl -s "$BASE/api/operator/app" \
  -H "Authorization: Bearer $ACCESS_TOKEN" | python3 -m json.tool

echo ""
echo "=== App Analytics ==="
curl -s "$BASE/api/operator/analytics" \
  -H "Authorization: Bearer $ACCESS_TOKEN" | python3 -m json.tool

After running this, you have a working Buddo Business integration. From here:


Questions or issues? Contact the Buddo Business team for support.