BuddyPro End-user API (Client API)
OpenAI-compatible API access for BuddyPro end-users — any Buddy user can generate their own key to programmatically access their personal BuddyPro profile. This is a pay-per-use API billed via prepaid credits purchased through the BuddyPro instance you use; the instance sets the per-request price. It accepts requests structured like the OpenAI Chat Completions API and returns responses in the same format, with BuddyPro-specific extensions in message (e.g., image, audio).
Not the Owner API. If you are a BuddyPro instance owner or team member building internal tools, automations, or service integrations, see the Owner API (separate B2B documentation) instead.
Overview
| Property | Value |
|---|---|
| Path | POST /v1/chat/completions |
| Auth | Authorization: Bearer bapi_B2C_... header |
| Format | OpenAI Chat Completions compatible |
| Billing | Prepaid credits (Stripe) — purchased through your BuddyPro instance, billed per request |
| Streaming | Not supported yet |
Who Can Use the Client API
Any authenticated BuddyPro user can generate a Client API key — not just instance owners or team members — provided the instance owner has enabled the Client API. The Client API is off by default; if the owner hasn't turned it on, /generateClientApiKey returns an error and existing keys stop working. Once enabled, the key is tied to your own personal BuddyPro profile: every request is processed as if you sent a message in Telegram, with full access to your conversation history, long-term memory, and personal settings.
To actually use the key, you must set up credit billing — and that has two requirements:
- An active subscription to the BuddyPro instance you are using.
- The instance must accept API credit payments. Billing is handled by the instance (its owner), who sets the per-request price; an instance whose owner hasn't enabled API payments cannot sell you credits.
If either requirement isn't met, /setupApiCredits returns an explanatory error (see Setting Up Credit Billing).
Persistent Memory & Conversation History
Like the Telegram interface, BuddyPro maintains long-term memory and full conversation history for your profile. Every API request is treated exactly like a message sent in Telegram — it is saved to your chat history, contributes to BuddyPro's memory about you, and influences future responses.
This means:
- Conversations are cumulative. BuddyPro remembers everything said through the API, just as it remembers Telegram conversations. You do not need to (and should not) send conversation history — just send the current message.
- Memory builds over time. BuddyPro learns preferences, facts, and context from API interactions, the same way it does from Telegram chats.
- Stateless mode available. Set
x_buddy_saveToHistory: falseto make a request that doesn't persist anything — no chat history, no memory updates, no profile changes. See Stateless Mode.
Do not send conversation history in the
messagesarray. Send only the current user message. BuddyPro stores and manages conversation context server-side.
Privacy & Data Access
The Client API provides full data ownership for end-users. Unlike the Owner API (where the instance owner can switch to any test profile they created), the Client API is designed around user privacy:
- Your API key is yours only. The BuddyPro instance owner cannot see your key.
- Your profile is private. The instance owner cannot switch to your personal Telegram profile through the bot — Telegram user IDs are numeric and the
/testcommand blocks numeric IDs entirely. - Your isolated profiles are protected. Profiles you create via the
userfield are attributed to your own API key. The instance owner cannot switch to them — the bot enforces that you can only switch to profiles created by your own keys.
What this means in practice:
The Client API is safe to use for personal integrations, automations, and tools where you want programmatic access to your own BuddyPro profile. You control your data through the same key you generated.
Authentication
Getting a Client API Key
Send this command to your BuddyPro bot in Telegram:
/generateClientApiKey:my-app
The name (:my-app) is optional — if omitted, a name is auto-generated. You'll receive a key starting with bapi_B2C_.
Store this key securely — it won't be shown again. To revoke it:
/invalidateApiKey:my-app(you can use the key name or the raw key string)
After generating the key, you must set up credit billing before making your first API request.
NOTE: By default, requests use your own personal BuddyPro profile — messages are saved to your history and contribute to your memory. Use the
userfield to create additional isolated profiles with separate conversation history and memory (e.g., for different projects or contexts). See User Isolation.
Setting Up Credit Billing
The Client API requires prepaid credits via Stripe. After generating your key, run:
/setupApiCredits:100:20
This opens a Stripe checkout where you purchase your initial credit top-up. The two parameters are:
| Parameter | Description | Minimum | Maximum |
|---|---|---|---|
topUpAmount | Amount in USD to deposit when credits run low | $10 | $10,000 |
rechargeAt | Credit balance threshold that triggers an automatic top-up | $2 | $10,000 |
Example: /setupApiCredits:100:20 — buy $100 initially, automatically recharge with $100 whenever your balance drops below $20.
Credits are automatically recharged via your saved payment method when your balance falls below the rechargeAt threshold. You don't need to manually top up after the initial setup.
Payment goes to the instance, not to BuddyPro. Your credit checkout and saved payment method are processed by the BuddyPro instance you use (its owner), who sets the per-request price. The amount deducted from your balance per request is that instance's price.
Setup requirements. /setupApiCredits returns an error if:
- You don't have an active subscription to the instance: "An active subscription is required to set up Client API credits."
- The instance isn't set up to accept API payments: "This instance is not set up to accept API credit payments yet. Please contact the instance owner."
Once billing is active, use /changeApiCreditsTopUp (below) to adjust the amounts — calling /setupApiCredits again returns an error telling you billing is already set up.
Changing Credit Settings
To change your top-up amount or recharge threshold without triggering a new Stripe checkout:
/changeApiCreditsTopUp:20:5
Updates the auto-recharge to $20 and the threshold to $5. Your existing billing relationship is preserved — no new checkout required.
API Key Management
| Command | Description |
|---|---|
/generateClientApiKey:{name} | Create a new Client API key (name is optional) |
/invalidateApiKey:{name or bapi_B2C_...} | Revoke a key by its name or the raw key string |
/getApiStats | List all your active API keys and usage |
/setupApiCredits:{topUp}:{rechargeAt} | Set up billing (first time — opens Stripe checkout) |
/changeApiCreditsTopUp:{topUp}:{rechargeAt} | Update auto-recharge amount and threshold |
Authenticating Requests
Pass your API key via the Authorization header:
Authorization: Bearer bapi_B2C_xxxxxxxxxxxx
Endpoint
POST https://api.buddypro.ai/v1/chat/completions
Authorization: Bearer bapi_B2C_xxxxxxxxxxxx
Content-Type: application/json
Request
Content Input
Standard OpenAI messages array. BuddyPro extracts the last user message for processing.
Only one
usermessage is allowed. BuddyPro manages conversation history server-side — do not send conversation turns. System and assistant messages are ignored.
Text only (string content):
{
"messages": [
{ "role": "user", "content": "Hello, how are you?" }
]
}
Multimodal (array content):
{
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "Describe this image" },
{ "type": "image_url", "image_url": { "url": "https://example.com/photo.jpg" } }
]
}
]
}
Content Part Types
When using array content inside messages[].content:
Text
{ "type": "text", "text": "What is the weather today?" }
Max 50,000 characters per text part.
Image (image_url)
{ "type": "image_url", "image_url": { "url": "https://example.com/photo.jpg" } }
Supported URL types:
- HTTPS URL — must be publicly accessible
- Data URI —
data:image/png;base64,iVBORw0KGgo...
Max 5 images per request. Max 40 MB per remote download.
Audio Input (input_audio)
{
"type": "input_audio",
"input_audio": {
"data": "<base64>",
"format": "mp3"
}
}
Fields:
| Field | Type | Description |
|---|---|---|
data | string | Base64-encoded audio data or URL |
format | string | Audio format: mp3, wav, ogg, aac, flac |
type | "url" | "base64" | Optional data type hint. Default: base64 |
Audio via URL:
{
"type": "input_audio",
"input_audio": {
"data": "https://example.com/audio.mp3",
"type": "url",
"format": "mp3"
}
}
Audio Output (TTS via Modalities)
To request TTS audio output, use the OpenAI-style modalities and audio fields:
{
"modalities": ["text", "audio"],
"audio": { "format": "mp3" },
"messages": [
{
"role": "user",
"content": [
{ "type": "input_audio", "input_audio": { "data": "<base64>", "format": "mp3" } }
]
}
]
}
- When
modalitiesincludes"audio", TTS is enabled audio.formatdefaults to"mp3"if omitted- TTS only applies when audio input is present in the request
audio.voiceis accepted but ignored — voice is set by the bot owner
Request Fields Reference
| Field | Type | Required | Description |
|---|---|---|---|
messages | array | Yes | OpenAI-format messages. Must contain exactly 1 user message. |
modalities | ["text"] | ["text", "audio"] | — | Output types. Include "audio" to enable TTS |
audio | object | — | Audio config: { "format": "mp3"|"wav" } |
user | string | — | Custom user identifier for an isolated profile. See User Isolation |
x_buddy_saveToHistory | boolean | — | When false, nothing is saved to history, memory, or profile. Default: true. See Stateless Mode |
Prompt-override fields are not available in the Client API.
x_buddy_systemPrompt,x_buddy_systemPromptMode, andx_buddy_rolePromptare not supported for Client API keys — the instance owner's system and role prompts cannot be overridden per request. Sending any of these fields returns a400unsupported_parametererror.
Client Request ID
Provide via the X-Client-Request-Id HTTP header (max 64 characters, alphanumeric + hyphens + underscores):
curl -X POST https://api.buddypro.ai/v1/chat/completions \
-H "Authorization: Bearer bapi_B2C_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-H "X-Client-Request-Id: my-app_req-42" \
-d '{ "messages": [{ "role": "user", "content": "Hello!" }] }'
User Isolation
By default, API requests use your personal BuddyPro profile — all conversations are saved to your history and contribute to your memory.
To create isolated profiles with separate conversation history and memory, use the user field. Each unique user value creates a fully separate profile — useful for different projects, personas, or contexts.
| Mode | How to activate | Behavior |
|---|---|---|
| Personal profile (default) | Omit user | Uses your own profile (history, settings, memory) |
| Isolated profile | Set user to a custom identifier | Creates a separate profile per value — own history, memory, settings |
| Stateless | Set x_buddy_saveToHistory: false | Nothing is persisted. Can be combined with either mode |
Isolated profile example:
{
"user": "work-assistant",
"messages": [
{ "role": "user", "content": "Help me prepare for my 3pm meeting." }
]
}
Validation rules for user:
- Alphanumeric characters, hyphens, underscores, and dots only (
a-z,A-Z,0-9,-,_,.) - Cannot be a purely numeric value
- Max 128 characters, no spaces
Stateless Mode
Set x_buddy_saveToHistory: false to make a request that does not persist anything. In stateless mode:
- Nothing is saved to chat history
- No updates to long-term memory
- No profile updates or preference learning
- The AI still responds normally using existing context
Example — stateless Q&A:
{
"x_buddy_saveToHistory": false,
"messages": [
{ "role": "user", "content": "What is the best way to start a business?" }
]
}
Response
Response Headers
| Header | Description |
|---|---|
x-request-id | Server-generated unique request ID (always present) |
x-client-request-id | Client-supplied request ID echoed back (if provided) |
Content-Type | application/json |
Success — Text Only
{
"id": "chatcmpl-req_abc123def456",
"object": "chat.completion",
"created": 1710964800,
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello! How can I help you today?"
},
"finish_reason": "stop"
}
]
}
Success — Image Output
When BuddyPro generates an image, it appears in message.image:
{
"id": "chatcmpl-req_def456",
"object": "chat.completion",
"created": 1710964800,
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Here's the image you requested:",
"image": {
"id": "image_req_def456_generated_image.png",
"data": "iVBORw0KGgo...",
"media_type": "image/png",
"file_name": "generated_image.png",
"caption": "A sunset over the ocean"
}
},
"finish_reason": "stop"
}
]
}
Success — Audio Output (Music / Meditation)
Audio from music or meditation features is always returned regardless of modalities:
{
"id": "chatcmpl-req_ghi789",
"object": "chat.completion",
"created": 1710964800,
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"audio": {
"id": "audio_req_ghi789_response.ogg",
"data": "<base64>",
"format": "ogg",
"transcript": null,
"media_type": "audio/ogg",
"file_name": "response.ogg"
}
},
"finish_reason": "stop"
}
]
}
Success — Text + TTS Audio
When TTS is enabled via modalities and audio input was sent:
{
"id": "chatcmpl-req_abc123",
"object": "chat.completion",
"created": 1710964800,
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Why don't scientists trust atoms? Because they make up everything!",
"audio": {
"id": "audio_req_abc123_response.mp3",
"data": "<base64>",
"format": "mp3",
"transcript": "Why don't scientists trust atoms? Because they make up everything!",
"media_type": "audio/mpeg",
"file_name": "response.mp3"
}
},
"finish_reason": "stop"
}
]
}
When audio output is present,
transcriptcontains the same text ascontent(the concatenated text response).
Response Fields Reference
Top-level
| Field | Type | Description |
|---|---|---|
id | string | Unique completion ID: chatcmpl-{requestId} |
object | string | Always "chat.completion" |
created | number | Unix timestamp (seconds) of request start |
choices | array | Array with single choice (index 0) |
Note: Request ID and processing time metadata are available via response headers (
X-Request-Id,X-Client-Request-Id) — they are not included in the response body.
choices[0].message
| Field | Type | Description |
|---|---|---|
role | string | Always "assistant" |
content | string | null | Concatenated text response. null if only media |
audio | object | undefined | Audio output (first audio item). See below |
image | object | undefined | Image output (first image item). See below |
message.audio (OpenAiAudioOutput)
| Field | Type | Description |
|---|---|---|
id | string | Audio identifier: audio_{requestId}_{fileName} |
data | string | Base64-encoded audio data |
format | string | Audio format (e.g., mp3, ogg, wav) |
transcript | string | undefined | Text transcript (same as content when TTS) |
media_type | string | MIME type (e.g., audio/mpeg, audio/ogg) |
file_name | string | Suggested filename |
message.image (OpenAiImageOutput — BuddyPro Extension)
| Field | Type | Description |
|---|---|---|
id | string | Image identifier: image_{requestId}_{fileName} |
data | string | Base64-encoded image data |
media_type | string | MIME type (e.g., image/png) |
file_name | string | Suggested filename |
caption | string | Image caption/description |
Error Responses
Error Response Format
All errors use a structured format with an error object:
{
"error": {
"message": "Invalid or inactive API key",
"type": "authentication_error",
"statusCode": 401,
"code": "invalid_api_key",
"param": null
}
}
Important: Always inspect the response body for the
errorfield — do not rely solely on the HTTP status code. In certain conditions, the HTTP status code may be200even when the response body contains an error.
Error Fields
| Field | Type | Description |
|---|---|---|
error.message | string | Human-readable error description |
error.type | string | Error category |
error.statusCode | number | HTTP status code |
error.code | string | null | Machine-readable error code |
error.param | string | null | The request parameter that caused the error |
Error Types
| HTTP Status | type | Description |
|---|---|---|
| 400 | invalid_request_error | Malformed request, missing fields, invalid content |
| 401 | authentication_error | Missing or invalid API key |
| 402 | payment_required | Billing not set up, insufficient credits, or instance billing unavailable (Client API specific — see below) |
| 403 | permission_error | Insufficient permissions |
| 405 | method_not_allowed | Wrong HTTP method |
| 410 | gone | Deprecated endpoint no longer available |
| 429 | rate_limit_error | Rate limit exceeded |
| 500 | server_error | Internal server error |
Common Error Codes
| Code | Meaning |
|---|---|
invalid_json | Request body is not valid JSON |
missing_required_parameter | Required field missing |
missing_api_key | No API key provided in Authorization header |
invalid_api_key | API key not found or inactive |
invalid_value | Field has wrong type or invalid value |
invalid_text_content | Text empty or exceeds 50,000 char limit |
invalid_content_type | Unknown content part type |
invalid_content | Content has no usable items |
invalid_media_data | Media data invalid, download failed, or exceeds size limit |
invalid_media_type | Unsupported MIME type |
invalid_audio_format | Unsupported audio format |
invalid_image_count | Too many images (max 5) |
invalid_parameter | Invalid parameter value (e.g., bad user or x_buddy_saveToHistory) |
unsupported_parameter | A parameter that is not supported for Client API keys was sent (e.g., x_buddy_systemPrompt, x_buddy_systemPromptMode, x_buddy_rolePrompt) |
insufficient_permissions | API key lacks required permissions, or the instance owner has disabled the Client API (see Client API Disabled) |
endpoint_deprecated | API version has been deprecated and is no longer available |
rate_limit_exceeded | More than 30 requests/minute |
Client API-Specific Errors (HTTP 402)
The Client API adds four billing-related error codes. All use type: "payment_required":
| HTTP Status | code | Description |
|---|---|---|
| 402 | billing_not_set_up | No billing configured yet. Run /setupApiCredits:{topUp}:{rechargeAt} in Telegram first. |
| 402 | insufficient_credits | Your credit balance is zero or negative and the auto-recharge failed or is on cooldown — check your payment method. Recharge retries automatically on the next request. |
| 402 | insufficient_credits_recharging | Your credit balance was zero or negative, but this request just triggered a successful auto-recharge payment — the credits arrive within seconds. The response carries a Retry-After: 5 header; simply retry shortly. |
| 402 | owner_billing_unavailable | The instance you're using is temporarily unable to bill for usage (the instance's own billing balance is depleted). This is on the instance owner's side, not yours — retry later. |
Example — billing not set up:
{
"error": {
"message": "Billing not set up. Please set up billing to use the Buddy API. Call /setupApiCredits:{topUpAmount}:{rechargeAt} first.",
"type": "payment_required",
"statusCode": 402,
"code": "billing_not_set_up",
"param": null
}
}
Example — insufficient credits:
{
"error": {
"message": "Insufficient credits. Auto-recharge failed or is in cooldown — please check your payment method and try again later.",
"type": "payment_required",
"statusCode": 402,
"code": "insufficient_credits",
"param": null
}
}
Example — insufficient credits, recharge in flight (response includes Retry-After: 5):
{
"error": {
"message": "Insufficient credits. An automatic recharge was just initiated and payment succeeded — credits will be available shortly. Please retry.",
"type": "payment_required",
"statusCode": 402,
"code": "insufficient_credits_recharging",
"param": null
}
}
Example — instance billing unavailable:
{
"error": {
"message": "Service is temporarily unavailable for billing reasons. Please try again later.",
"type": "payment_required",
"statusCode": 402,
"code": "owner_billing_unavailable",
"param": null
}
}
Client API Disabled (HTTP 403)
The Client API is an owner opt-in and can be disabled by the instance owner at any time. While it is disabled, every request — including with previously issued, valid keys — is rejected with 403 insufficient_permissions. Keys are not revoked; they resume working as soon as the owner re-enables the Client API.
{
"error": {
"message": "The Client API is not enabled for this instance. The instance owner must enable it first.",
"type": "permission_error",
"statusCode": 403,
"code": "insufficient_permissions",
"param": null
}
}
Rate Limits
- 30 requests per minute per API key
Quick Start
Step 1 — Generate a key
/generateClientApiKey:my-app
Step 2 — Set up billing
/setupApiCredits:100:20
Complete the Stripe checkout. Your account now has $100 in credits and will auto-recharge when your balance drops below $20.
Step 3 — Make your first request
curl -X POST https://api.buddypro.ai/v1/chat/completions \
-H "Authorization: Bearer bapi_B2C_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{ "role": "user", "content": "Hello! What do you know about me?" }
]
}'
curl — Stateless Mode
curl -X POST https://api.buddypro.ai/v1/chat/completions \
-H "Authorization: Bearer bapi_B2C_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"x_buddy_saveToHistory": false,
"messages": [
{ "role": "user", "content": "What is the best way to start a profitable business?" }
]
}'
curl — Isolated Profile
curl -X POST https://api.buddypro.ai/v1/chat/completions \
-H "Authorization: Bearer bapi_B2C_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"user": "work-assistant",
"messages": [
{ "role": "user", "content": "Help me write a professional email." }
]
}'
curl — Multimodal (Image + Text)
curl -X POST https://api.buddypro.ai/v1/chat/completions \
-H "Authorization: Bearer bapi_B2C_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "What is in this image?" },
{ "type": "image_url", "image_url": { "url": "https://example.com/photo.jpg" } }
]
}
]
}'
curl — Audio Input with TTS Output
curl -X POST https://api.buddypro.ai/v1/chat/completions \
-H "Authorization: Bearer bapi_B2C_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"modalities": ["text", "audio"],
"audio": { "format": "mp3" },
"messages": [
{
"role": "user",
"content": [
{ "type": "input_audio", "input_audio": { "data": "<base64>", "format": "mp3" } }
]
}
]
}'
Limitations
| Limitation | Detail |
|---|---|
| No streaming | Streaming is not supported yet |
| Single user message | Only 1 user message in messages array (BuddyPro manages history) |
| No model selection | model field is accepted but ignored — BuddyPro has its own model implementation |
| No usage stats | usage object is not included in responses |
| First media wins | Only the first audio and first image in the response are surfaced per choice |
| Voice not controllable | audio.voice is accepted but ignored — voice is set by the bot owner |
Media Limits
- Max 5 images per request
- Max 40 MB per media download (URL-fetched media)
- Max 50,000 characters per text content part
Important Notes
- Do not send conversation history — send only the current user message. BuddyPro stores and manages conversation context internally.
- Credit billing is required before your first request. Set it up once with
/setupApiCredits:{topUp}:{rechargeAt}. It requires an active subscription to the instance, and the instance must accept API payments. - Billing is handled by your instance. Credit checkouts and the per-request price are set by the BuddyPro instance you use (its owner), not by BuddyPro directly.
- Credits auto-recharge when your balance falls below the configured threshold, using your saved Stripe payment method.
- Your data is yours. The instance owner cannot read your conversation history or switch to your profile or your isolated profiles through the bot interface.
- SSRF protection: URLs pointing to private/internal network addresses are blocked.