This API facilitates the management of agents, chats, messages, workflows, CMS content, files, API keys, and webhooks within the Brightsy AI ecosystem, providing endpoints for creating, retrieving, updating, and deleting resources.
Base URL
https://brightsy.ai/api/v1beta
Authentication
The API uses bearer token authentication to secure its endpoints.
Type: HTTP Bearer Authentication Format: Include your API key in the Authorization header
Authorization: Bearer YOUR_API_KEY
Managing API Keys
API keys can be created and managed in the Automations section:
All API endpoints have a required timeout of 300 seconds. Clients should be configured to wait at least 300 seconds for a response before timing out the request. This is especially important for endpoints that perform complex processing or generate completions.
BrightsyClient SDK
The BrightsyClient provides a convenient JavaScript/TypeScript SDK for interacting with the Brightsy API. It handles authentication, request formatting, and provides type-safe methods for all API operations.
Installation
npm install @brightsy/client
Initialization
The SDK supports multiple authentication methods:
API Key Authentication (Default)
import { BrightsyClient } from '@brightsy/client';
const client = new BrightsyClient({
account_id: 'your-account-id',
api_key: 'your-api-key'
});
OAuth 2.1 Authentication (User-Authorized)
import { BrightsyClient, OAuthClient } from '@brightsy/client';
// For external agents or applications requiring user authorization
const client = new BrightsyClient({
authMode: 'oauth',
oauthAccessToken: 'user-access-token',
oauthRefreshToken: 'user-refresh-token',
oauthClientId: 'your-oauth-client-id',
oauthClientSecret: 'your-oauth-client-secret', // Optional, for confidential clients
oauthTokenExpiry: Date.now() + 3600 * 1000, // Optional, for automatic refresh
// Optional: Get notified when tokens are refreshed
onTokenRefresh: (accessToken, refreshToken) => {
console.log('Token refreshed automatically');
// Save new tokens to storage
saveTokens({ accessToken, refreshToken });
}
});
OAuth Features:
✅ Automatic token refresh before expiry (no interruption)
✅ PKCE support (S256) for security
✅ Dynamic client registration
✅ Token revocation on logout
✅ Works in browser and Node.js
Complete OAuth Flow Example:
import { OAuthClient } from '@brightsy/client';
// 1. Register OAuth client (one-time)
const oauthClient = new OAuthClient({
clientId: '', // Will be provided after registration
redirectUri: 'http://localhost:3000/callback'
});
const credentials = await oauthClient.register({
client_name: 'My Application',
redirect_uris: ['http://localhost:3000/callback'],
scopes: ['brightsy:api']
});
// 2. Start authorization flow
const { url, state, codeVerifier } = await oauthClient.buildAuthorizationUrl();
// Store state and codeVerifier, redirect user to url
// 3. Handle callback and exchange code for tokens
const tokens = await oauthClient.exchangeCode(authCode, codeVerifier);
// 4. Initialize client with tokens (auto-refresh enabled)
const client = new BrightsyClient({
authMode: 'oauth',
oauthAccessToken: tokens.access_token,
oauthRefreshToken: tokens.refresh_token,
oauthClientId: credentials.client_id,
oauthClientSecret: credentials.client_secret
});
// Use the client normally - tokens refresh automatically!
const agents = await client.agents.list();
The SDK provides streaming utilities for processing streaming responses:
import { processStream, formatMessages, generateUUID } from '@brightsy/client';
import type { ChatMessage } from '@brightsy/client';
// Create a streaming request
const stream = await client.chat.completions('agent-id').create({
messages: [
{
role: 'user',
content: [{ text: 'Hello' }]
}
],
stream: true
});
// Keep track of prior messages (messages that existed before streaming started)
const priorMessages = [...existingMessages]; // Your existing message history
// Process stream - accumulates NEW messages from the stream
await processStream(stream, {
onMessagesUpdate: (streamedMessages: ChatMessage[]) => {
// Called whenever new content arrives during streaming
// streamedMessages contains only NEW messages from this stream (in Brightsy format)
// Combine with priorMessages for complete message history
const allMessages = [...priorMessages, ...streamedMessages];
// Convert to Vercel format with parts for rendering
const formattedMessages = formatMessages(allMessages);
// Update UI state
setMessages(formattedMessages);
},
onComplete: (streamedMessages: ChatMessage[]) => {
// Called when stream completes
// streamedMessages contains all NEW messages from this stream
const allMessages = [...priorMessages, ...streamedMessages];
const formattedMessages = formatMessages(allMessages);
setMessages(formattedMessages);
setStreaming(false);
},
onError: (error: Error) => {
// Called if an error occurs during streaming
console.error('Stream error:', error);
setStreaming(false);
}
});
Key Functions:
processStream(stream, callbacks) - Processes streaming responses and calls handlers with NEW messages from the stream in Brightsy format
formatMessages(messages) - Converts Brightsy format messages to Vercel AI SDK format with parts for rendering
generateUUID() - Generates PostgreSQL-compliant UUIDs for message IDs
Important: processStream doesn't return messages - it calls your handlers (onMessagesUpdate, onComplete) with the NEW messages from the stream. Always combine with your prior message history ([...priorMessages, ...streamedMessages]) in the handlers before formatting for display.
Using formatMessages to Render Messages
The formatMessages function converts Brightsy format messages (with content arrays) to Vercel format messages (with parts arrays) for rendering:
import { formatMessages } from '@brightsy/client';
// Brightsy format messages (from API or processStream)
const brightsyMessages = [
{
id: 'msg-1',
role: 'user',
content: [{ text: 'Hello!' }],
createdAt: new Date().toISOString()
},
{
id: 'msg-2',
role: 'assistant',
content: [{ text: 'Hi! How can I help you?' }],
tool_calls: [
{
id: 'call-1',
type: 'function',
function: {
name: 'search',
arguments: '{"query":"example"}'
}
}
],
createdAt: new Date().toISOString()
},
{
id: 'msg-3',
role: 'tool',
tool_call_id: 'call-1',
name: 'search',
content: '{"results":["result1","result2"]}',
createdAt: new Date().toISOString()
}
];
// Convert to Vercel format with parts
const formattedMessages = formatMessages(brightsyMessages);
// Render the formatted messages
formattedMessages.forEach(message => {
console.log(`[${message.role}]:`, message.id);
message.parts.forEach(part => {
if (part.type === 'text') {
console.log(' Text:', part.text);
} else if (part.type === 'tool-invocation') {
console.log(' Tool:', part.toolInvocation.toolName);
console.log(' Args:', part.toolInvocation.args);
if (part.toolInvocation.result) {
console.log(' Result:', part.toolInvocation.result);
}
} else if (part.type === 'reasoning') {
console.log(' Reasoning:', part.reasoning);
} else if (part.type === 'source') {
console.log(' Source:', part.source.url, part.source.title);
} else if (part.type === 'image_url') {
console.log(' Image:', part.image_url.url);
} else if (part.type === 'file') {
console.log(' File:', part.file);
}
});
});
Output:
[user]: msg-1
Text: Hello!
[assistant]: msg-2
Text: Hi! How can I help you?
Tool: search
Args: { query: "example" }
Result: { results: ["result1", "result2"] }
Key Points:
formatMessages merges consecutive messages of the same role and tool/assistant messages
Tool calls and their results are combined into single tool-invocation parts with state: 'result'
The function handles text, tool invocations, reasoning, sources, images, and file attachments
Usage information is accumulated when messages are merged
API Structure
The BrightsyClient uses a consistent property-based API pattern for all resource access:
// Analyze an image
const analysis = await client.images.analyze({
imageUrl: 'https://example.com/image.jpg',
prompt: 'Describe this image'
});
// Generate an image
const image = await client.images.generate({
prompt: 'A sunset over mountains'
});
Scenarios
// List scenarios
const scenarios = await client.scenarios.list();
// Get a scenario
const scenario = await client.scenarios.get('scenario-id');
// Execute a scenario (uses method since it requires scenarioId)
const result = await client.scenario('scenario-id').execute({
input: 'Process this data'
});
Sites
// List sites
const sites = await client.sites.list();
// Get a site
const site = await client.sites.get('site-id');
// List pages for a site
const pages = await client.sites.pages('site-id').list();
// Get a page
const page = await client.sites.pages('site-id').get('page-id');
Agents are AI-powered assistants that can be accessed through the Agents section. They support chat completions, multi-turn conversations, and file attachments.
401 Unauthorized - User does not have access to agent
404 Not Found - Agent not found
Error Responses:
{
"error": {
"message": "request_id is required"
}
}
Note: If a request has already been cancelled, the endpoint will return a 200 status with the message "Request already cancelled".
A2A Protocol (Agent-to-Agent)
The A2A protocol enables Brightsy agents to communicate with other agents, both within Brightsy and across external platforms. For complete documentation, see the A2A Protocol Guide.
Agent Card Discovery
Get Agent Card
GET /agent/{agentId}/a2a/card
Returns the agent's A2A card describing its capabilities, skills, and endpoint.
Path Parameters:
agentId - The unique identifier of the agent (string, required)
The Automations section provides tools for workflow automation, including scenarios, API keys, webhooks, and schedules.
Scenarios
Scenarios are automated workflows that can be triggered by webhooks or scheduled executions.
Execute Scenario
POST /scenario/{scenarioId}
Executes a scenario.
Path Parameters:
scenarioId - The unique identifier of the scenario (string, required)
Request Headers:
[Signature Header Name] - The header name is defined in the scenario configuration as signature_name. For example, if signature_name is set to "X-Scenario-Signature", you would include that header in your request. While you could technically use the raw signature_secret as the header value, it's best practice to generate a signature using the signature_secret and the payload as described below.
Signature Generation:
The signature must be generated using HMAC SHA-256 with the following process:
Convert the entire request body to a JSON string (payload)
Create an HMAC SHA-256 hash using the scenario's signature_secret as the key
Update the hash with the payload string directly
Get the hex digest of the hash, which becomes the signature value
Security Best Practice: Using HMAC signatures as shown above is the recommended best practice for API security. The signature validates the authenticity of requests and ensures data integrity, protecting against tampering. Store your signature_secret securely and never expose it in client-side code.
Request Body:
Content Type: application/json
Schema: An object containing an input property of type string
Responses:
200 OK - Scenario executed successfully
401 Unauthorized - When the signature header is missing or invalid
204 No Content - For CORS preflight requests
API Keys
API keys are managed in Automations → API Keys. They can be scoped to specific agents, scenarios, or CMS access.
Webhooks
Webhooks are managed in Automations → Webhooks. They allow external systems to trigger scenarios or receive notifications.
Schedules
Scheduled executions can be configured in Automations → Schedules to run scenarios at specific times or intervals.
Object Models
Message Object
Represents a message with various content types.
Properties:
id - Unique identifier for the message (string)
content - Array of content items, each being one of:
Text: Object with type as "text" and text as a string
Image URL: Object with type as "image_url" and image_url containing url and optional detail
Example:
{
"id": "msg_123456789",
"content": [
{
"type": "text",
"text": "Hello, this is a text message"
},
{
"type": "image_url",
"image_url": {
"url": "https://example.com/image.jpg",
"detail": "high"
}
}
]
}
401 Unauthorized - User does not have access to agent
404 Not Found - Agent not found
Error Responses:
{
"error": {
"message": "request_id is required"
}
}
Note: If a request has already been cancelled, the endpoint will return a 200 status with the message "Request already cancelled".
Scenarios
Execute Scenario
POST /scenario/{scenarioId}
Executes a scenario.
Path Parameters:
scenarioId - The unique identifier of the scenario (string, required)
Request Headers:
[Signature Header Name] - The header name is defined in the scenario configuration as signature_name. For example, if signature_name is set to "X-Scenario-Signature", you would include that header in your request. While you could technically use the raw signature_secret as the header value, it's best practice to generate a signature using the signature_secret and the payload as described below.
Signature Generation:
The signature must be generated using HMAC SHA-256 with the following process:
Convert the entire request body to a JSON string (payload)
Create an HMAC SHA-256 hash using the scenario's signature_secret as the key
Update the hash with the payload string directly
Get the hex digest of the hash, which becomes the signature value
Security Best Practice: Using HMAC signatures as shown above is the recommended best practice for API security. The signature validates the authenticity of requests and ensures data integrity, protecting against tampering. Store your signature_secret securely and never expose it in client-side code.
Request Body:
Content Type: application/json
Schema: An object containing an input property of type string
Responses:
200 OK - Scenario executed successfully
401 Unauthorized - When the signature header is missing or invalid
204 No Content - For CORS preflight requests
Object Models
Message Object
Represents a message with various content types.
Properties:
id - Unique identifier for the message (string)
content - Array of content items, each being one of:
Text: Object with type as "text" and text as a string
Image URL: Object with type as "image_url" and image_url containing url and optional detail
Example:
{
"id": "msg_123456789",
"content": [
{
"type": "text",
"text": "Hello, this is a text message"
},
{
"type": "image_url",
"image_url": {
"url": "https://example.com/image.jpg",
"detail": "high"
}
}
]
}