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.
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.
Sign up at buddo.xyz and verify your email address. You need a verified account before you can register an 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.
Your app’s status depends on which scopes you request:
profile:read and/or points:read, your app is
approved instantly and can begin the OAuth flow immediately.points:spend, points:transfer, or
app:balance:read, your app enters a pending state. The
OAuth flow will not work until an admin reviews and approves it.Recommendation: Start with auto-approved scopes to begin building. Once your integration works, use the scope request endpoint to apply for additional permissions.
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) |
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.
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"]
}
}PKCE adds a one-time challenge to the authorization flow. Here is the complete sequence.
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"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
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"
}Include the token in every API request:
curl https://api.buddo.xyz/api/external/user \
-H "Authorization: Bearer at_live_x7k9m2p4q8r1s5t3v6w0y2z4a1b3c5d7"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.
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
}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-00c04fd430c8Response (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
}
}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"
}
]
}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.
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.
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:
profile:read, points:read), your app is
approved instantly at registration.points:spend, points:transfer,
app:balance:read, deploy:manage), your app
enters a pending state. The OAuth flow will return an error until
approval is granted.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
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"
}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"
}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"
}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"
}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"
}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
}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"
}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.
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"
}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
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"
}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"]
}
}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. |
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
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. |
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"
}
]
}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".
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.
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. |
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".
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.
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"
}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"
}
]
}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"}
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"
}
]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.
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. |
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.
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"
}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"}
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"}
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"}
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.
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"
}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"
}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"}
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.
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.
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.
These endpoints require no authentication.
Browse all published Buddo Business apps in the directory.
Endpoint: GET /api/public/operators
curl https://api.buddo.xyz/api/public/operatorsResponse (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
}
]
}Endpoint: GET /health
curl https://api.buddo.xyz/healthResponse (200 OK):
{
"status": "ok"
}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-resourceEndpoint:
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.jsonAll errors return JSON with an error field. Some include
additional detail fields.
{
"error": "error_code",
"error_description": "Human-readable explanation"
}{
"error": "validation_failed",
"details": {
"field_name": ["error message"]
}
}| 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. |
| 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. |
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:
/api/external/user or /api/external/points on
every page load.429, implement exponential backoff:
wait 1s, then 2s, then 4s before retrying.Your game awards achievements and lets players spend points on in-game items.
profile:read and points:spend scopesGET /api/external/user to
get the player’s profileGET /api/external/points to show their balance
in your game UIPOST /api/external/points/spend with the item costGET /api/external/ads/serve and record views with
POST /api/external/ads/eventYour platform rewards users for reading articles or watching videos.
profile:read, points:read)Your business gives loyalty points for customer actions.
POST /api/external/points/transfer to send them points from
another user (peer-to-peer gifting) or design your flow around the
spend/earn mechanicsGET /api/external/pointsGET /api/operator/analyticsA complete curl walkthrough from zero to your first API call. Run these commands in order.
curl, openssl, and python3
available on your command line# === 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.toolAfter running this, you have a working Buddo Business integration. From here:
points:spend to build a points-based store
(requires admin approval)Questions or issues? Contact the Buddo Business team for support.