# Manage Agents
Source: https://docs.sundaypyjamas.com/api-reference/agents/agents
List, create, retrieve, update, and delete managed agent configurations
## List Agents
```http theme={null}
GET /api/v1/managed-agents/agents
```
Returns every agent in your workspace.
Always `"agent"``"active"` or `"draft"`ISO 8601 timestampISO 8601 timestamp
```bash cURL theme={null}
curl https://suite.sundaypyjamas.com/api/v1/managed-agents/agents \
-H "Authorization: Bearer spj_ai_your_api_key_here"
```
```json Response theme={null}
{
"agents": [
{
"id": "3fae1c2e-...",
"name": "Research Assistant",
"description": "Summarizes market research documents",
"businessUseCase": "Managed agent",
"appType": "agent",
"isPublic": false,
"status": "active",
"createdAt": "2026-06-01T12:00:00Z",
"updatedAt": "2026-06-14T09:30:00Z"
}
]
}
```
***
## Create Agent
```http theme={null}
POST /api/v1/managed-agents/agents
```
Display name for the agent.
Free-text description of what this agent is for.
Longer description shown in agent listings.
System prompt that defines the agent's behavior and persona.
Partial [agent config](#agent-config-reference) object. Deep-merged with platform defaults — you only need to specify the fields you want to override.
ID of the LLM component/model to bind to this agent.
```bash cURL theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/managed-agents/agents \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Research Assistant",
"description": "Summarizes market research documents",
"prompt": "You are a meticulous research analyst.",
"config": {
"capabilities": {
"tools": { "web_search": true }
}
}
}'
```
```json 201 Created theme={null}
{
"agent": {
"id": "3fae1c2e-...",
"name": "Research Assistant",
"appType": "agent",
"createdAt": "2026-07-08T10:00:00Z"
}
}
```
Returns `400` with `"name is required"` if `name` is missing or empty.
***
## Get Agent
```http theme={null}
GET /api/v1/managed-agents/agents/{agentId}
```
`"assist"` or `"autonomous"`See [Agent Config Reference](#agent-config-reference)
```bash cURL theme={null}
curl https://suite.sundaypyjamas.com/api/v1/managed-agents/agents/3fae1c2e-... \
-H "Authorization: Bearer spj_ai_your_api_key_here"
```
```json 404 Not Found theme={null}
{ "error": "Agent not found" }
```
***
## Update Agent
```http theme={null}
PATCH /api/v1/managed-agents/agents/{agentId}
```
All fields are optional — only the fields you send are changed.
Partial config, deep-merged with the agent's existing config.
Replaces the agent's full tool binding list.
```bash cURL theme={null}
curl -X PATCH https://suite.sundaypyjamas.com/api/v1/managed-agents/agents/3fae1c2e-... \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{ "config": { "capabilities": { "tools": { "web_fetch": true } } } }'
```
***
## Delete Agent
```http theme={null}
DELETE /api/v1/managed-agents/agents/{agentId}
```
```json Response theme={null}
{ "deleted": true }
```
## Agent Config Reference
The `config` object controls runtime behavior. All fields are optional and merge with `DEFAULT_MANAGED_AGENT_CONFIG`.
| Field | Type | Description |
| ----------------------------------- | ---------------------------------------- | -------------------------------------------------------------------------------- |
| `platformVersion` | number | Config schema version (currently `2`) |
| `kind` | string | Always `"agent"` |
| `runtimeMode` | `"assist"` \| `"autonomous"` | How the agent is driven |
| `capabilities.memory.enabled` | boolean | Whether the agent retains memory across turns |
| `capabilities.memory.scope` | `"agent"` \| `"workspace"` | Memory isolation scope |
| `capabilities.deliverables.enabled` | boolean | Required `true` to use the [Runs API](/api-reference/agents/runs) |
| `capabilities.tools.web_search` | boolean | Enable web search tool |
| `capabilities.tools.web_fetch` | boolean | Enable URL fetching tool |
| `capabilities.tools.web_provider` | `"perplexity"` \| `"tavily"` \| `"both"` | Search provider |
| `capabilities.mcp_servers` | array | Connected MCP servers |
| `compute.maxIterations` | number | Max reasoning/tool-call loops per run |
| `compute.maxWallClockMs` | number | Hard timeout per run |
| `toolAllowlist` | string\[] | Restricts which tools the agent may call |
| `customTools` | object\[] | Custom tool definitions with `name`, `description`, `inputSchema`, `callbackUrl` |
# Agents API
Source: https://docs.sundaypyjamas.com/api-reference/agents/introduction
Create and run autonomous or assist-mode AI agents, manage their sessions, and stream results in real time
## Overview
The Agents API lets you provision **managed agents** — configurable AI workers with their own tools, memory, and runtime behavior — and drive them programmatically. Agents run in one of two modes:
Turn-based, conversational. You send a message to a session and stream back the agent's reply — ideal for chat-style copilots embedded in your product.
Task-based. You submit a task ("run"), the agent works independently (using tools, memory, and multi-step reasoning) and produces deliverables/artifacts you poll or subscribe to.
## Core Concepts
The configuration object: name, system prompt, model, tool allowlist, memory settings, and compute limits (max iterations, wall-clock timeout). Created once, reused across many sessions and runs.
A conversation/work context scoped to one agent. Sessions hold message history and metadata (e.g. which external platform or end user they belong to). Assist-mode messages and autonomous-mode runs both happen inside a session.
A single autonomous task execution inside a session — e.g. "research X and produce a summary document." Runs progress through `pending → running → completed/failed` and emit events and artifacts as they work.
When an agent needs to call a tool it doesn't have native access to, it emits a pending tool call. Your integration executes the tool and reports the result back via the [tool-results endpoint](/api-reference/agents/messaging#submit-tool-results).
## Base URL
```
https://suite.sundaypyjamas.com/api/v1/managed-agents
```
## Authentication
All requests require a valid API key in the Authorization header:
```http theme={null}
Authorization: Bearer spj_ai_your_api_key_here
```
Learn more about [API key generation and management](/authentication).
## Resources
Create, list, update, and delete agent configurations.
Submit autonomous tasks and track their progress.
Manage the conversational/work context an agent operates in.
Stream assist-mode replies, read session timelines, submit tool results, and cancel runs.
# Messaging & Events
Source: https://docs.sundaypyjamas.com/api-reference/agents/messaging
Stream assist-mode replies, read the session timeline, respond to tool calls, and cancel runs
## Send Message (Assist Mode)
```http theme={null}
POST /api/v1/managed-agents/sessions/{sessionId}/messages
```
Sends a user message into the session and streams the agent's reply back as **Server-Sent Events**.
The user message.
Override the model configured on the agent for this turn only.
### Response
```http theme={null}
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
```
The stream carries a sequence of JSON events covering turn-run state changes, incremental text/token chunks, and tool calls made by the agent as it works.
```javascript Streaming client theme={null}
const response = await fetch(
`https://suite.sundaypyjamas.com/api/v1/managed-agents/sessions/${sessionId}/messages`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ message: 'What did you find in the report?' }),
}
);
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
console.log(decoder.decode(value));
}
```
Returns `400` with `"message is required"` if `message` is missing. Returns `429` if the workspace has hit its managed-agent rate limit.
***
## Get Session Timeline
```http theme={null}
GET /api/v1/managed-agents/sessions/{sessionId}/events
```
Returns the full ordered history of a session — messages and turn runs interleaved, sorted ascending by `created_at`.
`"message"``user` · `assistant` · `tool``"turn_run"`
***
## Submit Tool Results
```http theme={null}
POST /api/v1/managed-agents/sessions/{sessionId}/tool-results
```
When an agent needs to call a tool your integration owns, it pauses and emits a pending tool call in the event stream. Execute the tool on your side, then report the result back here so the agent can continue.
The ID of the pending tool call, taken from the event stream.
The tool's return value. Provide this on success.
Error message if the tool failed. Provide either `result` or `error`, not both.
```bash cURL theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/managed-agents/sessions/sess_44bc.../tool-results \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{ "toolCallId": "tc_991a...", "result": { "status": "ok", "rows": 12 } }'
```
```json Response theme={null}
{
"accepted": true,
"sessionId": "sess_44bc...",
"toolCallId": "tc_991a...",
"status": "completed"
}
```
Returns `403` with `"toolCallId does not belong to session"` if the tool call belongs to a different session, and `404` with `"Unknown toolCallId"` if it doesn't exist.
***
## Cancel Run
```http theme={null}
POST /api/v1/managed-agents/sessions/{sessionId}/cancel
```
The turn run (or [autonomous run](/api-reference/agents/runs)) to cancel.
```json Response theme={null}
{
"runId": "run_8a21...",
"status": "cancelled"
}
```
Returns `400` with `"Run is {current_status}"` if the run isn't currently running (e.g. already completed).
# Runs
Source: https://docs.sundaypyjamas.com/api-reference/agents/runs
Submit autonomous tasks to an agent and track their progress
Runs require `capabilities.deliverables.enabled: true` in the agent's [config](/api-reference/agents/agents#agent-config-reference). Autonomous mode only — for turn-based chat, use [Messaging](/api-reference/agents/messaging) instead.
## List Runs
```http theme={null}
GET /api/v1/managed-agents/agents/{agentId}/runs
```
Number of runs to return (1–100).
`pending` · `running` · `completed` · `failed`The task description submitted for this run
***
## Create Run
```http theme={null}
POST /api/v1/managed-agents/agents/{agentId}/runs
```
The task you want the agent to complete, in natural language.
Run inside an existing session. If omitted, a new session is created.
Originating integration platform. **Do not** set this for known integrations (e.g. `slack`, `pumble`) — those must trigger runs via their platform webhook, not this endpoint directly.
Thread/channel container IDIdempotency/dedup key
```bash cURL theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/managed-agents/agents/3fae1c2e-.../runs \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{ "task": "Summarize the attached Q2 sales report and list top 3 risks." }'
```
```json 201 Created theme={null}
{
"runId": "run_8a21...",
"sessionId": "sess_44bc...",
"created_at": "2026-07-08T10:05:00Z"
}
```
Returns `400` with `"Deliverables are disabled for this agent"` if the agent's config doesn't enable deliverables. Returns `400` with `"Integration mentions must use the platform webhook"` if `metadata.source` is a recognized integration.
***
## Get Run
```http theme={null}
GET /api/v1/managed-agents/agents/{agentId}/runs/{runId}
```
Run object — see [List Runs](#list-runs)
```bash cURL theme={null}
curl https://suite.sundaypyjamas.com/api/v1/managed-agents/agents/3fae1c2e-.../runs/run_8a21... \
-H "Authorization: Bearer spj_ai_your_api_key_here"
```
## Polling vs. Streaming
For long-running tasks, poll `GET .../runs/{runId}` every few seconds until `status` is `completed` or `failed`, then read `artifacts`. To cancel a run in progress, see [Cancel Run](/api-reference/agents/messaging#cancel-run).
# Sessions
Source: https://docs.sundaypyjamas.com/api-reference/agents/sessions
Manage the conversational or work context an agent operates in
A session groups messages, runs, and memory for one continuous interaction with an agent — one session per end user, chat thread, or integration conversation.
## List Sessions
```http theme={null}
GET /api/v1/managed-agents/agents/{agentId}/sessions
```
1–100`pumble` · `public` · `api` · `manual` · ...`chat` or `deliverable_job`Total session count, for pagination
***
## Create Session
```http theme={null}
POST /api/v1/managed-agents/agents/{agentId}/sessions
```
Arbitrary custom metadata to attach to the session.Identifier for the end user this session belongs to, if applicable.
```bash cURL theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/managed-agents/agents/3fae1c2e-.../sessions \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{ "end_user_id": "user_123" }'
```
```json 201 Created theme={null}
{
"session": {
"id": "sess_44bc...",
"mount": null,
"created_at": "2026-07-08T10:00:00Z"
}
}
```
Session creation is rate-limited per workspace. A `429` is returned if you exceed the limit — see [Rate Limits](/rate-limits).
***
## Get Session
```http theme={null}
GET /api/v1/managed-agents/sessions/{sessionId}
```
```json Response theme={null}
{
"session": {
"id": "sess_44bc...",
"title": "Q2 report review",
"mount": null,
"app_id": "3fae1c2e-...",
"last_message_at": "2026-07-08T10:12:00Z",
"created_at": "2026-07-08T10:00:00Z"
}
}
```
***
## Delete Session
```http theme={null}
DELETE /api/v1/managed-agents/sessions/{sessionId}
```
```json Response theme={null}
{ "deleted": true }
```
# Chat
Source: https://docs.sundaypyjamas.com/api-reference/apps/chat
Send a message to an app and receive a reply, non-streaming or streamed
## Send Message
```http theme={null}
POST /api/v1/apps/{appId}/chat/message
```
User message, 1–2000 characters.
Actor role sending the message (non-empty string, e.g. `"user"`).
Existing thread to continue. If omitted, a new [thread](/api-reference/apps/memory) is created.
Session identifier for grouping threads.
App-specific lesson context, passed through to prompt construction.App-specific session context, passed through to prompt construction.Free-form page/document context.Prior messages to seed context, if not relying on `threadId`.
### Response
The assistant's reply`"assistant"``{ input, output, total }`Fraction of context window used (0–1)
```bash cURL theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/apps/app_123/chat/message \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{ "message": "What'"'"'s our refund policy?", "role": "user" }'
```
```json Response theme={null}
{
"threadId": "thr_a1b2...",
"turnId": "turn_c3d4...",
"content": "Refunds are processed within 5 business days of the request.",
"role": "assistant",
"metadata": {
"model": "gpt-4o-mini",
"tokensUsed": { "input": 214, "output": 18, "total": 232 },
"latencyMs": 640
},
"contextWindow": { "utilization": 0.04 }
}
```
***
## Send Message (Streaming)
```http theme={null}
POST /api/v1/apps/{appId}/chat/message/stream
```
Same request body as [Send Message](#send-message), plus:
### Response
`Content-Type: text/event-stream`. Each `data:` event is one of:
```json Token chunk theme={null}
{ "type": "token", "content": "Refunds " }
```
```json Done (final event) theme={null}
{
"type": "done",
"threadId": "thr_a1b2...",
"turnId": "turn_c3d4...",
"contextWindow": { "utilization": 0.04 },
"metadata": {
"model": "gpt-4o-mini",
"tokensUsed": { "input": 214, "output": 18, "total": 232 },
"latencyMs": 640
}
}
```
```json Error theme={null}
{ "type": "error", "code": "MODEL_ERROR", "message": "..." }
```
```javascript Streaming client theme={null}
const res = await fetch(`https://suite.sundaypyjamas.com/api/v1/apps/${appId}/chat/message/stream`, {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ message: "What's our refund policy?", role: 'user' }),
});
const reader = res.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
for (const line of decoder.decode(value).split('\n')) {
if (line.startsWith('data: ')) {
const event = JSON.parse(line.slice(6));
if (event.type === 'token') process.stdout.write(event.content);
}
}
}
```
## Errors
| Status | Error | Cause |
| ------ | ----------------------------------------- | ---------------------------------------- |
| 400 | `message is required` | Missing/empty `message` |
| 400 | `message must be 2000 characters or less` | Message too long |
| 400 | `role is required` | Missing `role` |
| 401 | `Unauthorized or app not found` | Invalid key or app not in your workspace |
| 402 | `Insufficient credits` | Workspace balance too low |
| 502 | `AI model error: {details}` | Upstream model failure |
| 503 | `Model configuration error` | App has no valid model configured |
# Apps API
Source: https://docs.sundaypyjamas.com/api-reference/apps/introduction
Drive a deployed AI app: send chat messages, manage conversation memory, and track usage
## Overview
An **app** is a deployed, configured AI application in your workspace (a chat assistant, a widget, an embedded copilot). The Apps API lets you talk to it programmatically and manage its conversation state — independent of the Agents API, which is for provisioning and running autonomous [managed agents](/api-reference/agents/introduction).
## Base URL
```
https://suite.sundaypyjamas.com/api/v1/apps/{appId}
```
## Authentication
```http theme={null}
Authorization: Bearer spj_ai_your_api_key_here
```
Widget tokens are also accepted as a fallback, scoped to a specific app — useful for calling these endpoints directly from a browser-embedded widget.
## Resources
Send a message and get a reply, non-streaming or streamed via SSE.
Manage threads and messages — an app's persistent conversation history.
Retrieve usage statistics for an app over a time window.
# Memory
Source: https://docs.sundaypyjamas.com/api-reference/apps/memory
Manage an app's conversation threads and messages
Threads are an app's persistent conversation history — created automatically by [Chat](/api-reference/apps/chat) or managed directly via these endpoints.
## List Threads
```http theme={null}
GET /api/v1/apps/{appId}/memory/threads
```
Capped at 100.Filter by `active`, `archived`, or `closed`.
```json Response theme={null}
{
"data": [
{
"id": "thr_a1b2...",
"app_id": "app_123",
"workspace_id": "ws_...",
"session_id": null,
"title": "New Thread",
"status": "active",
"message_count": 4,
"metadata": {},
"created_at": "2026-07-08T09:00:00Z",
"updated_at": "2026-07-08T09:12:00Z"
}
],
"meta": { "limit": 20, "offset": 0, "total": 1 }
}
```
***
## Create Thread
```http theme={null}
POST /api/v1/apps/{appId}/memory/threads
```
***
## Get Thread
```http theme={null}
GET /api/v1/apps/{appId}/memory/threads/{threadId}
```
***
## Update Thread
```http theme={null}
PATCH /api/v1/apps/{appId}/memory/threads/{threadId}
```
`active`, `archived`, or `closed`.
***
## Delete (Archive) Thread
```http theme={null}
DELETE /api/v1/apps/{appId}/memory/threads/{threadId}
```
Marks the thread `archived` rather than hard-deleting it.
```json Response theme={null}
{ "success": true, "threadId": "thr_a1b2..." }
```
***
## List Thread Messages
```http theme={null}
GET /api/v1/apps/{appId}/memory/threads/{threadId}/messages
```
Capped at 200.
```json Response theme={null}
{
"data": [
{
"id": "msg_1",
"workspace_id": "ws_...",
"app_id": "app_123",
"thread_id": "thr_a1b2...",
"role": "user",
"content": "What's our refund policy?",
"sequence_number": 1,
"request_id": "req_1",
"turn_id": "turn_1",
"provider_status": "success",
"latency_ms": 640,
"input_tokens": 214,
"output_tokens": 18,
"cost_usd": 0.0004,
"rag_sources": [],
"metadata": {},
"created_at": "2026-07-08T09:00:00Z"
}
],
"meta": { "limit": 50, "offset": 0, "total": 1 }
}
```
***
## Batch Save Messages
```http theme={null}
POST /api/v1/apps/{appId}/memory/save-messages
```
Writes messages directly into a thread's history without triggering a model call — useful for importing conversation history from another system.
1–100 items.
`"user"` or `"assistant"`.Non-empty.
```bash cURL theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/apps/app_123/memory/save-messages \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"threadId": "thr_a1b2...",
"messages": [
{ "role": "user", "content": "Hi there" },
{ "role": "assistant", "content": "Hello! How can I help?" }
]
}'
```
```json 201 Created theme={null}
{ "saved": 2, "threadId": "thr_a1b2..." }
```
***
## Memory Status
```http theme={null}
GET /api/v1/apps/{appId}/memory/status
```
```json Response theme={null}
{
"appId": "app_123",
"threadCount": 42,
"activeThreadCount": 9,
"messageCount": 631
}
```
## Errors
| Status | Error | Applies to |
| ------ | -------------------------------------------------------------------------------------------------- | ----------------------- |
| 400 | `Invalid status. Must be one of: active, archived, closed` | Update Thread |
| 400 | `threadId is required` / `messages must be a non-empty array` / `Maximum 100 messages per request` | Save Messages |
| 401 | `Unauthorized or app not found` | All |
| 404 | `Thread not found` | Thread-scoped endpoints |
# Usage
Source: https://docs.sundaypyjamas.com/api-reference/apps/usage
Retrieve usage statistics for an app
```http theme={null}
GET /api/v1/apps/{appId}/usage
```
Days of history to include, capped at 90.
```bash cURL theme={null}
curl "https://suite.sundaypyjamas.com/api/v1/apps/app_123/usage?days=7" \
-H "Authorization: Bearer spj_ai_your_api_key_here"
```
Response shape mirrors the workspace-level usage data shown in [Rate Limits](/rate-limits) — token counts and cost, broken out per day for the requested window.
## Errors
| Status | Error |
| ------ | ------------------------------- |
| 401 | `Unauthorized or app not found` |
# POST /artifacts/generate
Source: https://docs.sundaypyjamas.com/api-reference/artifacts/generate
POST https://suite.sundaypyjamas.com/api/v1/artifacts/generate
Generate a structured insight report from aggregated data
## Request Body
One of `"organization"`, `"network"`, or `"item"`.
Max 256 chars.User-friendly label for the scope. Max 500 chars.Total records the aggregates were computed from.Key metrics as `string | number | boolean | null` values.
Up to 20 dimensional breakdowns. Each has `dimension: string` and `rows` (up to 100 items of `{ key, count?, value? }`).
Up to 60 sample records. Each item: `{ id?: string, fields: object }` — `fields` supports up to 20 keys, each value truncated server-side to \~800 characters.
Custom instructions for this scope/period. Max 4000 chars.e.g. `"en-CA"`. Max 32 chars.e.g. `"ministry_briefing"` — influences tone and framing. Max 64 chars.
Known values: `"insight-narrative-v1"`, `"phabc-data-insights-v1"`. Returns `400` if unrecognized.
UUID of an app in your workspace. Returns `404` if it doesn't belong to your workspace.
Max 32 chars, ISO 8601 recommended.Max 32 chars, ISO 8601 recommended.External reference ID, tracked in billing. Max 256 chars.
Override model, e.g. `"google/gemini-2.5-flash"`. Max 200 chars.
The full request body, serialized as JSON, must not exceed 512,000 bytes.
## Response
Currently `1`ISO 8601Echoes request `scope`Echoes request `period``"customer_payload"`120–400 word summary1–120 chars`key_development` · `implementation_pressure` · `alignment_opportunity`Up to 12 items`elevated` · `moderate` · `for_awareness`Optional human-readable label overrides for categories/sections/attention levels
```bash cURL theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/artifacts/generate \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"templateId": "insight-narrative-v1",
"scope": { "level": "organization", "label": "Acme Inc." },
"period": { "start": "2026-04-01", "end": "2026-06-30" },
"input": {
"aggregates": {
"recordCount": 4210,
"metrics": { "activeUsers": 1893, "churnRate": 0.042 },
"breakdowns": [
{ "dimension": "plan", "rows": [ { "key": "pro", "count": 1200 }, { "key": "free", "count": 3010 } ] }
]
},
"samples": [
{ "id": "u_1", "fields": { "plan": "pro", "lastActive": "2026-06-28" } }
],
"context": { "instructions": "Focus on churn drivers.", "locale": "en-US" }
}
}'
```
```json Response theme={null}
{
"data": {
"templateId": "insight-narrative-v1",
"outputSnapshot": {
"version": 1,
"generatedAt": "2026-07-08T10:00:00Z",
"templateId": "insight-narrative-v1",
"scope": { "level": "organization", "label": "Acme Inc." },
"period": { "start": "2026-04-01", "end": "2026-06-30" },
"source": { "type": "customer_payload", "recordCount": 4210, "sampleCount": 1 },
"llm": {
"executiveSummary": "Acme's active user base grew steadily through Q2...",
"insights": [
{
"title": "Free-to-pro conversion is slowing",
"body": "Only 8% of new free users converted to pro this quarter, down from 12%.",
"category": "implementation_pressure",
"evidence": ["1200 pro / 4210 total"]
}
],
"followUpConsiderations": [
{
"title": "Investigate onboarding drop-off",
"body": "Session data suggests most churn happens in the first 7 days.",
"attentionLevel": "elevated"
}
],
"confidenceNotes": "Based on a 4210-record sample with moderate breakdown coverage."
}
},
"usage": {
"model": "google/gemini-2.5-flash",
"inputTokens": 812,
"outputTokens": 340,
"costUsd": 0.0021,
"providerSlug": "google"
}
},
"meta": { "requestId": "9f2a...", "persisted": false }
}
```
## Errors
| Status | Code | Message |
| ------ | ---------------------- | ------------------------------------------------------------------------------------------------------ |
| 400 | `INVALID_INPUT` | Schema validation failure (bad `scope.level`, unknown `templateId`, oversized payload) |
| 401 | — | `Invalid API key` |
| 402 | `INSUFFICIENT_CREDITS` | `Insufficient credits. Please purchase additional credits to continue.` |
| 404 | `APP_NOT_FOUND` | `App not found in this workspace` |
| 500 | — | `The AI model could not produce a valid structured report. Please try again or use a different model.` |
| 503 | — | `No AI model configured` |
# Artifacts API
Source: https://docs.sundaypyjamas.com/api-reference/artifacts/introduction
Generate structured, LLM-authored reports from your own aggregated data
## Overview
The Artifacts API turns data you already have — aggregates, metrics, sample records — into a structured, narrative report: an executive summary, categorized insights, and follow-up considerations, generated by an LLM against a fixed output schema so it's safe to render directly in your product.
Unlike the Chat API, you don't send free-form prompts here. You send **data** (aggregates + samples) and a **template**, and the model returns a validated JSON report shaped to that template.
## Base URL
```
https://suite.sundaypyjamas.com/api/v1/artifacts
```
## Authentication
```http theme={null}
Authorization: Bearer spj_ai_your_api_key_here
```
## Resources
POST aggregated data and get back a structured insight report.
# Chat API Reference
Source: https://docs.sundaypyjamas.com/api-reference/chat/introduction
Complete API reference for the SundayPyjamas AI Suite Chat API with interactive examples
## Overview
The Chat API is the core endpoint for conversational AI, content generation, and text completion. It provides access to powerful language models with streaming responses for real-time interactions.
This reference includes all request/response schemas, parameters, and interactive examples you can test directly.
## Base URL
```
https://suite.sundaypyjamas.com/api/v1
```
## Authentication
All API requests require authentication using your API key:
```http theme={null}
Authorization: Bearer spj_ai_your_api_key_here
```
Learn more about [API key generation and management](/authentication).
## Endpoints
Send messages to AI models and receive streaming responses
## Quick Example
Here's a simple example to get you started:
```bash cURL theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/chat \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "Hello! Write me a professional email greeting."
}
]
}'
```
```javascript JavaScript theme={null}
const response = await fetch('https://suite.sundaypyjamas.com/api/v1/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer spj_ai_your_api_key_here',
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [
{ role: 'user', content: 'Hello! Write me a professional email greeting.' }
]
})
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let result = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
result += decoder.decode(value);
}
console.log(result);
```
```python Python theme={null}
import requests
response = requests.post(
'https://suite.sundaypyjamas.com/api/v1/chat',
headers={
'Authorization': 'Bearer spj_ai_your_api_key_here',
'Content-Type': 'application/json',
},
json={
'messages': [
{
'role': 'user',
'content': 'Hello! Write me a professional email greeting.'
}
]
},
stream=True
)
full_response = ''
for chunk in response.iter_content(chunk_size=None, decode_unicode=True):
if chunk:
full_response += chunk
print(full_response)
```
## Common Patterns
### System Messages
Use system messages to set the AI's behavior and context:
```json theme={null}
{
"messages": [
{
"role": "system",
"content": "You are a helpful assistant that writes professional emails."
},
{
"role": "user",
"content": "Write a follow-up email after a job interview."
}
]
}
```
### Multi-turn Conversations
Include conversation history for context:
```json theme={null}
{
"messages": [
{
"role": "user",
"content": "What's the weather like?"
},
{
"role": "assistant",
"content": "I don't have access to real-time weather data..."
},
{
"role": "user",
"content": "What about general weather patterns?"
}
]
}
```
### Content Generation
Structure prompts for specific content types:
```json theme={null}
{
"messages": [
{
"role": "system",
"content": "You are a content marketing expert. Write engaging blog posts with clear structure and actionable insights."
},
{
"role": "user",
"content": "Write a blog post about remote work productivity tips for software developers. Target length: 1000 words."
}
]
}
```
## Error Handling
All errors return a consistent format:
```json theme={null}
{
"error": "Human-readable error message"
}
```
Common HTTP status codes:
Invalid request format or missing required fields.
**Example:**
```json theme={null}
{
"error": "Messages array is required"
}
```
Invalid or missing API key.
**Example:**
```json theme={null}
{
"error": "Invalid API key"
}
```
Token limit exceeded or insufficient permissions.
**Example:**
```json theme={null}
{
"error": "Token limit exceeded"
}
```
Rate limit exceeded.
**Example:**
```json theme={null}
{
"error": "Rate limit exceeded"
}
```
Server-side error occurred.
**Example:**
```json theme={null}
{
"error": "Failed to generate response"
}
```
## Rate Limits
Usage measured in tokens (input + output)
No hard limits, but monitored for abuse
Monthly token limits per workspace
Multiple simultaneous requests supported
For detailed rate limit information, see the [Rate Limits guide](/rate-limits).
## Best Practices
### Request Optimization
* Be specific and clear in your instructions
* Use system messages to set context once
* Keep conversation history relevant and concise
```json theme={null}
{
"messages": [
{
"role": "system",
"content": "You write concise, professional emails."
},
{
"role": "user",
"content": "Write a project status update email to stakeholders."
}
]
}
```
* Trim old messages to stay within token limits
* Keep only relevant context for the current task
* Use consistent message formatting
```javascript theme={null}
function trimConversation(messages, maxTokens = 2000) {
// Keep system message and recent relevant messages
const systemMessage = messages.find(m => m.role === 'system');
const recentMessages = messages.slice(-10); // Last 10 messages
return systemMessage
? [systemMessage, ...recentMessages.filter(m => m.role !== 'system')]
: recentMessages;
}
```
* Always check response status codes
* Implement retry logic for transient errors
* Provide user-friendly error messages
```javascript theme={null}
async function makeRequest(messages) {
try {
const response = await fetch('/api/v1/chat', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ messages })
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error);
}
return response;
} catch (error) {
console.error('API request failed:', error);
throw error;
}
}
```
## SDK Libraries
Official and community libraries for Node.js and browsers
Async and sync clients with full type support
Community-maintained Go client library
Official SDKs are coming soon! For now, use the examples in our [code examples section](/examples/overview).
## Testing Tools
### API Testing
Use tools like Postman, Insomnia, or curl for testing:
```bash theme={null}
# Test endpoint availability
curl -X POST https://suite.sundaypyjamas.com/api/v1/chat \
-H "Authorization: Bearer your_api_key" \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "test"}]}' \
-w "\nStatus: %{http_code}\nTime: %{time_total}s\n"
```
### Load Testing
For production readiness testing:
```bash theme={null}
# Simple load test with ab (Apache Bench)
ab -n 100 -c 10 -T application/json \
-H "Authorization: Bearer your_api_key" \
-p test_payload.json \
https://suite.sundaypyjamas.com/api/v1/chat
```
## Support
Comprehensive guides and examples
Join discussions with other developers
Contact support through your workspace
Monitor API status and uptime
## Next Steps
Detailed documentation for the chat endpoint with all parameters
Complete implementation examples in multiple languages
Understanding usage limits and optimization strategies
Comprehensive error handling guide and patterns
# POST /chat
Source: https://docs.sundaypyjamas.com/api-reference/chat/post-chat
POST https://suite.sundaypyjamas.com/api/v1/chat
Send messages to AI models and receive streaming text responses for conversational AI and content generation
## Overview
The POST /chat endpoint is the primary way to interact with SundayPyjamas AI models. It accepts a conversation history and returns an AI-generated response as a streaming text.
All responses are streamed in real-time for better user experience in conversational applications.
## Authentication
Bearer token with your API key. Format: `Bearer spj_ai_your_api_key_here`
## Request Body
Array of conversation messages. Must contain at least one message with valid content.
The role of the message sender. Must be one of:
* `user` - Messages from the user/human
* `assistant` - Previous AI responses in the conversation
* `system` - System instructions to guide AI behavior
The message content. Cannot be empty or null.
AI model to use for generating the response. Currently available models:
* `llama-3.3-70b-versatile` - High-quality general-purpose model (default)
## Response
### Headers
The response includes the following headers for streaming:
```http theme={null}
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
```
### Body
Streaming text response from the AI model. The complete response is built by concatenating all streamed chunks.
## Examples
### Basic Request
```bash cURL theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/chat \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "Write a professional email greeting."
}
]
}'
```
```text Response theme={null}
Hello! Here's a professional email greeting for you:
Dear [Recipient's Name],
I hope this email finds you well. I wanted to reach out to discuss [purpose of email].
Best regards,
[Your Name]
```
### With System Message
```bash cURL theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/chat \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "system",
"content": "You are a technical writer who creates clear, concise documentation."
},
{
"role": "user",
"content": "Explain what an API is in simple terms."
}
]
}'
```
```text Response theme={null}
An API (Application Programming Interface) is like a waiter in a restaurant:
- You (the customer) make a request for food
- The waiter takes your order to the kitchen
- The kitchen prepares your meal
- The waiter brings back your food
Similarly, an API:
- Receives requests from applications
- Processes those requests
- Returns the requested data or performs actions
- Enables different software systems to communicate
APIs make it possible for apps on your phone to get weather data, social media feeds, or payment processing without having to build those services themselves.
```
### Multi-turn Conversation
```bash cURL theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/chat \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "What is machine learning?"
},
{
"role": "assistant",
"content": "Machine learning is a subset of artificial intelligence (AI) that enables computers to learn and improve from experience without being explicitly programmed for every task."
},
{
"role": "user",
"content": "Can you give me a simple example?"
}
]
}'
```
```text Response theme={null}
Sure! Here's a simple example:
**Email Spam Detection**
1. **Training**: You show the computer thousands of emails labeled as "spam" or "not spam"
2. **Learning**: The computer finds patterns - spam emails often have words like "FREE!", "URGENT!", multiple exclamation marks, or suspicious links
3. **Prediction**: When a new email arrives, the computer uses these learned patterns to predict if it's spam or not
The computer "learned" to recognize spam without being programmed with explicit rules like "if email contains 'FREE!' then it's spam." Instead, it discovered these patterns by analyzing examples.
Other everyday examples:
- Netflix recommending movies based on what you've watched
- Your phone's autocorrect learning your typing patterns
- Banks detecting fraudulent transactions
```
### Specifying Model
```bash cURL theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/chat \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "Generate a Python function to calculate fibonacci numbers."
}
],
"model": "llama-3.3-70b-versatile"
}'
```
### Content Generation
```bash cURL theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/chat \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "system",
"content": "You are a marketing copywriter. Write compelling copy that drives action and focuses on benefits."
},
{
"role": "user",
"content": "Write marketing copy for a productivity app targeting remote workers. Include a headline, 3 key benefits, and a call-to-action."
}
]
}'
```
## Error Responses
### 400 Bad Request
```json Missing Messages theme={null}
{
"error": "Messages array is required"
}
```
```json Invalid Message Format theme={null}
{
"error": "Last message must have valid content"
}
```
```json Invalid Request Body theme={null}
{
"error": "Invalid request body"
}
```
### 401 Unauthorized
```json Invalid API Key theme={null}
{
"error": "Invalid API key"
}
```
### 403 Forbidden
```json Token Limit Exceeded theme={null}
{
"error": "Token limit exceeded"
}
```
```json Insufficient Permissions theme={null}
{
"error": "Insufficient permissions"
}
```
### 429 Too Many Requests
```json Rate Limited theme={null}
{
"error": "Rate limit exceeded"
}
```
### 500 Internal Server Error
```json Server Error theme={null}
{
"error": "Failed to generate response"
}
```
## Streaming Implementation
### JavaScript/TypeScript
```typescript theme={null}
async function streamChatResponse(messages: ChatMessage[]) {
const response = await fetch('/api/v1/chat', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ messages })
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error);
}
const reader = response.body!.getReader();
const decoder = new TextDecoder();
let fullResponse = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
fullResponse += chunk;
// Process each chunk in real-time
onChunk(chunk);
}
return fullResponse;
}
```
### Python
```python theme={null}
import requests
def stream_chat_response(messages):
response = requests.post(
'https://suite.sundaypyjamas.com/api/v1/chat',
headers={
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json',
},
json={'messages': messages},
stream=True
)
response.raise_for_status()
full_response = ''
for chunk in response.iter_content(chunk_size=None, decode_unicode=True):
if chunk:
full_response += chunk
# Process each chunk in real-time
print(chunk, end='', flush=True)
return full_response
```
### Go
```go theme={null}
package main
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"net/http"
)
func streamChatResponse(messages []Message) (string, error) {
requestBody := ChatRequest{Messages: messages}
jsonData, _ := json.Marshal(requestBody)
req, _ := http.NewRequest("POST", "https://suite.sundaypyjamas.com/api/v1/chat",
bytes.NewBuffer(jsonData))
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
// Handle error response
var errorResp ErrorResponse
json.NewDecoder(resp.Body).Decode(&errorResp)
return "", fmt.Errorf("API error: %s", errorResp.Error)
}
scanner := bufio.NewScanner(resp.Body)
var fullResponse strings.Builder
for scanner.Scan() {
chunk := scanner.Text()
fullResponse.WriteString(chunk)
// Process each chunk in real-time
fmt.Print(chunk)
}
return fullResponse.String(), nil
}
```
## Request Validation
### Message Array Requirements
```json theme={null}
{
"messages": [
{
"role": "user",
"content": "Your message here"
}
]
}
```
✅ **Valid**: Contains required fields with proper types
```json theme={null}
{
"messages": []
}
```
❌ **Invalid**: Empty messages array
```json theme={null}
{
"messages": [
{
"role": "user"
}
]
}
```
❌ **Invalid**: Missing content field
```json theme={null}
{
"messages": [
{
"role": "user",
"content": ""
}
]
}
```
❌ **Invalid**: Empty content string
```json theme={null}
{
"messages": [
{
"role": "invalid_role",
"content": "Hello"
}
]
}
```
❌ **Invalid**: Invalid role value
### Content Length Limits
Very long requests may hit token limits. Consider breaking large content into smaller chunks or summarizing previous context.
**Practical limits:**
* **Single message**: \~10,000 characters recommended
* **Total conversation**: \~20,000 characters for optimal performance
* **Token estimation**: \~4 characters per token
## Rate Limiting Details
Input + output tokens count toward workspace limits
No hard limits, but monitored for abuse
Multiple simultaneous requests supported
Excessive usage may be throttled
### Optimization Tips
To optimize your usage:
* Use clear, concise prompts
* Trim conversation history to relevant context
* Batch similar requests when possible
* Monitor token usage through workspace analytics
## Testing with Different Tools
### Postman
```json theme={null}
POST https://suite.sundaypyjamas.com/api/v1/chat
Headers:
Authorization: Bearer spj_ai_your_api_key_here
Content-Type: application/json
Body (raw JSON):
{
"messages": [
{
"role": "user",
"content": "Test message"
}
]
}
```
### HTTPie
```bash theme={null}
http POST https://suite.sundaypyjamas.com/api/v1/chat \
Authorization:"Bearer spj_ai_your_api_key_here" \
messages:='[{"role": "user", "content": "Test message"}]'
```
### Insomnia
```json theme={null}
{
"method": "POST",
"url": "https://suite.sundaypyjamas.com/api/v1/chat",
"headers": {
"Authorization": "Bearer spj_ai_your_api_key_here",
"Content-Type": "application/json"
},
"body": {
"messages": [
{
"role": "user",
"content": "Test message"
}
]
}
}
```
## Performance Considerations
### Response Times
Typical response times vary based on:
* **Request complexity**: Simple queries respond faster
* **Response length**: Longer responses take more time
* **Server load**: Peak times may have slightly longer latencies
**Expected ranges:**
* Simple queries: 1-3 seconds
* Complex content generation: 3-10 seconds
* Very long responses: 10-30 seconds
### Best Practices
* Use specific, focused prompts
* Limit conversation history to relevant context
* Request shorter responses when appropriate
* Use streaming to show progress to users
```javascript theme={null}
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 30000); // 30s timeout
try {
const response = await fetch('/api/v1/chat', {
method: 'POST',
headers: headers,
body: JSON.stringify({ messages }),
signal: controller.signal
});
clearTimeout(timeoutId);
// Process response...
} catch (error) {
if (error.name === 'AbortError') {
console.log('Request timed out');
}
}
```
* Reuse HTTP connections when possible
* Implement proper connection pooling
* Handle network interruptions gracefully
* Use appropriate timeouts for your use case
## Next Steps
See complete implementation examples in multiple languages
Learn comprehensive error handling patterns
Understand usage optimization and monitoring
Manage API keys and security best practices
# POST /image
Source: https://docs.sundaypyjamas.com/api-reference/image/generate
POST https://suite.sundaypyjamas.com/api/v1/image
Generate a new image from a prompt, or edit an existing image
## Request Body
Text description of the image to generate, or the edit instructions when `operation` is `"edit"`. Required unless `profile` supplies a default prompt.
`"generate"` (text-to-image) or `"edit"` (image-to-image).
Gemini image model, e.g. `"gemini-2.5-flash-image"` or `"gemini-3-pro-image-preview"`. Defaults to the first active image model in your workspace's catalog, or the profile default.
Named enhancement profile, e.g. `"ultrasound"`. Applies profile defaults for prompt, operation, and model.
Base64-encoded source image (with or without a `data:image/png;base64,` prefix). Required for `operation: "edit"` unless `image_url` or `raw_image_id` is given.
URL to a source image (PNG, JPEG, WebP, or GIF). Alternative to `image`.
UUID of a previously uploaded raw image (requires S3 storage) — re-runs enhancement on a stored upload instead of re-uploading.
`"gemini"` or `"aws-nova-canvas"`. AWS Nova Canvas is only available with `profile: "ultrasound"`.
Store raw/processed images in S3. Defaults to `true` if `STORAGE_PROVIDER=s3` is configured for your workspace, otherwise `false`.
## Response
Model usedResolved prompt actually sent to the model`"generate"` or `"edit"`
Base64-encoded PNG. Present when S3 storage isn't enabled.
Profile used, if anyPresent only with S3 storage enabledPresent only with S3 storage enabledSigned URL to the raw image. Present only with S3 storage enabledSigned URL to the generated/edited image. Present only with S3 storage enabled
```bash cURL (generate) theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/image \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"prompt": "A minimalist logo of a mountain, flat design, teal and white"
}'
```
```bash cURL (edit) theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/image \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"operation": "edit",
"prompt": "Add a sunset gradient in the background",
"image_url": "https://example.com/logo.png"
}'
```
```json Response (base64) theme={null}
{
"model": "gemini-2.5-flash-image",
"prompt": "A minimalist logo of a mountain, flat design, teal and white",
"operation": "generate",
"tokensUsed": 1290,
"image": "iVBORw0KGgoAAAANSUhEUgAA..."
}
```
```json Response (S3 storage enabled) theme={null}
{
"model": "gemini-2.5-flash-image",
"prompt": "A minimalist logo of a mountain, flat design, teal and white",
"operation": "generate",
"raw_image_id": "9f21...",
"processed_run_id": "af02...",
"raw_url": "https://storage.../raw/9f21....png?sig=...",
"processed_url": "https://storage.../processed/af02....png?sig=...",
"request_id": "7c31..."
}
```
## Errors
| Status | Code | Message |
| ------ | ---------------------- | ----------------------------------------------------------------------------------------- |
| 400 | `MISSING_IMAGE_INPUT` | `Image input required for edit operation. Provide either "image" (base64) or "image_url"` |
| 400 | — | `Prompt is required` |
| 400 | — | `Invalid model` |
| 401 | — | `Invalid API key` |
| 402 | `INSUFFICIENT_CREDITS` | `Insufficient credits. Please purchase additional credits to continue.` |
| 429 | — | Gemini quota exceeded |
| 500 | — | `Failed to generate image: {details}` |
## GET /image
Returns endpoint metadata — useful for a quick connectivity check.
```bash cURL theme={null}
curl https://suite.sundaypyjamas.com/api/v1/image \
-H "Authorization: Bearer spj_ai_your_api_key_here"
```
```json Response theme={null}
{
"message": "Image API. POST with { prompt?, profile?: \"ultrasound\", model?, image?, operation? }. Auth: Bearer API key",
"endpoint": "/api/v1/image",
"methods": ["GET", "POST", "OPTIONS"],
"profiles": ["ultrasound"]
}
```
# Image API
Source: https://docs.sundaypyjamas.com/api-reference/image/introduction
Generate and edit images with Gemini image models
## Overview
The Image API generates images from a text prompt, or edits an existing image given a prompt and a source image. It also supports **profiles** — named presets (e.g. `ultrasound`) that pre-configure the prompt, operation, and model for a specialized use case.
## Base URL
```
https://suite.sundaypyjamas.com/api/v1/image
```
## Authentication
```http theme={null}
Authorization: Bearer spj_ai_your_api_key_here
```
## Storage
If your workspace has S3 storage configured (`STORAGE_PROVIDER=s3`), generated images are persisted and the response returns signed URLs (`raw_url`, `processed_url`) instead of inline base64 — pass `persist: true`/`false` to override the default.
## Resources
POST a prompt (and optionally a source image) to generate or edit an image.
# Conversations
Source: https://docs.sundaypyjamas.com/api-reference/insights/conversations
List and inspect conversation analytics
## List Conversations
```http theme={null}
GET /api/v1/workspaces/{workspaceId}/insights/conversations
```
Page size, clamped to 1–100.ISO 8601. Filters by `updated_at` (or `created_at` if `updated_at` is null) >= `from`.ISO 8601. Filters by `updated_at` (or `created_at`) \<= `to`.Filter by app ID. Repeat the param for multiple apps, or use `app_ids` as a comma-separated list.
Filter by end user — matches `metadata.external_user_id`, `metadata.anonymous_visitor_id`, or `session_id`.
Derived end-user identifier`{ limit, offset, total }`
```bash cURL theme={null}
curl "https://suite.sundaypyjamas.com/api/v1/workspaces/ws_123/insights/conversations?limit=10&from=2026-06-01" \
-H "Authorization: Bearer spj_ai_your_api_key_here"
```
***
## Get Conversation
```http theme={null}
GET /api/v1/workspaces/{workspaceId}/insights/conversations/{threadId}
```
Truncated to 4000 characters (with `…`) if longerPer-component cost breakdownUp to 200 most recent raw usage events
```bash cURL theme={null}
curl https://suite.sundaypyjamas.com/api/v1/workspaces/ws_123/insights/conversations/thr_a1b2... \
-H "Authorization: Bearer spj_ai_your_api_key_here"
```
## Errors
| Status | Error | Cause |
| ------ | ------------------------ | -------------------------------------------------------------------- |
| 401 | `Unauthorized` | Invalid key, or `workspaceId` doesn't match your API key's workspace |
| 404 | `Conversation not found` | Thread doesn't exist or belongs to a different workspace |
# Insights API
Source: https://docs.sundaypyjamas.com/api-reference/insights/introduction
Query conversation analytics and usage across your workspace's apps
## Overview
The Insights API gives you read access to every conversation your workspace's apps have had — across [Apps](/api-reference/apps/introduction) and [Agents](/api-reference/agents/introduction) — along with per-conversation usage and cost.
## Base URL
```
https://suite.sundaypyjamas.com/api/v1/workspaces/{workspaceId}/insights
```
## Authentication
```http theme={null}
Authorization: Bearer spj_ai_your_api_key_here
```
The `workspaceId` in the path must match the workspace your API key belongs to.
## Resources
List and inspect conversations with usage summaries, messages, and cost breakdowns.
# Integration Packs
Source: https://docs.sundaypyjamas.com/api-reference/integrations/integration-packs
Browse the catalog of available integration packs
## List Integration Packs
```http theme={null}
GET /api/v1/integration-packs
```
Public, unauthenticated. Returns only active packs, ordered alphabetically by name.
```bash cURL theme={null}
curl https://suite.sundaypyjamas.com/api/v1/integration-packs
```
***
## Get Integration Pack
```http theme={null}
GET /api/v1/integration-packs/{packId}
```
Pack object — see [List Integration Packs](#list-integration-packs)`callback_sync` or `hitl_deferred` (human-in-the-loop)JSON SchemaJSON Schema
```bash cURL theme={null}
curl https://suite.sundaypyjamas.com/api/v1/integration-packs/notion
```
```json 404 Not Found theme={null}
{ "error": "Pack not found" }
```
# Integrations API
Source: https://docs.sundaypyjamas.com/api-reference/integrations/introduction
Browse integration packs and manage a workspace's connected MCP tools
## Overview
**Integration packs** are bundled sets of tools and skills (e.g. a "Notion" pack, a "Linear" pack) that an [agent](/api-reference/agents/introduction) can use. **MCP connectors** are the workspace-level OAuth connections that back those packs — connect once per workspace, then bind the pack to any agent.
[`GET /integration-packs`](/api-reference/integrations/integration-packs) to see what's available, and what tools/skills each pack provides.
[`POST .../mcp-connectors/{connectorId}`](/api-reference/integrations/mcp-connectors#connect) to start an OAuth flow and connect the underlying service to your workspace.
Attach the pack via `integrationBindings` on [Update Agent](/api-reference/agents/agents#update-agent).
## Base URL
```
https://suite.sundaypyjamas.com/api/v1
```
## Authentication
`GET /integration-packs` is public and unauthenticated (catalog metadata only). MCP connector endpoints require a workspace API key or session; **connecting, disconnecting, and enabling/disabling connectors requires a workspace owner or admin** — these are session-only operations, not available via API key.
## Resources
Browse the pack catalog and each pack's tools/skills.
Connect, disconnect, and toggle workspace integrations.
# MCP Connectors
Source: https://docs.sundaypyjamas.com/api-reference/integrations/mcp-connectors
Connect, disconnect, and toggle a workspace's MCP integrations
Connect, Disconnect, and Toggle require an authenticated **session** (workspace owner or admin) — they're designed for the AI Suite web app's settings UI, not machine-to-machine API key access. List works with either a session or an API key.
## List Connectors
```http theme={null}
GET /api/v1/workspaces/{workspaceId}/mcp-connectors
```
e.g. `"notion"`, `"canva"`, `"linear"`MCP server name, as referenced in an agent's `config.capabilities.mcp_servers``http` · `sse` · `stdio``connected` · `disconnected` · `error`
```bash cURL theme={null}
curl https://suite.sundaypyjamas.com/api/v1/workspaces/ws_123/mcp-connectors \
-H "Authorization: Bearer spj_ai_your_api_key_here"
```
***
## Connect
```http theme={null}
POST /api/v1/workspaces/{workspaceId}/mcp-connectors/{connectorId}
```
Starts an OAuth flow for the connector. Redirect the user to the returned `authorizeUrl`; after they approve, the provider redirects to AI Suite's OAuth callback, which completes the connection and redirects the user to `returnTo`.
Where to send the user after the OAuth flow completes. Defaults to the workspace's agent settings page. Unsafe/external paths are ignored.
Redirect the user's browser here to start OAuth.
```bash cURL theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/workspaces/ws_123/mcp-connectors/notion \
-H "Cookie: " \
-H "Content-Type: application/json" \
-d '{ "returnTo": "/workspace/ws_123/agents?tab=connectors" }'
```
```json Response theme={null}
{ "authorizeUrl": "https://api.notion.com/v1/oauth/authorize?client_id=...&code_challenge=...&state=..." }
```
Tokens are never returned to the client — they're exchanged server-side and stored encrypted. On completion, the user lands back on `returnTo` with `?connected={connectorId}` appended so your UI can refresh.
***
## Disconnect
```http theme={null}
DELETE /api/v1/workspaces/{workspaceId}/mcp-connectors/{connectorId}
```
Revokes the stored OAuth tokens and marks the connector `disconnected`. Idempotent.
```json Response theme={null}
{ "disconnected": true }
```
***
## Enable / Disable
```http theme={null}
PATCH /api/v1/workspaces/{workspaceId}/mcp-connectors/{connectorId}
```
Toggles whether a **connected** connector is available to agents, without revoking its tokens — useful for temporarily pausing access.
```bash cURL theme={null}
curl -X PATCH https://suite.sundaypyjamas.com/api/v1/workspaces/ws_123/mcp-connectors/notion \
-H "Cookie: " \
-H "Content-Type: application/json" \
-d '{ "enabled": false }'
```
```json Response theme={null}
{ "enabled": false }
```
## Errors
| Status | Error | Cause |
| --------- | ----------------------------------------------------------------------------------------- | ---------------------------------------- |
| 400 | `enabled boolean is required` | Toggle: missing/invalid `enabled` |
| 401 / 403 | `Only workspace owners and admins can manage skills` | Connect/Disconnect/Toggle by a non-admin |
| 404 | `Connector not found` | Unknown `connectorId` |
| 500 | `Failed to start OAuth` / `Failed to disconnect connector` / `Failed to update connector` | Server error |
# Platform Tools API
Source: https://docs.sundaypyjamas.com/api-reference/platform-tools/introduction
Browse the catalog of built-in tools available to agents
## List Platform Tools
```http theme={null}
GET /api/v1/platform-tools
```
Returns the public catalog of tools that can be attached to an [agent's `toolAllowlist`](/api-reference/agents/agents#agent-config-reference) — e.g. web search, web fetch, and other built-ins. This endpoint is **unauthenticated** and returns only display metadata (no secrets or usage data).
Filter to tools compatible with a given agent kind.
Filter to tools compatible with a given runtime mode (`assist` or `autonomous`).
```bash cURL theme={null}
curl "https://suite.sundaypyjamas.com/api/v1/platform-tools?runtimeMode=autonomous"
```
```json Response theme={null}
{
"tools": [
{
"toolId": "web_search",
"name": "Web Search",
"description": "Search the web for current information",
"category": "research",
"executionKind": "builtin",
"agentKinds": ["agent"],
"runtimeModes": ["assist", "autonomous"]
}
]
}
```
# RAG API
Source: https://docs.sundaypyjamas.com/api-reference/rag/introduction
Upload documents and query them with retrieval-augmented generation
## Overview
The RAG API lets a workspace ingest documents, chunk and embed them, and query them with natural-language questions that return a generated answer plus the source passages it was grounded in.
POST files to [`/api/v1/rag/upload`](/api-reference/rag/upload). Each file is text-extracted, chunked, embedded, and indexed per-workspace.
POST a natural-language question to [`/api/v1/rag/query`](/api-reference/rag/query). The engine retrieves the most relevant chunks and returns a generated answer with cited sources.
## Base URL
```
https://suite.sundaypyjamas.com/api/v1/rag
```
Unlike the rest of the platform API, the RAG endpoints currently authenticate via a **Supabase session** (the same auth used by the AI Suite web app) rather than a workspace API key. If you're integrating from a backend service rather than the AI Suite frontend, reach out about API-key support for these endpoints before building against them in production.
## Quotas
Each workspace has a monthly query quota (`rag_workspaces.monthly_quota`, defaults to 1,000). Queries increment usage by 1; uploads are not quota-limited but are capped at 100 MB per file.
## Resources
Ingest documents (PDF, DOCX, TXT, MD, CSV, JSON) into the workspace's RAG index.
Ask questions and get grounded answers with source citations.
# POST /rag/query
Source: https://docs.sundaypyjamas.com/api-reference/rag/query
POST https://suite.sundaypyjamas.com/api/v1/rag/query
Ask a natural-language question grounded in your uploaded documents
## Authentication
Bearer token from an authenticated Supabase session. See the [note on RAG auth](/api-reference/rag/introduction#base-url).
## Request Body
The question to answer.
UUID of the workspace whose documents to search.
Maximum number of source chunks to retrieve.
Minimum relevance score (0–1) for a chunk to be included as a source.
Whether to include source metadata in the response.
Optional identifier to associate this query with a conversation for context continuity.
## Response
`"success"`Generated answer0–1 confidence score for the answerRelevant text excerptSource document nameToken/quota usage for this queryTiming/processing metricsISO 8601
```bash cURL theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/rag/query \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"query": "What were the top risks identified in the Q2 report?",
"workspace_id": "9d4c1a2e-...",
"options": { "max_sources": 3 }
}'
```
```json Response theme={null}
{
"status": "success",
"result": {
"answer": "The Q2 report identifies three key risks: supply chain delays, rising customer acquisition costs, and increased competitive pressure in the EU market.",
"confidence": 0.86,
"sources": [
{
"content": "Supply chain delays increased average fulfillment time by 4 days...",
"document": "report.pdf",
"page": 12,
"confidence": 0.91,
"relevance_score": 0.88
}
]
},
"usage": {},
"performance": {},
"workspace_id": "9d4c1a2e-...",
"timestamp": "2026-07-08T10:05:00Z"
}
```
## Errors
| Status | Error | Cause |
| ------ | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| 400 | `Query is required and must be a string` | Missing/invalid `query` |
| 400 | `workspace_id is required` | Missing `workspace_id` |
| 401 | `Authentication required` | Missing/invalid session |
| 403 | `Access denied to workspace` | Caller isn't a member of the workspace |
| 403 | `RAG service not enabled for this workspace` | RAG hasn't been enabled/provisioned yet — [upload a document](/api-reference/rag/upload) first |
| 429 | `Monthly quota exceeded` (includes `quota` and `current_usage`) | Workspace has used its monthly query quota |
| 500 | `Failed to process query` | Server error (see `details` in response) |
# POST /rag/upload
Source: https://docs.sundaypyjamas.com/api-reference/rag/upload
POST https://suite.sundaypyjamas.com/api/v1/rag/upload
Upload and index documents for retrieval-augmented generation
## Overview
Uploads one or more files, extracts their text, splits it into chunks, generates embeddings, and stores everything in the workspace's RAG index so it can be queried via [`/rag/query`](/api-reference/rag/query).
## Authentication
Bearer token from an authenticated Supabase session. See the [note on RAG auth](/api-reference/rag/introduction#base-url).
## Request
Content type: `multipart/form-data`
One or more files to upload, as `files[]` form fields.
UUID of the workspace to associate the documents with. The workspace's RAG index is auto-provisioned on first upload if it doesn't already exist.
### Supported File Types
| Type | MIME type | Max size |
| ---------- | ------------------------------------------------------------------------- | -------- |
| Plain text | `text/plain` (or `.txt`) | 100 MB |
| Markdown | `text/markdown` (or `.md`) | 100 MB |
| CSV | `text/csv` | 100 MB |
| JSON | `application/json` | 100 MB |
| PDF | `application/pdf` | 100 MB |
| Word | `application/vnd.openxmlformats-officedocument.wordprocessingml.document` | 100 MB |
Documents are split into \~1000-character chunks along sentence boundaries. CSV files are converted to structured text (with column headers preserved) before chunking.
## Response
`"success"`e.g. `"Processed 3 files"`Number of chunks created`"completed"` or `"failed"`Present only if `status` is `"failed"`ISO 8601
```bash cURL theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/rag/upload \
-H "Authorization: Bearer " \
-F "workspace_id=9d4c1a2e-..." \
-F "files=@report.pdf" \
-F "files=@notes.md"
```
```json Response theme={null}
{
"status": "success",
"message": "Processed 2 files",
"results": {
"successful": 2,
"failed": 0,
"details": [
{ "documentId": "doc_1a2b...", "chunks": 14, "status": "completed" },
{ "documentId": "doc_3c4d...", "chunks": 6, "status": "completed" }
]
},
"workspace_id": "9d4c1a2e-...",
"timestamp": "2026-07-08T10:00:00Z"
}
```
## Errors
| Status | Error | Cause |
| ------ | ------------------------------------------- | -------------------------------------------- |
| 400 | `workspace_id is required` | Missing `workspace_id` field |
| 400 | `At least one file is required` | No files in `files[]` |
| 400 | `File {name} exceeds maximum size of 100MB` | File too large |
| 400 | `File type {type} not supported for {name}` | Unsupported MIME type |
| 401 | `Authentication required` | Missing/invalid session |
| 403 | `Access denied to workspace` | Caller isn't a member of the workspace |
| 500 | `Failed to upload files` | Processing error (see `details` in response) |
Partial success is allowed — some files can fail while others succeed. Check `results.details[].status` for each file.
# Files
Source: https://docs.sundaypyjamas.com/api-reference/storage/files
List, move, rename, and read file content
## List Files
```http theme={null}
GET /api/v1/workspaces/{workspaceId}/storage
```
`all` · `images` · `deliverables` · `documents` · `files`
Capped at 200.Filename search (partial match).`image` · `deliverable` · `document` · `file`Human-readable origin labelApp ID, run ID, or upload ID that produced this fileSigned S3 URL, valid 15 minutesLogical path in the workspace tree (`type: "file"` only)`none` · `queued` · `processing` · `ready` · `failed`True if this file was imported from GitHubGitHub repo/branch/commit metadata, if applicable`{ images, deliverables, documents, files }` counts
```bash cURL theme={null}
curl "https://suite.sundaypyjamas.com/api/v1/workspaces/ws_123/storage?type=documents&limit=20" \
-H "Authorization: Bearer spj_ai_your_api_key_here"
```
***
## Get File Content
```http theme={null}
GET /api/v1/workspaces/{workspaceId}/storage/content
```
`image` · `deliverable` · `document` · `file`
Streams the raw file with `Content-Type` set to the stored MIME type and `Content-Disposition: inline`.
```bash cURL theme={null}
curl "https://suite.sundaypyjamas.com/api/v1/workspaces/ws_123/storage/content?fileId=file_1&fileType=document" \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-o downloaded-file.pdf
```
***
## Move or Rename File
```http theme={null}
PATCH /api/v1/workspaces/{workspaceId}/storage/files/{fileId}
```
`move` or `rename`Required when `action` is `move`.Required when `action` is `rename`.
```bash cURL theme={null}
curl -X PATCH https://suite.sundaypyjamas.com/api/v1/workspaces/ws_123/storage/files/file_1 \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{ "action": "move", "destFolderPath": "/archive" }'
```
```json Response theme={null}
{ "file": { "id": "file_1", "filename": "report.pdf", "path": "/archive/report.pdf", "...": "..." } }
```
There is also a bulk variant of this operation at `PATCH /api/v1/workspaces/{workspaceId}/storage/files` — same body shape, plus a top-level `fileId` field to select the target file.
## Errors
| Status | Error |
| ------ | ----------------------------------------------------------------------------------------------- |
| 400 | `fileId is required` / `destFolderPath is required for move` / `newName is required for rename` |
| 400 | `Missing fileId or fileType` (Get Content) |
| 401 | `Unauthorized` |
| 404 | `File not found` / `Storage key not found` / `Empty file` |
# Folders & Tree
Source: https://docs.sundaypyjamas.com/api-reference/storage/folders
Create and rename folders, and browse the folder tree
## Create Folder
```http theme={null}
POST /api/v1/workspaces/{workspaceId}/storage/folders
```
```json 201 Created theme={null}
{
"created": true,
"folder": { "id": "fold_1", "name": "archive", "path": "/archive" }
}
```
***
## Rename Folder
```http theme={null}
PATCH /api/v1/workspaces/{workspaceId}/storage/folders
```
***
## Browse Path / Tree
```http theme={null}
GET /api/v1/workspaces/{workspaceId}/storage/path
```
Alias: `path`.
`1` — return just the list of folder paths. `full` — return the entire tree (folders + files).
`tree` is an alias for `tree=1`.
### Response shapes
```json theme={null}
{
"path": "/archive",
"folders": [ { "id": "fold_2", "name": "2026", "path": "/archive/2026" } ],
"files": [ /* StorageFile[] — see Files */ ]
}
```
```json theme={null}
{ "paths": ["/", "/archive", "/archive/2026", "/uploads"] }
```
```json theme={null}
{
"folders": [ /* all folders */ ],
"files": [ /* all files */ ]
}
```
```bash cURL theme={null}
curl "https://suite.sundaypyjamas.com/api/v1/workspaces/ws_123/storage/path?folderPath=/archive" \
-H "Authorization: Bearer spj_ai_your_api_key_here"
```
## Errors
| Status | Error |
| ------ | ---------------------------------------------------------------------------------- |
| 400 | `folderPath is required` (Create) / `folderPath and newName are required` (Rename) |
| 401 | `Unauthorized` |
# Import
Source: https://docs.sundaypyjamas.com/api-reference/storage/import
Bulk-import a folder, zip archive, or GitHub repository
## Import Files
```http theme={null}
POST /api/v1/workspaces/{workspaceId}/storage/import
```
Content type: `multipart/form-data`. Max 500 MB per file.
`folder` · `zip` · `files`A single zip archive — required when `kind` is `zip`.Multiple files (with `webkitRelativePath` for folder structure) — required when `kind` is `folder` or `files`.Destination path prefix, e.g. `/my-docs/`.`{ path, error }` for any files that failed to import
```bash cURL theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/workspaces/ws_123/storage/import \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-F "kind=zip" \
-F "pathPrefix=/imports/" \
-F "file=@archive.zip"
```
```json 201 Created theme={null}
{ "imported": 12, "skipped": 1, "errors": [], "fileIds": ["file_1", "file_2"] }
```
***
## Import from GitHub
```http theme={null}
POST /api/v1/workspaces/{workspaceId}/storage/import/github
```
Downloads a repository (as a zip) directly from GitHub and imports it into the workspace tree — no local download required.
`https://github.com/owner/repo` (`.git` suffix accepted). Append `?branch=develop` to the URL, or use the `branch` field.
Defaults to the repo's default branch.Destination path prefix.`owner/repo`
```bash cURL theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/workspaces/ws_123/storage/import/github \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{ "repoUrl": "https://github.com/acme/handbook", "branch": "main", "pathPrefix": "/handbook/" }'
```
```json 201 Created theme={null}
{
"imported": 84,
"skipped": 0,
"errors": [],
"fileIds": ["file_10", "file_11"],
"repo": "acme/handbook",
"branch": "main"
}
```
For private repositories, configure a GitHub token on the workspace (`GITHUB_TOKEN` / `GITHUB_IMPORT_TOKEN`) — reach out if you need this enabled for your workspace.
## Errors
| Status | Error | Applies to |
| ------ | ------------------------------------------------------- | ------------- |
| 400 | `Zip file is required` / `No files to import` | Import |
| 413 | `File "{name}" exceeds the 500 MB upload limit` | Import |
| 400 | `repoUrl is required` / `Invalid GitHub repository URL` | GitHub Import |
| 502 | `GitHub archive fetch failed: {status} {statusText}` | GitHub Import |
| 401 | `Unauthorized` | Both |
# Storage API
Source: https://docs.sundaypyjamas.com/api-reference/storage/introduction
Browse, organize, and import files in a workspace's file tree
## Overview
The Storage API exposes a workspace's unified file tree — uploaded documents, generated deliverables, images, and imported source files (including whole GitHub repos) — as a single browsable, foldered structure.
## Base URL
```
https://suite.sundaypyjamas.com/api/v1/workspaces/{workspaceId}/storage
```
## Authentication
Supports **dual authentication** — either works:
```http theme={null}
Authorization: Bearer spj_ai_your_api_key_here
```
or an authenticated session cookie (for calls made from the AI Suite web app itself).
## Resources
List, move, rename, and read the content of files.
Create and rename folders, and browse the folder tree.
Bulk-import folders, zip archives, or an entire GitHub repository.
# Indexes
Source: https://docs.sundaypyjamas.com/api-reference/vector-store/indexes
Create, list, inspect, and delete vector collections
## Create Index
```http theme={null}
POST /api/v1/apps/{appId}/vector/{vectorName}/create-index
```
Embedding dimension. Must be a positive integer.
One of `Cosine`, `Dot`, `Euclid`, `Manhattan`.
Optional backend-specific configuration, passed through as-is.
```bash cURL theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/apps/app_123/vector/docs/create-index \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{ "vectorSize": 1536, "distance": "Cosine" }'
```
```json 201 Created theme={null}
{
"created": true,
"collectionName": "docs",
"vectorSize": 1536,
"distance": "Cosine"
}
```
Returns `409` if a collection with this `vectorName` already exists for the app.
***
## List Indexes
```http theme={null}
GET /api/v1/apps/{appId}/vector/{vectorName}/indexes
```
```json Response theme={null}
{ "indexes": ["docs", "products"] }
```
***
## Get Index
```http theme={null}
GET /api/v1/apps/{appId}/vector/{vectorName}/indexes/{indexName}
```
Backend-specific index metadata (point count, config, etc.)
```json 404 Not Found theme={null}
{ "error": "Index \"docs\" not found" }
```
***
## Delete Index
```http theme={null}
DELETE /api/v1/apps/{appId}/vector/{vectorName}/indexes/{indexName}
```
```json Response theme={null}
{ "deleted": true, "collectionName": "docs" }
```
# Vector Store API
Source: https://docs.sundaypyjamas.com/api-reference/vector-store/introduction
Per-app vector collections for similarity search and custom retrieval pipelines
## Overview
Each app can have one or more named **vector stores** — collections of embeddings you manage directly, useful when you want full control over chunking and retrieval instead of using the higher-level [RAG API](/api-reference/rag/introduction).
[`POST .../create-index`](/api-reference/vector-store/indexes#create-index) with your embedding dimension and distance metric.
[`POST .../upsert`](/api-reference/vector-store/vectors#upsert-vectors) with `{ id, vector, payload }` points.
[`POST .../query`](/api-reference/vector-store/vectors#query-vectors) with a raw vector (or `query` text for Bedrock Knowledge Base–backed apps).
## Base URL
```
https://suite.sundaypyjamas.com/api/v1/apps/{appId}/vector/{vectorName}
```
`vectorName` is a namespace you choose per app — e.g. `"docs"`, `"products"` — allowing one app to maintain multiple independent vector collections.
## Authentication
```http theme={null}
Authorization: Bearer spj_ai_your_api_key_here
```
Widget tokens are also accepted as a fallback for browser-embedded use cases.
## Resources
Create, list, inspect, and delete vector collections.
Upsert and query vector points.
# Vectors
Source: https://docs.sundaypyjamas.com/api-reference/vector-store/vectors
Upsert and query vector points
## Upsert Vectors
```http theme={null}
POST /api/v1/apps/{appId}/vector/{vectorName}/upsert
```
Non-empty array of points to insert or update.
Unique point identifier — upserting an existing ID overwrites it.Embedding vector, matching the index's `vectorSize`.Arbitrary metadata to store alongside the vector. Defaults to `{}`.
```bash cURL theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/apps/app_123/vector/docs/upsert \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"vectors": [
{ "id": "chunk_1", "vector": [0.012, -0.034, "..."], "payload": { "text": "Refunds are processed within 5 business days." } }
]
}'
```
```json Response theme={null}
{ "upserted": 1, "collectionName": "docs" }
```
***
## Query Vectors
```http theme={null}
POST /api/v1/apps/{appId}/vector/{vectorName}/query
```
Query embedding. Required unless `query` is provided and the app's vector backend is a Bedrock Knowledge Base.
Natural-language query text. Only used (and required) for apps backed by a Bedrock Knowledge Base — ignored by other backends.
Number of nearest results to return.
Backend-specific metadata filter.
Include each result's stored `payload` in the response.
```bash cURL theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/apps/app_123/vector/docs/query \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{ "vector": [0.011, -0.028, "..."], "topK": 5 }'
```
```json Response theme={null}
{
"results": [
{
"id": "chunk_1",
"score": 0.912,
"payload": { "text": "Refunds are processed within 5 business days." }
}
]
}
```
## Errors
| Status | Error | Applies to |
| ------ | ---------------------------------------------------------------------------------------- | ------------------------------- |
| 400 | `vectorSize must be a positive integer` | Create Index |
| 400 | `distance must be one of: Cosine, Dot, Euclid, Manhattan` | Create Index |
| 400 | `vectors must be a non-empty array` | Upsert |
| 400 | `vector must be a non-empty array of numbers (or query for Bedrock Knowledge Base apps)` | Query |
| 401 | `Unauthorized or app not found` | All |
| 404 | `Collection "{vectorName}" not found` | Query, Upsert, Get/Delete Index |
| 409 | `Collection "{vectorName}" already exists` | Create Index |
# Authentication
Source: https://docs.sundaypyjamas.com/authentication
Learn how to securely authenticate your requests using API keys and best practices
## Overview
The SundayPyjamas AI Suite API uses API keys for authentication. All API requests must include a valid API key in the Authorization header.
API keys provide secure access to the API while maintaining workspace-level isolation and usage tracking.
## API Key Format
API keys follow this specific format:
```
spj_ai_[64-character-random-string]
```
**Example:**
```
spj_ai_a1b2c3d4e5f6789012345678901234567890abcdef123456789012345678901234
```
API keys are only shown once during creation. Store them securely immediately after generation!
## Getting Your API Key
Follow these steps to generate your API key:
Navigate to your workspace settings in the SundayPyjamas platform.
Click on the "API" tab in your workspace settings.
Click "Generate API Key" to create a new key.
Give your API key a descriptive name to help you identify it later.
Copy the generated key immediately and store it securely. It won't be shown again!
## Making Authenticated Requests
Include your API key in the `Authorization` header with the `Bearer` scheme:
```javascript JavaScript theme={null}
const response = await fetch('https://suite.sundaypyjamas.com/api/v1/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer spj_ai_your_api_key_here',
'Content-Type': 'application/json'
},
body: JSON.stringify({
messages: [{ role: 'user', content: 'Hello!' }]
})
});
```
```python Python theme={null}
import requests
response = requests.post(
'https://suite.sundaypyjamas.com/api/v1/chat',
headers={
'Authorization': 'Bearer spj_ai_your_api_key_here',
'Content-Type': 'application/json'
},
json={
'messages': [{'role': 'user', 'content': 'Hello!'}]
}
)
```
```bash cURL theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/chat \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "Hello!"}]}'
```
## Permissions and Access Control
API keys inherit the permissions of the user who created them:
Keys can only access the workspace they were created in
Only workspace `owners` and `admins` can create/manage API keys
API usage counts toward your workspace token limit
Monitor API key usage through workspace analytics
### Auth by API Group
Most endpoints accept your workspace API key exactly as described above. A few groups differ:
| API | Accepts API key? | Notes |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------: | -------------------------------------------------------------------------------------------------------------------------- |
| [Chat](/chat-api), [Agents](/api-reference/agents/introduction), [Artifacts](/api-reference/artifacts/introduction), [Image](/api-reference/image/introduction), [Vector Store](/api-reference/vector-store/introduction), [Apps](/api-reference/apps/introduction), [Insights](/api-reference/insights/introduction) | ✅ | Standard `Bearer spj_ai_...` |
| [Apps](/api-reference/apps/introduction), [Vector Store](/api-reference/vector-store/introduction) | ✅ (+ widget token) | Also accept a workspace/app-scoped **widget token** as a fallback, for calls made directly from an embedded browser widget |
| [RAG](/api-reference/rag/introduction) | ❌ (currently) | Requires a Supabase **session** token — the same auth as the AI Suite web app. Not yet available via workspace API key |
| [Integration Packs](/api-reference/integrations/integration-packs) list, [Platform Tools](/api-reference/platform-tools/introduction) | Public | No authentication required — these are read-only catalog endpoints |
| [MCP Connectors](/api-reference/integrations/mcp-connectors) connect/disconnect/toggle | ❌ | Session only, and restricted to workspace `owner`/`admin` — not available via API key |
| [Storage](/api-reference/storage/introduction) | ✅ (+ session) | Accepts either an API key or a session, whichever you have |
## Security Best Practices
### ✅ Do
* Use environment variables or secure key management systems
* Never hardcode API keys in your source code
* Use different API keys for different applications/environments
```bash theme={null}
# .env file
SUNDAYPYJAMAS_API_KEY=spj_ai_your_api_key_here
```
```javascript theme={null}
// In your application
const apiKey = process.env.SUNDAYPYJAMAS_API_KEY;
```
* Generate new API keys periodically
* Deactivate old keys after replacement
* Use descriptive names to track key usage
```javascript theme={null}
// Example rotation strategy
const config = {
apiKey: process.env.SUNDAYPYJAMAS_API_KEY,
// Fallback key for seamless rotation
fallbackApiKey: process.env.SUNDAYPYJAMAS_FALLBACK_API_KEY
};
```
* Review usage analytics regularly
* Set up alerts for unusual activity
* Track token consumption patterns
Use workspace analytics to monitor which API keys are consuming the most tokens and identify optimization opportunities.
### ❌ Don't
API keys should never be included in frontend JavaScript, mobile apps, or any client-side code where users can access them.
```javascript theme={null}
// ❌ Never do this - API key exposed to users
const apiKey = 'spj_ai_your_api_key_here';
fetch('/api/chat', {
headers: { 'Authorization': `Bearer ${apiKey}` }
});
```
```javascript theme={null}
// ✅ Use a backend proxy instead
fetch('/api/chat-proxy', {
headers: { 'Authorization': `Bearer ${userSessionToken}` }
});
```
Use separate API keys for different applications to maintain better security and usage tracking.
```javascript theme={null}
// ❌ Shared key across projects
const sharedApiKey = 'spj_ai_shared_key';
// ✅ Separate keys per application
const config = {
webApp: process.env.WEBAPP_API_KEY,
mobileApp: process.env.MOBILE_API_KEY,
analytics: process.env.ANALYTICS_API_KEY
};
```
Use `.gitignore` to exclude files containing API keys and use environment variables instead.
```bash theme={null}
# .gitignore
.env
.env.local
.env.production
config/secrets.json
```
## API Key Management
### Creating API Keys
Go to your workspace settings in the SundayPyjamas web interface.
Click on the "API" tab to view key management options.
Click "Generate API Key" and optionally provide a descriptive name.
Copy the generated key immediately and store it in your secure key management system.
### Managing Existing Keys
In your workspace API settings, you can:
* **View all active API keys** with their names and creation dates
* **Delete keys** you no longer need
* **Monitor usage** for each individual key
* **Track token consumption** per API key
API key creation and management is done exclusively through the web interface to ensure proper security and access control.
## Rate Limits and Quotas
Maximum 10 active API keys per workspace
API usage counts toward workspace token quotas
Standard rate limiting applies to all API endpoints
Usage monitoring to ensure fair access for all users
## Error Responses
### Invalid API Key (401)
```json theme={null}
{
"error": "Invalid API key"
}
```
**Common causes:**
* API key doesn't exist or has been deleted
* Incorrect API key format
* Missing or malformed Authorization header
**Solution:**
* Verify your API key is correct and active
* Check the Authorization header format: `Bearer spj_ai_...`
* Generate a new API key if necessary
### Insufficient Permissions (403)
```json theme={null}
{
"error": "Insufficient permissions to create API keys"
}
```
**Cause:** User doesn't have required role (owner/admin) for API key management
**Solution:** Contact your workspace owner to grant appropriate permissions
### Token Limit Exceeded (403)
```json theme={null}
{
"error": "Token limit exceeded"
}
```
**Solutions:**
* Wait for your monthly token reset
* Upgrade your subscription plan
* Optimize prompts to reduce token usage
## Environment Variables Best Practices
### Local Development
Create a `.env` file for local development:
```bash theme={null}
# .env
SUNDAYPYJAMAS_API_KEY=spj_ai_your_development_key_here
SUNDAYPYJAMAS_API_URL=https://suite.sundaypyjamas.com/api/v1
```
### Production Deployment
Set environment variables in your deployment platform:
```bash theme={null}
vercel env add SUNDAYPYJAMAS_API_KEY
```
```bash theme={null}
netlify env:set SUNDAYPYJAMAS_API_KEY spj_ai_your_key_here
```
```bash theme={null}
heroku config:set SUNDAYPYJAMAS_API_KEY=spj_ai_your_key_here
```
```bash theme={null}
aws lambda update-function-configuration \
--function-name your-function \
--environment Variables='{SUNDAYPYJAMAS_API_KEY=spj_ai_your_key_here}'
```
### Access in Code
```javascript Node.js theme={null}
const apiKey = process.env.SUNDAYPYJAMAS_API_KEY;
if (!apiKey) {
throw new Error('SUNDAYPYJAMAS_API_KEY environment variable is required');
}
```
```python Python theme={null}
import os
api_key = os.getenv('SUNDAYPYJAMAS_API_KEY')
if not api_key:
raise ValueError("SUNDAYPYJAMAS_API_KEY environment variable is required")
```
```go Go theme={null}
package main
import (
"os"
"log"
)
func main() {
apiKey := os.Getenv("SUNDAYPYJAMAS_API_KEY")
if apiKey == "" {
log.Fatal("SUNDAYPYJAMAS_API_KEY environment variable is required")
}
}
```
## Testing Authentication
### Verify API Key
Test your API key with a simple request:
```bash cURL theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/chat \
-H "Authorization: Bearer ${SUNDAYPYJAMAS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "Test"}]}' \
-w "\nHTTP Status: %{http_code}\n"
```
```javascript JavaScript theme={null}
async function testApiKey() {
try {
const response = await fetch('https://suite.sundaypyjamas.com/api/v1/chat', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SUNDAYPYJAMAS_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
messages: [{ role: 'user', content: 'Test' }]
})
});
if (response.ok) {
console.log('✅ API key is valid');
} else {
console.log('❌ API key is invalid');
}
} catch (error) {
console.error('Connection error:', error);
}
}
```
```python Python theme={null}
import requests
def test_api_key():
try:
response = requests.post(
'https://suite.sundaypyjamas.com/api/v1/chat',
headers={
'Authorization': f'Bearer {os.getenv("SUNDAYPYJAMAS_API_KEY")}',
'Content-Type': 'application/json'
},
json={'messages': [{'role': 'user', 'content': 'Test'}]}
)
if response.ok:
print('✅ API key is valid')
else:
print('❌ API key is invalid')
except requests.exceptions.RequestException as e:
print(f'Connection error: {e}')
```
## Next Steps
Start making requests to the Chat API with your authenticated key
Understand usage limits and optimization strategies
View complete implementation examples with authentication
Learn how to handle authentication errors gracefully
# Chat API
Source: https://docs.sundaypyjamas.com/chat-api
Complete guide to the Chat API for conversational AI, content generation, and text completion
## Overview
The Chat API provides access to powerful language models for conversational AI, content generation, and text completion tasks. Built for developers who need reliable, scalable AI solutions with streaming responses.
All responses are streamed in real-time, providing a better user experience for conversational applications.
## Base URL
```
POST /api/v1/chat
```
## Authentication
All requests require a valid API key in the Authorization header:
```http theme={null}
Authorization: Bearer spj_ai_your_api_key_here
```
Learn more about [API key generation and management](/authentication).
## Request Format
### Required Headers
| Header | Value | Description |
| --------------- | ------------------------------ | ---------------------------------------------- |
| `Authorization` | `Bearer spj_ai_[your_api_key]` | **Required** - Your API key for authentication |
| `Content-Type` | `application/json` | **Required** - Must be set to application/json |
### Request Body
Array of conversation messages. Must contain at least one message.
AI model to use for generating responses. Optional parameter.
### Message Object
Each message in the `messages` array must contain:
The role of the message sender. Must be one of:
* `user` - Messages from the user/human
* `assistant` - Previous AI responses
* `system` - System instructions to guide AI behavior
The actual message content. Cannot be empty.
### Available Models
| Model | Description | Best For |
| ------------------------- | -------------------------------------------- | ------------------------------------ |
| `llama-3.3-70b-versatile` | High-quality general-purpose model (default) | Most use cases, balanced performance |
More models will be available soon! Check back for updates on specialized models.
## Response Format
The API returns a streaming text response with the following headers:
```http theme={null}
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
```
### Response Body
The response is streamed as text chunks. Concatenate all chunks to get the complete AI response.
```
Hello! Here's a professional email greeting:
Dear [Recipient's Name],
I hope this email finds you well. I wanted to reach out regarding...
```
## Examples
### Basic Chat Request
```bash cURL theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/chat \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "Hello! Can you help me write a professional email?"
}
]
}'
```
### Multi-turn Conversation
```bash cURL theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/chat \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "What is the capital of France?"
},
{
"role": "assistant",
"content": "The capital of France is Paris."
},
{
"role": "user",
"content": "What about its population?"
}
]
}'
```
### With System Message
```bash cURL theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/chat \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "system",
"content": "You are a professional copywriter specializing in marketing content."
},
{
"role": "user",
"content": "Write a product description for wireless headphones."
}
]
}'
```
### Specifying Model
```bash cURL theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/chat \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "Explain quantum computing in simple terms."
}
],
"model": "llama-3.3-70b-versatile"
}'
```
## Streaming Response Handling
The API returns responses as a stream of text chunks. Here's how to handle streaming in different languages:
```javascript JavaScript/Node.js theme={null}
const response = await fetch('https://suite.sundaypyjamas.com/api/v1/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer spj_ai_your_api_key_here',
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [
{ role: 'user', content: 'Hello!' }
]
})
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let fullResponse = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
fullResponse += chunk;
console.log(chunk); // Process each chunk as it arrives
}
console.log('Complete response:', fullResponse);
```
```python Python theme={null}
import requests
url = 'https://suite.sundaypyjamas.com/api/v1/chat'
headers = {
'Authorization': 'Bearer spj_ai_your_api_key_here',
'Content-Type': 'application/json',
}
data = {
'messages': [
{'role': 'user', 'content': 'Hello!'}
]
}
response = requests.post(url, headers=headers, json=data, stream=True)
response.raise_for_status()
full_response = ''
for chunk in response.iter_content(chunk_size=None, decode_unicode=True):
if chunk:
full_response += chunk
print(chunk, end='') # Process each chunk as it arrives
print(f'\nComplete response: {full_response}')
```
```go Go theme={null}
package main
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"net/http"
"strings"
)
func main() {
requestBody := map[string]interface{}{
"messages": []map[string]string{
{"role": "user", "content": "Hello!"},
},
}
jsonData, _ := json.Marshal(requestBody)
req, _ := http.NewRequest("POST", "https://suite.sundaypyjamas.com/api/v1/chat",
bytes.NewBuffer(jsonData))
req.Header.Set("Authorization", "Bearer spj_ai_your_api_key_here")
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
scanner := bufio.NewScanner(resp.Body)
var fullResponse strings.Builder
for scanner.Scan() {
chunk := scanner.Text()
fullResponse.WriteString(chunk)
fmt.Print(chunk) // Process each chunk as it arrives
}
fmt.Printf("\nComplete response: %s\n", fullResponse.String())
}
```
## Token Usage and Billing
Counted based on the total length of all messages in your request
Counted based on the length of the AI's response
Token usage is tracked and counted toward your workspace limits
Monitor usage through workspace analytics to optimize costs
### Token Estimation
Roughly **4 characters = 1 token** for English text. The API uses the same tokenization as the underlying model for precise counting.
**Example calculation:**
```
Input: "Hello, how are you today?" (26 characters) ≈ 7 tokens
Output: "I'm doing well, thank you for asking!" (36 characters) ≈ 9 tokens
Total: ~16 tokens
```
## Common Use Cases
### Content Generation
```json theme={null}
{
"messages": [
{
"role": "system",
"content": "You are a creative content writer specializing in blog posts."
},
{
"role": "user",
"content": "Write an introduction for a blog post about sustainable living tips."
}
]
}
```
```json theme={null}
{
"messages": [
{
"role": "system",
"content": "You are a professional email writer. Write clear, polite, and effective emails."
},
{
"role": "user",
"content": "Write a follow-up email for a job interview."
}
]
}
```
```json theme={null}
{
"messages": [
{
"role": "system",
"content": "You are an expert copywriter. Write compelling marketing copy that drives action."
},
{
"role": "user",
"content": "Create homepage copy for a productivity app targeting busy professionals."
}
]
}
```
### Code Assistance
```json theme={null}
{
"messages": [
{
"role": "system",
"content": "You are a helpful programming assistant. Provide clean, well-documented code."
},
{
"role": "user",
"content": "Write a Python function to calculate the fibonacci sequence."
}
]
}
```
```json theme={null}
{
"messages": [
{
"role": "system",
"content": "You are a senior software engineer. Provide constructive code reviews."
},
{
"role": "user",
"content": "Review this JavaScript function and suggest improvements: [code here]"
}
]
}
```
```json theme={null}
{
"messages": [
{
"role": "system",
"content": "You are a debugging expert. Help identify and fix code issues."
},
{
"role": "user",
"content": "I'm getting a TypeError in this Python code. Can you help me fix it?"
}
]
}
```
## Error Handling
### Common Errors
```json theme={null}
{
"error": "Invalid API key"
}
```
**Causes:**
* API key doesn't exist or has been deleted
* Incorrect API key format
* Missing Authorization header
**Solutions:**
* Verify your API key is correct and active
* Check the Authorization header format: `Bearer spj_ai_...`
* Generate a new API key if necessary
```json theme={null}
{
"error": "Messages array is required"
}
```
**Cause:** Request body doesn't include a `messages` array
**Solution:** Ensure your request includes a valid `messages` array with at least one message
```json theme={null}
{
"error": "Last message must have valid content"
}
```
**Causes:**
* Message missing required `content` field
* Empty content string
* Invalid role value
**Solution:** Ensure all messages have valid `role` and non-empty `content` fields
```json theme={null}
{
"error": "Token limit exceeded"
}
```
**Causes:**
* Workspace has exceeded monthly token quota
* Request is too large
**Solutions:**
* Wait for monthly token reset
* Upgrade subscription plan
* Optimize prompts to use fewer tokens
```json theme={null}
{
"error": "Rate limit exceeded"
}
```
**Cause:** Making requests too quickly
**Solutions:**
* Implement exponential backoff
* Reduce request frequency
* Use batch processing for multiple prompts
```json theme={null}
{
"error": "Failed to generate response"
}
```
**Causes:**
* AI model temporarily unavailable
* Server overload
* Temporary service disruption
**Solution:** Implement retry logic with exponential backoff
## Best Practices
### Message Design
Use specific, clear prompts for better results. Be explicit about what you want.
Use system messages to set context and guide AI behavior for specialized tasks.
Include relevant conversation history, but keep it concise to manage token usage.
Break complex requests into clear, structured instructions.
### Performance Optimization
Stream responses to provide better user experience in conversational applications.
```javascript theme={null}
// ✅ Good - Handle streaming for real-time display
const processStreamingResponse = async (response) => {
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
displayChunk(chunk); // Update UI immediately
}
};
```
Always implement proper error handling and retry logic.
```javascript theme={null}
// ✅ Good - Robust error handling
const makeRequest = async (messages, retries = 3) => {
for (let i = 0; i < retries; i++) {
try {
const response = await fetch('/api/v1/chat', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ messages })
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error);
}
return response;
} catch (error) {
if (i === retries - 1) throw error;
await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i)));
}
}
};
```
Track token usage to stay within limits and optimize costs.
```javascript theme={null}
// ✅ Good - Track usage
const estimateTokens = (text) => Math.ceil(text.length / 4);
const makeRequestWithTracking = async (messages) => {
const inputTokens = messages.reduce((sum, msg) => sum + estimateTokens(msg.content), 0);
console.log(`Estimated input tokens: ${inputTokens}`);
// Make request and track output tokens
const response = await makeRequest(messages);
const output = await readStreamingResponse(response);
const outputTokens = estimateTokens(output);
console.log(`Total tokens used: ${inputTokens + outputTokens}`);
return output;
};
```
### Security Considerations
Never expose API keys in client-side code. Always use backend proxies for frontend applications.
Validate and sanitize user inputs before sending to the API
Implement content filtering for user-generated prompts
Implement application-side rate limiting to prevent abuse
Monitor API usage patterns for unusual activity
## Rate Limits
For detailed information about rate limits, token usage, and optimization strategies, see the [Rate Limits guide](/rate-limits).
* **Token-based limits**: Usage counts toward workspace token quotas
* **Request rate**: Standard rate limiting applies to prevent abuse
* **Concurrent requests**: Multiple simultaneous requests are supported
* **Fair usage**: Excessive usage may be throttled
## Next Steps
View complete implementation examples in JavaScript, Python, and cURL
Learn about token usage, optimization, and billing
Comprehensive guide to error codes and recovery patterns
Complete API reference with schemas and interactive examples
# Development Setup
Source: https://docs.sundaypyjamas.com/development
Set up your local development environment for API integration and testing
## Overview
This guide covers setting up your development environment for integrating with the SundayPyjamas AI Suite API, including local testing, documentation preview, and development best practices.
This documentation can be run locally for contributions and updates.
## API Development Setup
### Environment Configuration
Generate an API key from your SundayPyjamas workspace:
1. Navigate to **Settings** → **API** tab
2. Click **"Generate API Key"**
3. Save the key securely
Create a `.env` file in your project:
```bash theme={null}
# API Configuration
SUNDAYPYJAMAS_API_KEY=spj_ai_your_api_key_here
SUNDAYPYJAMAS_API_URL=https://suite.sundaypyjamas.com/api/v1
# Development Settings
NODE_ENV=development
PORT=3000
```
Choose your preferred language and install required packages:
```bash theme={null}
# Basic setup (Node.js 18+)
npm init -y
npm install dotenv
# For advanced features
npm install node-fetch @types/node
```
```bash theme={null}
# Create virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install packages
pip install requests python-dotenv aiohttp
```
```bash theme={null}
# Initialize Go module
go mod init your-project
# No additional packages needed for basic HTTP requests
```
## Local Documentation Development
If you want to contribute to this documentation or run it locally:
* Node.js version 19 or higher
* Git for version control
```bash theme={null}
npm i -g mint
```
```bash theme={null}
# Clone the documentation repository
git clone
cd ai-suite-platform-docs
# Start local preview
mint dev
```
A local preview will be available at `http://localhost:3000`.
### Custom Ports
```bash theme={null}
# Use a different port
mint dev --port 3333
# Automatic port selection if 3000 is in use
# Port 3000 is already in use. Trying 3001 instead.
```
## Testing Your Integration
### Basic Connection Test
```javascript test-connection.js theme={null}
// test-connection.js
require('dotenv').config();
async function testConnection() {
const response = await fetch(`${process.env.SUNDAYPYJAMAS_API_URL}/chat`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SUNDAYPYJAMAS_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
messages: [{ role: 'user', content: 'Hello! This is a test.' }]
})
});
if (response.ok) {
console.log('✅ API connection successful');
const result = await response.text();
console.log('Response:', result);
} else {
console.error('❌ API connection failed:', response.status);
const error = await response.text();
console.error('Error:', error);
}
}
testConnection();
```
```python test_connection.py theme={null}
# test_connection.py
import os
import requests
from dotenv import load_dotenv
load_dotenv()
def test_connection():
try:
response = requests.post(
f"{os.getenv('SUNDAYPYJAMAS_API_URL')}/chat",
headers={
'Authorization': f"Bearer {os.getenv('SUNDAYPYJAMAS_API_KEY')}",
'Content-Type': 'application/json'
},
json={
'messages': [{'role': 'user', 'content': 'Hello! This is a test.'}]
}
)
if response.ok:
print('✅ API connection successful')
print('Response:', response.text)
else:
print(f'❌ API connection failed: {response.status_code}')
print('Error:', response.text)
except Exception as e:
print(f'❌ Connection error: {e}')
if __name__ == '__main__':
test_connection()
```
```bash test-connection.sh theme={null}
#!/bin/bash
# test-connection.sh
source .env
response=$(curl -s -w "%{http_code}" -o response.txt \
-X POST "$SUNDAYPYJAMAS_API_URL/chat" \
-H "Authorization: Bearer $SUNDAYPYJAMAS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "Hello! This is a test."}]}')
if [ "$response" = "200" ]; then
echo "✅ API connection successful"
echo "Response:"
cat response.txt
else
echo "❌ API connection failed: HTTP $response"
cat response.txt
fi
rm response.txt
```
### Integration Testing
Create a comprehensive test suite:
```javascript theme={null}
// tests/api-integration.test.js
const assert = require('assert');
require('dotenv').config();
describe('SundayPyjamas AI API Integration', () => {
const apiUrl = process.env.SUNDAYPYJAMAS_API_URL;
const apiKey = process.env.SUNDAYPYJAMAS_API_KEY;
it('should handle basic chat request', async () => {
const response = await fetch(`${apiUrl}/chat`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
messages: [{ role: 'user', content: 'Hello' }]
})
});
assert.strictEqual(response.status, 200);
const result = await response.text();
assert(result.length > 0);
});
it('should handle system messages', async () => {
const response = await fetch(`${apiUrl}/chat`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
messages: [
{ role: 'system', content: 'You are a helpful assistant.' },
{ role: 'user', content: 'What is 2+2?' }
]
})
});
assert.strictEqual(response.status, 200);
});
it('should return error for invalid API key', async () => {
const response = await fetch(`${apiUrl}/chat`, {
method: 'POST',
headers: {
'Authorization': 'Bearer invalid_key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
messages: [{ role: 'user', content: 'Hello' }]
})
});
assert.strictEqual(response.status, 401);
});
});
```
## Development Tools
### Recommended Extensions
* MDX extension for documentation
* Prettier for code formatting
* REST Client for API testing
* dotenv for environment variables
* Postman for interactive testing
* Insomnia for REST API testing
* HTTPie for command-line testing
* Thunder Client for VS Code
### Environment Validation
Create a validation script to check your setup:
```javascript theme={null}
// scripts/validate-env.js
require('dotenv').config();
const requiredEnvVars = [
'SUNDAYPYJAMAS_API_KEY',
'SUNDAYPYJAMAS_API_URL'
];
console.log('🔍 Validating environment configuration...\n');
let hasErrors = false;
requiredEnvVars.forEach(envVar => {
const value = process.env[envVar];
if (!value) {
console.error(`❌ Missing required environment variable: ${envVar}`);
hasErrors = true;
} else {
console.log(`✅ ${envVar}: ${value.substring(0, 20)}...`);
}
});
// Validate API key format
const apiKey = process.env.SUNDAYPYJAMAS_API_KEY;
if (apiKey && !apiKey.startsWith('spj_ai_')) {
console.error('❌ Invalid API key format. Should start with "spj_ai_"');
hasErrors = true;
}
// Validate URL format
const apiUrl = process.env.SUNDAYPYJAMAS_API_URL;
if (apiUrl && !apiUrl.startsWith('https://')) {
console.warn('⚠️ API URL should use HTTPS for security');
}
if (hasErrors) {
console.error('\n❌ Environment validation failed. Please fix the issues above.');
process.exit(1);
} else {
console.log('\n✅ Environment validation passed!');
}
```
## Documentation Tools
### Link Validation
```bash theme={null}
# Validate all links in documentation
mint broken-links
# Check specific files
mint broken-links --files quickstart.mdx,authentication.mdx
```
### Building for Production
```bash theme={null}
# Build documentation
mint build
# Deploy to production (requires setup)
mint deploy
```
## Troubleshooting
**Common solutions:**
* Verify API key format (`spj_ai_[64-characters]`)
* Check environment variable loading
* Ensure HTTPS is used for API URL
* Verify workspace permissions
```bash theme={null}
# Debug API key
echo "API Key: ${SUNDAYPYJAMAS_API_KEY:0:20}..."
echo "API URL: $SUNDAYPYJAMAS_API_URL"
```
**Common solutions:**
* Update documentation CLI: `npm update -g mint`
* Clear cache: `rm -rf ~/.mintlify && mint dev`
* Check MDX syntax in files
* Validate JSON configuration
```bash theme={null}
# Reinstall CLI if needed
npm remove -g mint
npm i -g mint
```
**Common solutions:**
* Check `.env` file location (project root)
* Verify no extra spaces in variable assignments
* Ensure proper dotenv loading in code
* Check for conflicting environment variables
```javascript theme={null}
// Debug environment loading
console.log('Current working directory:', process.cwd());
console.log('Environment variables loaded:', !!process.env.SUNDAYPYJAMAS_API_KEY);
```
## Next Steps
Start making your first API calls with example code
Explore complete implementation examples
Learn about API key management and security
Understand usage optimization and monitoring
Join our developer community for support, updates, and to share your implementations with other developers.
# Error Handling
Source: https://docs.sundaypyjamas.com/errors
Comprehensive guide to understanding and handling errors in the SundayPyjamas AI Suite API
## Overview
This guide covers all possible errors you may encounter when using the SundayPyjamas AI Suite API, along with best practices for handling them gracefully in your applications.
All API errors follow a consistent JSON format with human-readable error messages and appropriate HTTP status codes.
## Error Response Format
### Standard Error Format
All API errors return a consistent JSON structure:
```json theme={null}
{
"error": "Human-readable error message"
}
```
### Enhanced Error Format
Some errors may include additional fields for better debugging:
```json theme={null}
{
"error": "Detailed error message",
"code": "ERROR_CODE",
"details": {
"field": "additional context"
}
}
```
## HTTP Status Codes
### 400 Bad Request
Invalid request format or parameters.
```json theme={null}
{
"error": "Messages array is required"
}
```
**Cause:** Request body doesn't include a `messages` array
**Solution:** Ensure your request includes a valid `messages` array
```javascript ❌ Wrong theme={null}
const request = { model: "llama-3.3-70b-versatile" };
```
```javascript ✅ Correct theme={null}
const request = {
messages: [{ role: "user", content: "Hello" }],
model: "llama-3.3-70b-versatile"
};
```
```json theme={null}
{
"error": "Last message must have valid content"
}
```
**Cause:** Message missing required `content` field or empty content
**Solution:** Ensure all messages have valid `role` and `content` fields
```javascript ❌ Wrong theme={null}
const messages = [
{ role: "user" }, // Missing content
{ role: "user", content: "" } // Empty content
];
```
```javascript ✅ Correct theme={null}
const messages = [
{ role: "user", content: "Hello, how can you help me?" }
];
```
```json theme={null}
{
"error": "Invalid request body"
}
```
**Cause:** Malformed JSON or invalid Content-Type
**Solution:** Ensure proper JSON formatting and Content-Type header
```bash ❌ Wrong - Missing Content-Type theme={null}
curl -X POST /api/v1/chat \
-H "Authorization: Bearer API_KEY" \
-d '{"messages": [{"role": "user", "content": "Hello"}]}'
```
```bash ✅ Correct theme={null}
curl -X POST /api/v1/chat \
-H "Authorization: Bearer API_KEY" \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "Hello"}]}'
```
### 401 Unauthorized
Authentication issues with your API key.
```json theme={null}
{
"error": "Invalid API key"
}
```
**Common Causes:**
* API key doesn't exist or has been deleted
* API key format is incorrect
* API key has been deactivated
**Solutions:**
* Verify your API key is correct and active
* Check the key format: `spj_ai_[64-character-string]`
* Generate a new API key if needed
```javascript theme={null}
// Check API key format
function validateApiKeyFormat(key) {
return /^spj_ai_[a-zA-Z0-9_]{32,}$/.test(key);
}
if (!validateApiKeyFormat(apiKey)) {
console.error("Invalid API key format");
}
```
```json theme={null}
{
"error": "Invalid API key"
}
```
**Cause:** Missing or malformed Authorization header
**Solution:** Include proper Bearer token authorization
```javascript ❌ Wrong theme={null}
fetch('/api/v1/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ messages })
});
```
```javascript ✅ Correct theme={null}
fetch('/api/v1/chat', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ messages })
});
```
### 402 Payment Required
Returned by credit-metered endpoints — [Agents](/api-reference/agents/introduction), [Artifacts](/api-reference/artifacts/introduction), [Image](/api-reference/image/introduction), and [Apps chat](/api-reference/apps/chat) — when your workspace's credit balance is insufficient. This is separate from the legacy token-limit model used by the base [Chat API](/chat-api); see [Rate Limits](/rate-limits#credit-based-billing) for how the two relate.
```json theme={null}
{
"error": "Insufficient credits. Please purchase additional credits to continue.",
"code": "INSUFFICIENT_CREDITS"
}
```
**Cause:** Workspace credit balance is too low to cover the request (checked up front, and again mid-stream for long-running generations).
**Solution:** Top up credits from workspace billing settings, or catch `code: "INSUFFICIENT_CREDITS"` and prompt the user to do so.
### 403 Forbidden
Permission or limit issues.
```json theme={null}
{
"error": "Token limit exceeded"
}
```
**Cause:** Workspace has exceeded monthly token quota
**Solutions:**
* Wait for monthly reset
* Upgrade subscription plan
* Optimize prompts to use fewer tokens
```javascript theme={null}
async function handleTokenLimit() {
try {
const response = await chatAPI(messages);
return response;
} catch (error) {
if (error.message.includes('Token limit exceeded')) {
// Implement graceful degradation
return "I'm temporarily unavailable due to usage limits. Please try again later.";
}
throw error;
}
}
```
```json theme={null}
{
"error": "Insufficient permissions to create API keys"
}
```
**Cause:** User doesn't have required role (owner/admin) for API key management
**Solution:** Contact workspace owner to grant appropriate permissions
### 404 Not Found
Resource doesn't exist.
```json theme={null}
{
"error": "API key not found"
}
```
**Cause:** Attempting to delete or access a non-existent API key
**Solution:** Verify the API key ID is correct
**Cause:** Invalid API endpoint URL
**Solution:** Check the API documentation for correct endpoints
```javascript ❌ Wrong endpoint theme={null}
const response = await fetch('/api/v2/chat'); // v2 doesn't exist
```
```javascript ✅ Correct endpoint theme={null}
const response = await fetch('/api/v1/chat');
```
### 429 Too Many Requests
Rate limiting applied.
```json theme={null}
{
"error": "Rate limit exceeded"
}
```
**Cause:** Making requests too quickly
**Solution:** Implement exponential backoff and retry logic
```javascript theme={null}
async function retryWithBackoff(fn, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
if (error.status === 429 && attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s, 8s
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
}
```
### 500 Internal Server Error
Server-side issues.
```json theme={null}
{
"error": "Failed to generate response"
}
```
**Causes:**
* AI model temporarily unavailable
* Server overload
* Temporary service disruption
**Solution:** Implement retry logic with exponential backoff
```json theme={null}
{
"error": "Failed to initialize AI model"
}
```
**Cause:** AI service configuration issues
**Solution:** Retry the request; contact support if persistent
### 502 Bad Gateway
```json theme={null}
{
"error": "AI service is temporarily unavailable"
}
```
**Cause:** Upstream AI service is down
**Solution:** Retry with exponential backoff
### 503 Service Unavailable
```json theme={null}
{
"error": "Service temporarily unavailable"
}
```
**Cause:** Scheduled maintenance or high load
**Solution:** Wait and retry; check status page
## Error Handling Patterns
### Basic Error Handling
```javascript JavaScript theme={null}
async function basicChatRequest(messages) {
try {
const response = await fetch('/api/v1/chat', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ messages })
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(`API Error (${response.status}): ${errorData.error}`);
}
return await response.text();
} catch (error) {
console.error('Chat request failed:', error.message);
throw error;
}
}
```
```python Python theme={null}
import requests
def basic_chat_request(messages):
try:
response = requests.post(
'https://suite.sundaypyjamas.com/api/v1/chat',
headers={
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json'
},
json={'messages': messages}
)
response.raise_for_status()
return response.text
except requests.exceptions.HTTPError as e:
error_data = e.response.json() if e.response else {}
error_msg = error_data.get('error', 'Unknown error')
print(f'API Error ({e.response.status_code}): {error_msg}')
raise
except requests.exceptions.RequestException as e:
print(f'Request failed: {e}')
raise
```
### Comprehensive Error Handling
```javascript JavaScript - Robust Client theme={null}
class APIError extends Error {
constructor(message, status, code = null) {
super(message);
this.name = 'APIError';
this.status = status;
this.code = code;
}
}
class SundayPyjamasClient {
constructor(apiKey, apiUrl, maxRetries = 3) {
this.apiKey = apiKey;
this.apiUrl = apiUrl;
this.maxRetries = maxRetries;
}
async chat(messages, options = {}) {
const { timeout = 30000 } = options;
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
const response = await fetch(`${this.apiUrl}/chat`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ messages }),
signal: controller.signal
});
clearTimeout(timeoutId);
if (!response.ok) {
await this.handleErrorResponse(response, attempt);
}
return await this.readStreamingResponse(response);
} catch (error) {
if (error.name === 'AbortError') {
throw new APIError('Request timeout', 408);
}
if (attempt === this.maxRetries) {
throw error;
}
// Wait before retry (exponential backoff)
await this.wait(Math.pow(2, attempt) * 1000);
}
}
}
async handleErrorResponse(response, attempt) {
const errorData = await response.json().catch(() => ({ error: 'Unknown error' }));
switch (response.status) {
case 400:
throw new APIError(errorData.error, 400, 'BAD_REQUEST');
case 401:
throw new APIError('Invalid API key', 401, 'UNAUTHORIZED');
case 403:
if (errorData.error.includes('Token limit')) {
throw new APIError('Token limit exceeded', 403, 'TOKEN_LIMIT_EXCEEDED');
}
throw new APIError(errorData.error, 403, 'FORBIDDEN');
case 404:
throw new APIError('Endpoint not found', 404, 'NOT_FOUND');
case 429:
if (attempt < this.maxRetries) {
// Don't throw immediately for rate limits, let retry logic handle it
await this.wait(Math.pow(2, attempt + 1) * 1000);
return;
}
throw new APIError('Rate limit exceeded', 429, 'RATE_LIMITED');
case 500:
case 502:
case 503:
case 504:
if (attempt < this.maxRetries) {
// Retry server errors
return;
}
throw new APIError('Server error', response.status, 'SERVER_ERROR');
default:
throw new APIError(errorData.error || 'Unknown error', response.status);
}
}
async readStreamingResponse(response) {
const reader = response.body.getReader();
const decoder = new TextDecoder();
let fullResponse = '';
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
fullResponse += chunk;
}
return fullResponse;
} finally {
reader.releaseLock();
}
}
wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
// Usage with comprehensive error handling
const client = new SundayPyjamasClient(apiKey, apiUrl);
try {
const response = await client.chat(messages);
console.log('Success:', response);
} catch (error) {
if (error instanceof APIError) {
switch (error.code) {
case 'UNAUTHORIZED':
console.error('Authentication failed. Check your API key.');
break;
case 'TOKEN_LIMIT_EXCEEDED':
console.error('Monthly token limit reached. Upgrade plan or wait for reset.');
break;
case 'RATE_LIMITED':
console.error('Too many requests. Please slow down.');
break;
case 'BAD_REQUEST':
console.error('Invalid request:', error.message);
break;
default:
console.error('API error:', error.message);
}
} else {
console.error('Unexpected error:', error.message);
}
}
```
```python Python - Robust Client theme={null}
import requests
import time
import random
from typing import List, Dict, Optional
class APIError(Exception):
def __init__(self, message: str, status_code: int, error_code: Optional[str] = None):
super().__init__(message)
self.status_code = status_code
self.error_code = error_code
class SundayPyjamasClient:
def __init__(self, api_key: str, api_url: str, max_retries: int = 3):
self.api_key = api_key
self.api_url = api_url
self.max_retries = max_retries
self.session = requests.Session()
self.session.headers.update({
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json'
})
def chat(self, messages: List[Dict], **kwargs) -> str:
for attempt in range(self.max_retries + 1):
try:
response = self.session.post(
f'{self.api_url}/chat',
json={'messages': messages, **kwargs},
timeout=30,
stream=True
)
self._handle_error_response(response, attempt)
# Read streaming response
full_response = ''
for chunk in response.iter_content(chunk_size=None, decode_unicode=True):
if chunk:
full_response += chunk
return full_response
except APIError as e:
if e.status_code in [400, 401, 403, 404] or attempt == self.max_retries:
raise e
# Exponential backoff with jitter
delay = (2 ** attempt) + random.uniform(0, 1)
time.sleep(delay)
except requests.exceptions.RequestException as e:
if attempt == self.max_retries:
raise APIError(f"Request failed: {str(e)}", 0)
delay = (2 ** attempt) + random.uniform(0, 1)
time.sleep(delay)
def _handle_error_response(self, response: requests.Response, attempt: int):
if response.ok:
return
try:
error_data = response.json()
error_message = error_data.get('error', 'Unknown error')
except ValueError:
error_message = f"HTTP {response.status_code} error"
error_codes = {
400: 'BAD_REQUEST',
401: 'UNAUTHORIZED',
403: 'FORBIDDEN',
404: 'NOT_FOUND',
429: 'RATE_LIMITED',
500: 'SERVER_ERROR',
502: 'BAD_GATEWAY',
503: 'SERVICE_UNAVAILABLE'
}
error_code = error_codes.get(response.status_code)
# For server errors and rate limits, allow retries
if response.status_code in [429, 500, 502, 503, 504] and attempt < self.max_retries:
return # Will be retried
raise APIError(error_message, response.status_code, error_code)
# Usage
client = SundayPyjamasClient(api_key, api_url)
try:
response = client.chat([{'role': 'user', 'content': 'Hello'}])
print(response)
except APIError as e:
if e.error_code == 'UNAUTHORIZED':
print("Invalid API key. Please check your configuration.")
elif e.error_code == 'FORBIDDEN':
print("Access denied. Check permissions or token limits.")
elif e.error_code == 'RATE_LIMITED':
print("Rate limited. Please wait before making more requests.")
else:
print(f"API error: {e}")
except Exception as e:
print(f"Unexpected error: {e}")
```
## Error Recovery Strategies
### Graceful Degradation
```javascript theme={null}
class ChatService {
constructor(apiKey) {
this.client = new SundayPyjamasClient(apiKey);
this.fallbackResponses = {
'TOKEN_LIMIT_EXCEEDED': 'I\'m temporarily unavailable due to usage limits. Please try again later.',
'RATE_LIMITED': 'I\'m receiving too many requests. Please wait a moment and try again.',
'SERVER_ERROR': 'I\'m experiencing technical difficulties. Please try again in a few minutes.',
'UNAUTHORIZED': 'There\'s an authentication issue. Please contact support.'
};
}
async chat(messages, options = {}) {
try {
return await this.client.chat(messages, options);
} catch (error) {
if (error instanceof APIError && this.fallbackResponses[error.code]) {
return this.fallbackResponses[error.code];
}
// Log error for debugging but provide user-friendly message
console.error('Chat service error:', error);
return 'I\'m currently unavailable. Please try again later.';
}
}
}
```
### Circuit Breaker Pattern
```javascript theme={null}
class CircuitBreaker {
constructor(failureThreshold = 5, resetTimeout = 60000) {
this.failureThreshold = failureThreshold;
this.resetTimeout = resetTimeout;
this.failureCount = 0;
this.lastFailureTime = null;
this.state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN
}
async execute(fn) {
if (this.state === 'OPEN') {
if (Date.now() - this.lastFailureTime > this.resetTimeout) {
this.state = 'HALF_OPEN';
} else {
throw new Error('Circuit breaker is OPEN');
}
}
try {
const result = await fn();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
onSuccess() {
this.failureCount = 0;
this.state = 'CLOSED';
}
onFailure() {
this.failureCount++;
this.lastFailureTime = Date.now();
if (this.failureCount >= this.failureThreshold) {
this.state = 'OPEN';
}
}
}
// Usage
const circuitBreaker = new CircuitBreaker(3, 30000); // 3 failures, 30s timeout
async function robustChatRequest(messages) {
try {
return await circuitBreaker.execute(async () => {
return await client.chat(messages);
});
} catch (error) {
if (error.message === 'Circuit breaker is OPEN') {
return 'Service is temporarily unavailable. Please try again later.';
}
throw error;
}
}
```
### Retry with Jitter
```javascript theme={null}
async function retryWithJitter(fn, maxRetries = 3, baseDelay = 1000) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
if (attempt === maxRetries) {
throw error;
}
// Don't retry on client errors (4xx)
if (error instanceof APIError && error.status >= 400 && error.status < 500) {
throw error;
}
// Exponential backoff with jitter
const delay = baseDelay * Math.pow(2, attempt);
const jitter = Math.random() * 0.1 * delay; // 10% jitter
await new Promise(resolve => setTimeout(resolve, delay + jitter));
}
}
}
```
## Debugging Tips
### Enable Detailed Logging
```javascript theme={null}
class DebugClient extends SundayPyjamasClient {
async chat(messages, options = {}) {
const requestId = Math.random().toString(36).substring(7);
console.log(`[${requestId}] Starting chat request`, {
messageCount: messages.length,
totalChars: messages.reduce((sum, m) => sum + m.content.length, 0),
options
});
try {
const startTime = Date.now();
const response = await super.chat(messages, options);
const duration = Date.now() - startTime;
console.log(`[${requestId}] Chat request successful`, {
duration: `${duration}ms`,
responseLength: response.length
});
return response;
} catch (error) {
console.error(`[${requestId}] Chat request failed`, {
error: error.message,
status: error.status,
code: error.code
});
throw error;
}
}
}
```
### Test Error Scenarios
```bash Test Invalid API Key theme={null}
curl -X POST "${API_URL}/chat" \
-H "Authorization: Bearer invalid_key" \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "test"}]}'
```
```bash Test Missing Messages theme={null}
curl -X POST "${API_URL}/chat" \
-H "Authorization: Bearer ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{}'
```
```bash Test Malformed JSON theme={null}
curl -X POST "${API_URL}/chat" \
-H "Authorization: Bearer ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "test"'
```
### Error Monitoring
```javascript theme={null}
class ErrorMonitor {
constructor() {
this.errors = [];
this.errorCounts = new Map();
}
logError(error, context = {}) {
const errorInfo = {
timestamp: new Date().toISOString(),
message: error.message,
status: error.status,
code: error.code,
context
};
this.errors.push(errorInfo);
// Count error types
const errorKey = `${error.status}-${error.code}`;
this.errorCounts.set(errorKey, (this.errorCounts.get(errorKey) || 0) + 1);
// Alert on high error rates
if (this.errorCounts.get(errorKey) > 10) {
console.warn(`High error rate detected: ${errorKey}`);
}
}
getErrorSummary() {
return {
totalErrors: this.errors.length,
errorBreakdown: Object.fromEntries(this.errorCounts),
recentErrors: this.errors.slice(-10)
};
}
}
const errorMonitor = new ErrorMonitor();
// Use in your error handling
catch (error) {
errorMonitor.logError(error, { userId, requestId });
// ... handle error
}
```
## Best Practices Summary
Implement comprehensive error handling for all API calls
Retry with increasing delays for transient errors
Provide fallback responses when the API is unavailable
Track error frequencies to identify and fix issues
Validate requests before sending to avoid 400 errors
Protect API keys and handle auth errors appropriately
Implement detailed logging for troubleshooting
Use circuit breakers to prevent cascade failures
## Next Steps
Learn about API key management and security
Understand usage limits and optimization
Complete API documentation with examples
See robust implementations with error handling
# cURL Examples
Source: https://docs.sundaypyjamas.com/examples/curl
Complete cURL examples for testing and interacting with the SundayPyjamas AI Suite API
## Overview
This guide provides comprehensive cURL examples for testing and automating interactions with the SundayPyjamas AI Suite API. Perfect for command-line testing, shell scripting, and CI/CD pipelines.
All examples include proper error handling, output formatting, and can be easily adapted for automation scripts.
## Basic Setup
### Environment Variables
Set up your environment variables for easier testing:
```bash theme={null}
export SUNDAYPYJAMAS_API_KEY="spj_ai_your_api_key_here"
export SUNDAYPYJAMAS_API_URL="https://suite.sundaypyjamas.com/api/v1"
```
```cmd theme={null}
set SUNDAYPYJAMAS_API_KEY=spj_ai_your_api_key_here
set SUNDAYPYJAMAS_API_URL=https://suite.sundaypyjamas.com/api/v1
```
```powershell theme={null}
$env:SUNDAYPYJAMAS_API_KEY="spj_ai_your_api_key_here"
$env:SUNDAYPYJAMAS_API_URL="https://suite.sundaypyjamas.com/api/v1"
```
### Verify Setup
Test your environment variables:
```bash theme={null}
echo "API Key: $SUNDAYPYJAMAS_API_KEY"
echo "API URL: $SUNDAYPYJAMAS_API_URL"
```
## Basic Chat Request
### Simple Single Message
```bash theme={null}
curl -X POST "${SUNDAYPYJAMAS_API_URL}/chat" \
-H "Authorization: Bearer ${SUNDAYPYJAMAS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "Hello! Can you help me write a professional email?"
}
]
}'
```
### With Specific Model
```bash theme={null}
curl -X POST "${SUNDAYPYJAMAS_API_URL}/chat" \
-H "Authorization: Bearer ${SUNDAYPYJAMAS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "Explain quantum computing in simple terms."
}
],
"model": "llama-3.3-70b-versatile"
}'
```
### Multi-turn Conversation
```bash theme={null}
curl -X POST "${SUNDAYPYJAMAS_API_URL}/chat" \
-H "Authorization: Bearer ${SUNDAYPYJAMAS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "What is the capital of France?"
},
{
"role": "assistant",
"content": "The capital of France is Paris."
},
{
"role": "user",
"content": "What about its population?"
}
]
}'
```
### With System Message
```bash theme={null}
curl -X POST "${SUNDAYPYJAMAS_API_URL}/chat" \
-H "Authorization: Bearer ${SUNDAYPYJAMAS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "system",
"content": "You are a professional copywriter specializing in marketing content. Write compelling, persuasive copy that drives action."
},
{
"role": "user",
"content": "Write a product description for wireless noise-canceling headphones."
}
]
}'
```
## Content Generation Examples
### Blog Post Generation
```bash theme={null}
curl -X POST "${SUNDAYPYJAMAS_API_URL}/chat" \
-H "Authorization: Bearer ${SUNDAYPYJAMAS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "system",
"content": "You are an expert blog writer. Create engaging, well-structured blog posts with clear headers, practical insights, and strong conclusions."
},
{
"role": "user",
"content": "Write a 1000-word blog post about sustainable web development practices for web developers. Include practical tips and examples."
}
]
}' | tee blog_post.txt
```
### Email Generation
```bash theme={null}
curl -X POST "${SUNDAYPYJAMAS_API_URL}/chat" \
-H "Authorization: Bearer ${SUNDAYPYJAMAS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "system",
"content": "You are a professional email writer. Write clear, effective emails with proper structure and tone."
},
{
"role": "user",
"content": "Write a follow-up email to a potential client after a product demo. Include next steps and a call to action."
}
]
}' | tee follow_up_email.txt
```
### Marketing Copy
```bash theme={null}
curl -X POST "${SUNDAYPYJAMAS_API_URL}/chat" \
-H "Authorization: Bearer ${SUNDAYPYJAMAS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "system",
"content": "You are an expert copywriter. Write compelling marketing copy that focuses on benefits, creates urgency, and drives conversions."
},
{
"role": "user",
"content": "Write landing page copy for an AI-powered project management tool targeting small business owners. Include headline, benefits, and call-to-action."
}
]
}' | tee landing_page_copy.txt
```
## Streaming Response Handling
### Basic Streaming
```bash theme={null}
# Stream response and display in real-time
curl -X POST "${SUNDAYPYJAMAS_API_URL}/chat" \
-H "Authorization: Bearer ${SUNDAYPYJAMAS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "Write a creative short story about AI and humans working together."
}
]
}' --no-buffer
```
### Save Streaming Response
```bash theme={null}
# Stream to file
curl -X POST "${SUNDAYPYJAMAS_API_URL}/chat" \
-H "Authorization: Bearer ${SUNDAYPYJAMAS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "Create a comprehensive guide on API best practices."
}
]
}' --no-buffer -o api_guide.txt
```
## Error Testing and Debugging
### Test Invalid API Key
```bash theme={null}
curl -X POST "${SUNDAYPYJAMAS_API_URL}/chat" \
-H "Authorization: Bearer invalid_key" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "Hello"
}
]
}' -w "\nHTTP Status: %{http_code}\n"
```
**Expected response:**
```json theme={null}
{
"error": "Invalid API key"
}
HTTP Status: 401
```
### Test Missing Messages
```bash theme={null}
curl -X POST "${SUNDAYPYJAMAS_API_URL}/chat" \
-H "Authorization: Bearer ${SUNDAYPYJAMAS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{}' \
-w "\nHTTP Status: %{http_code}\n"
```
**Expected response:**
```json theme={null}
{
"error": "Messages array is required"
}
HTTP Status: 400
```
### Test Invalid Message Format
```bash theme={null}
curl -X POST "${SUNDAYPYJAMAS_API_URL}/chat" \
-H "Authorization: Bearer ${SUNDAYPYJAMAS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user"
}
]
}' -w "\nHTTP Status: %{http_code}\n"
```
### Debug Request with Verbose Output
```bash theme={null}
curl -X POST "${SUNDAYPYJAMAS_API_URL}/chat" \
-H "Authorization: Bearer ${SUNDAYPYJAMAS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "Test message"
}
]
}' \
-v \
-w "\n\nResponse Time: %{time_total}s\nHTTP Code: %{http_code}\n"
```
## Advanced Examples
### Batch Processing with Shell Script
Create a file called `batch_process.sh`:
```bash theme={null}
#!/bin/bash
# batch_process.sh - Process multiple prompts
API_KEY="${SUNDAYPYJAMAS_API_KEY}"
API_URL="${SUNDAYPYJAMAS_API_URL}"
if [ -z "$API_KEY" ] || [ -z "$API_URL" ]; then
echo "Error: Please set SUNDAYPYJAMAS_API_KEY and SUNDAYPYJAMAS_API_URL environment variables"
exit 1
fi
# Array of prompts
prompts=(
"Write a haiku about technology"
"Explain the concept of recursion in programming"
"Create a product description for a smart watch"
"Write a motivational quote about learning"
)
# Process each prompt
for i in "${!prompts[@]}"; do
echo "Processing prompt $((i+1))/${#prompts[@]}: ${prompts[i]}"
curl -X POST "${API_URL}/chat" \
-H "Authorization: Bearer ${API_KEY}" \
-H "Content-Type: application/json" \
-d "{
\"messages\": [
{
\"role\": \"user\",
\"content\": \"${prompts[i]}\"
}
]
}" \
-o "output_$((i+1)).txt" \
-s
echo "Saved to output_$((i+1)).txt"
echo "---"
done
echo "Batch processing complete!"
```
Make it executable and run:
```bash theme={null}
chmod +x batch_process.sh
./batch_process.sh
```
### Content Generation Pipeline
Create `content_pipeline.sh`:
```bash theme={null}
#!/bin/bash
# Content generation pipeline
API_KEY="${SUNDAYPYJAMAS_API_KEY}"
API_URL="${SUNDAYPYJAMAS_API_URL}"
generate_content() {
local content_type="$1"
local prompt="$2"
local filename="$3"
echo "Generating ${content_type}..."
curl -X POST "${API_URL}/chat" \
-H "Authorization: Bearer ${API_KEY}" \
-H "Content-Type: application/json" \
-d "{
\"messages\": [
{
\"role\": \"system\",
\"content\": \"You are a professional ${content_type} writer. Create high-quality, engaging content.\"
},
{
\"role\": \"user\",
\"content\": \"${prompt}\"
}
]
}" \
-o "${filename}" \
-s
echo "✓ ${content_type} saved to ${filename}"
}
# Generate different types of content
generate_content "blog post" \
"Write a 800-word blog post about the benefits of remote work for software teams" \
"blog_post.txt"
generate_content "email" \
"Write a professional email announcing a new product launch to customers" \
"announcement_email.txt"
generate_content "social media" \
"Create an engaging LinkedIn post about work-life balance tips for professionals" \
"linkedin_post.txt"
generate_content "marketing copy" \
"Write compelling homepage copy for a productivity app targeting busy professionals" \
"homepage_copy.txt"
echo "Content generation pipeline complete!"
echo "Generated files:"
ls -la *.txt
```
### Response Analysis Script
Create `analyze_response.sh`:
```bash theme={null}
#!/bin/bash
# Analyze API response
API_KEY="${SUNDAYPYJAMAS_API_KEY}"
API_URL="${SUNDAYPYJAMAS_API_URL}"
prompt="$1"
if [ -z "$prompt" ]; then
echo "Usage: $0 \"Your prompt here\""
exit 1
fi
echo "Analyzing response for prompt: $prompt"
echo "=================================="
# Make request and capture timing
start_time=$(date +%s.%N)
response=$(curl -X POST "${API_URL}/chat" \
-H "Authorization: Bearer ${API_KEY}" \
-H "Content-Type: application/json" \
-d "{
\"messages\": [
{
\"role\": \"user\",
\"content\": \"${prompt}\"
}
]
}" \
-s -w "%{http_code}|%{time_total}|%{size_download}")
end_time=$(date +%s.%N)
# Parse response
http_code=$(echo "$response" | tail -c 20 | cut -d'|' -f1)
time_total=$(echo "$response" | tail -c 20 | cut -d'|' -f2)
size_download=$(echo "$response" | tail -c 20 | cut -d'|' -f3)
content=$(echo "$response" | head -c -20)
# Calculate metrics
duration=$(echo "$end_time - $start_time" | bc)
word_count=$(echo "$content" | wc -w)
char_count=$(echo "$content" | wc -c)
echo "HTTP Status: $http_code"
echo "Response Time: ${time_total}s"
echo "Total Duration: ${duration}s"
echo "Response Size: $size_download bytes"
echo "Word Count: $word_count"
echo "Character Count: $char_count"
echo ""
echo "Response Content:"
echo "=================="
echo "$content"
# Save detailed analysis
cat > analysis_$(date +%Y%m%d_%H%M%S).json << EOF
{
"prompt": "$prompt",
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"metrics": {
"http_status": $http_code,
"response_time": $time_total,
"total_duration": $duration,
"response_size": $size_download,
"word_count": $word_count,
"character_count": $char_count
},
"response": $(echo "$content" | jq -Rs .)
}
EOF
echo ""
echo "Analysis saved to analysis_$(date +%Y%m%d_%H%M%S).json"
```
Usage:
```bash theme={null}
chmod +x analyze_response.sh
./analyze_response.sh "Explain machine learning in simple terms"
```
## Testing and Debugging
### Check API Connectivity
```bash theme={null}
# Test basic connectivity
curl -X POST "${SUNDAYPYJAMAS_API_URL}/chat" \
-H "Authorization: Bearer ${SUNDAYPYJAMAS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "Hello"
}
]
}' \
-v \
-w "\n\nResponse Time: %{time_total}s\nHTTP Code: %{http_code}\n"
```
### Debug Headers and Request
```bash theme={null}
# Show detailed request/response information
curl -X POST "${SUNDAYPYJAMAS_API_URL}/chat" \
-H "Authorization: Bearer ${SUNDAYPYJAMAS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "Test message"
}
]
}' \
--trace-ascii trace.log \
-v
```
### Performance Testing
```bash theme={null}
# Time multiple requests
for i in {1..5}; do
echo "Request $i:"
time curl -X POST "${SUNDAYPYJAMAS_API_URL}/chat" \
-H "Authorization: Bearer ${SUNDAYPYJAMAS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "What is 2+2?"
}
]
}' \
-s -o /dev/null \
-w "Response time: %{time_total}s\n"
echo "---"
done
```
### Load Testing Script
```bash theme={null}
#!/bin/bash
# load_test.sh - Simple load testing
API_KEY="${SUNDAYPYJAMAS_API_KEY}"
API_URL="${SUNDAYPYJAMAS_API_URL}"
CONCURRENT_REQUESTS=5
TOTAL_REQUESTS=50
REQUEST_DELAY=0.1
echo "Starting load test:"
echo "- Concurrent requests: $CONCURRENT_REQUESTS"
echo "- Total requests: $TOTAL_REQUESTS"
echo "- Delay between requests: ${REQUEST_DELAY}s"
echo "=================================="
make_request() {
local request_id=$1
local start_time=$(date +%s.%N)
response=$(curl -X POST "${API_URL}/chat" \
-H "Authorization: Bearer ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "Hello from request '$request_id'"
}
]
}' \
-s -w "%{http_code}|%{time_total}")
local end_time=$(date +%s.%N)
local http_code=$(echo "$response" | tail -c 10 | cut -d'|' -f1)
local time_total=$(echo "$response" | tail -c 10 | cut -d'|' -f2)
local duration=$(echo "$end_time - $start_time" | bc)
echo "Request $request_id: HTTP $http_code, Time: ${time_total}s"
}
# Run load test
for ((i=1; i<=TOTAL_REQUESTS; i++)); do
make_request $i &
# Limit concurrent requests
if (( i % CONCURRENT_REQUESTS == 0 )); then
wait
sleep $REQUEST_DELAY
fi
done
wait
echo "Load test complete!"
```
## JSON Processing with jq
### Extract Specific Data
```bash theme={null}
# Process response with jq
curl -X POST "${SUNDAYPYJAMAS_API_URL}/chat" \
-H "Authorization: Bearer ${SUNDAYPYJAMAS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "Generate a JSON object with user data"
}
]
}' -s | jq -r '.'
```
### Format Error Responses
```bash theme={null}
# Handle errors gracefully
handle_api_response() {
local response="$1"
local http_code="$2"
if [ "$http_code" = "200" ]; then
echo "✅ Success:"
echo "$response"
else
echo "❌ Error (HTTP $http_code):"
echo "$response" | jq -r '.error // "Unknown error"'
fi
}
# Usage
response=$(curl -X POST "${SUNDAYPYJAMAS_API_URL}/chat" \
-H "Authorization: Bearer invalid_key" \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "test"}]}' \
-s -w "%{http_code}")
http_code=$(echo "$response" | tail -c 4)
content=$(echo "$response" | head -c -4)
handle_api_response "$content" "$http_code"
```
## Best Practices
### 1. Always Handle Errors
```bash theme={null}
# Check for errors in responses
response=$(curl -X POST "${SUNDAYPYJAMAS_API_URL}/chat" \
-H "Authorization: Bearer ${SUNDAYPYJAMAS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "Hello"}]}' \
-s -w "%{http_code}")
http_code=$(echo "$response" | tail -c 4)
content=$(echo "$response" | head -c -4)
if [ "$http_code" -ne 200 ]; then
echo "Error: HTTP $http_code"
echo "$content"
exit 1
fi
echo "$content"
```
### 2. Use Proper JSON Escaping
```bash theme={null}
# For prompts with quotes or special characters
prompt="Say \"Hello, World!\" and explain what programming is."
curl -X POST "${SUNDAYPYJAMAS_API_URL}/chat" \
-H "Authorization: Bearer ${SUNDAYPYJAMAS_API_KEY}" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg content "$prompt" '{
messages: [
{
role: "user",
content: $content
}
]
}')"
```
### 3. Set Timeouts
```bash theme={null}
# Set connection and max time limits
curl -X POST "${SUNDAYPYJAMAS_API_URL}/chat" \
-H "Authorization: Bearer ${SUNDAYPYJAMAS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "Hello"}]}' \
--connect-timeout 30 \
--max-time 300
```
### 4. Create Reusable Functions
```bash theme={null}
# api_helpers.sh - Reusable API functions
send_chat_request() {
local messages="$1"
local model="${2:-llama-3.3-70b-versatile}"
curl -X POST "${SUNDAYPYJAMAS_API_URL}/chat" \
-H "Authorization: Bearer ${SUNDAYPYJAMAS_API_KEY}" \
-H "Content-Type: application/json" \
-d "{
\"messages\": $messages,
\"model\": \"$model\"
}" \
-s
}
send_simple_message() {
local content="$1"
local role="${2:-user}"
local messages=$(jq -n --arg role "$role" --arg content "$content" '[{
role: $role,
content: $content
}]')
send_chat_request "$messages"
}
# Usage
source api_helpers.sh
response=$(send_simple_message "Write a haiku about coding")
echo "$response"
```
## CI/CD Integration
### GitHub Actions Example
```yaml theme={null}
# .github/workflows/api-test.yml
name: API Integration Test
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test-api:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Test API Connection
env:
SUNDAYPYJAMAS_API_KEY: ${{ secrets.SUNDAYPYJAMAS_API_KEY }}
SUNDAYPYJAMAS_API_URL: ${{ secrets.SUNDAYPYJAMAS_API_URL }}
run: |
response=$(curl -X POST "${SUNDAYPYJAMAS_API_URL}/chat" \
-H "Authorization: Bearer ${SUNDAYPYJAMAS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "API test"}]}' \
-s -w "%{http_code}")
http_code=$(echo "$response" | tail -c 4)
content=$(echo "$response" | head -c -4)
if [ "$http_code" -eq 200 ]; then
echo "✅ API test passed"
echo "Response: $content"
else
echo "❌ API test failed: HTTP $http_code"
echo "$content"
exit 1
fi
```
### Docker Integration
```dockerfile theme={null}
# Dockerfile for API testing
FROM curlimages/curl:latest
WORKDIR /app
COPY test_scripts/ .
RUN chmod +x *.sh
CMD ["./run_api_tests.sh"]
```
```bash theme={null}
# run_api_tests.sh
#!/bin/sh
echo "Running SundayPyjamas API tests..."
# Test basic connectivity
echo "Testing API connectivity..."
if ./test_connectivity.sh; then
echo "✅ Connectivity test passed"
else
echo "❌ Connectivity test failed"
exit 1
fi
# Test content generation
echo "Testing content generation..."
if ./test_content_generation.sh; then
echo "✅ Content generation test passed"
else
echo "❌ Content generation test failed"
exit 1
fi
echo "All tests passed! 🎉"
```
## Beyond Chat: Other APIs
```bash theme={null}
# Create a managed agent
curl -X POST https://suite.sundaypyjamas.com/api/v1/managed-agents/agents \
-H "Authorization: Bearer $SUNDAYPYJAMAS_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Research Assistant", "config": { "capabilities": { "deliverables": { "enabled": true } } } }'
# Submit a task to it
curl -X POST https://suite.sundaypyjamas.com/api/v1/managed-agents/agents/AGENT_ID/runs \
-H "Authorization: Bearer $SUNDAYPYJAMAS_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "task": "Summarize the attached Q2 report and list top 3 risks." }'
```
See the full [Agents API reference](/api-reference/agents/introduction).
```bash theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/rag/query \
-H "Authorization: Bearer $SUPABASE_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "query": "What were the top risks in Q2?", "workspace_id": "'"$WORKSPACE_ID"'" }'
```
See the full [RAG API reference](/api-reference/rag/introduction).
```bash theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/image \
-H "Authorization: Bearer $SUNDAYPYJAMAS_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "prompt": "A minimalist mountain logo, flat design, teal and white" }'
```
See the full [Image API reference](/api-reference/image/introduction).
```bash theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/artifacts/generate \
-H "Authorization: Bearer $SUNDAYPYJAMAS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"scope": { "level": "organization", "label": "Acme Inc." },
"input": { "aggregates": { "recordCount": 4210, "metrics": { "churnRate": 0.042 } } }
}'
```
See the full [Artifacts API reference](/api-reference/artifacts/introduction).
## Next Steps
Learn JavaScript/TypeScript implementations with React components
Explore comprehensive Python implementations with async support
Understand usage guidelines and optimization strategies
Review API key management and security best practices
All cURL examples can be easily adapted for automation, testing, and integration into CI/CD pipelines. The shell scripts provide a foundation for building more complex workflows.
# JavaScript/TypeScript Examples
Source: https://docs.sundaypyjamas.com/examples/javascript
Complete examples for integrating the SundayPyjamas AI Suite API with JavaScript and TypeScript applications
## Overview
This guide provides comprehensive JavaScript and TypeScript examples for integrating with the SundayPyjamas AI Suite API. All examples include proper error handling, type safety, and production-ready patterns.
Examples work in both Node.js and browser environments. TypeScript definitions are included for better development experience.
## Installation
### Basic Setup
No additional packages are required for basic usage with modern environments:
```bash theme={null}
# For Node.js < 18, install fetch polyfill
npm install node-fetch
# For TypeScript support
npm install @types/node
# Optional: For React examples
npm install react @types/react
```
### Environment Variables
Create a `.env` file for secure configuration:
```bash theme={null}
# .env
SUNDAYPYJAMAS_API_KEY=spj_ai_your_api_key_here
SUNDAYPYJAMAS_API_URL=https://suite.sundaypyjamas.com/api/v1
```
### TypeScript Configuration
```typescript theme={null}
// config.ts
export const config = {
apiKey: process.env.SUNDAYPYJAMAS_API_KEY!,
apiUrl: process.env.SUNDAYPYJAMAS_API_URL || 'https://suite.sundaypyjamas.com/api/v1'
};
if (!config.apiKey) {
throw new Error('SUNDAYPYJAMAS_API_KEY environment variable is required');
}
```
## Basic Examples
### Simple Chat Request
```typescript TypeScript theme={null}
interface ChatMessage {
role: 'user' | 'assistant' | 'system';
content: string;
}
interface ChatRequest {
messages: ChatMessage[];
model?: string;
}
async function sendChatMessage(messages: ChatMessage[]): Promise {
const response = await fetch(`${config.apiUrl}/chat`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${config.apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages,
model: 'llama-3.3-70b-versatile'
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(`API Error: ${error.error}`);
}
// Read the streaming response
const reader = response.body!.getReader();
const decoder = new TextDecoder();
let fullResponse = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
fullResponse += chunk;
}
return fullResponse;
}
// Usage
const messages: ChatMessage[] = [
{ role: 'user', content: 'Write a professional email about a project update.' }
];
try {
const response = await sendChatMessage(messages);
console.log('AI Response:', response);
} catch (error) {
console.error('Error:', error.message);
}
```
```javascript JavaScript theme={null}
async function sendChatMessage(messages) {
const response = await fetch(`${process.env.SUNDAYPYJAMAS_API_URL}/chat`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SUNDAYPYJAMAS_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages,
model: 'llama-3.3-70b-versatile'
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(`API Error: ${error.error}`);
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let fullResponse = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
fullResponse += decoder.decode(value);
}
return fullResponse;
}
// Usage
const messages = [
{ role: 'user', content: 'Write a professional email about a project update.' }
];
try {
const response = await sendChatMessage(messages);
console.log('AI Response:', response);
} catch (error) {
console.error('Error:', error.message);
}
```
## Streaming Responses
### Real-time Streaming
```typescript theme={null}
async function streamChatResponse(
messages: ChatMessage[],
onChunk: (chunk: string) => void,
onComplete: (fullResponse: string) => void,
onError: (error: Error) => void
): Promise {
try {
const response = await fetch(`${config.apiUrl}/chat`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${config.apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ messages })
});
if (!response.ok) {
const error = await response.json();
throw new Error(`API Error: ${error.error}`);
}
const reader = response.body!.getReader();
const decoder = new TextDecoder();
let fullResponse = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
fullResponse += chunk;
onChunk(chunk);
}
onComplete(fullResponse);
} catch (error) {
onError(error as Error);
}
}
// Usage
const messages: ChatMessage[] = [
{ role: 'user', content: 'Explain quantum computing in simple terms.' }
];
await streamChatResponse(
messages,
(chunk) => {
process.stdout.write(chunk); // Print each chunk as it arrives
},
(fullResponse) => {
console.log('\n\nComplete response received!');
console.log('Full response length:', fullResponse.length);
},
(error) => {
console.error('Streaming error:', error.message);
}
);
```
## React Chat Component
### Complete Chat Interface
```tsx theme={null}
// ChatInterface.tsx
import React, { useState, useRef, useEffect } from 'react';
interface Message {
role: 'user' | 'assistant';
content: string;
timestamp: Date;
}
interface ChatInterfaceProps {
apiKey: string;
apiUrl: string;
}
const ChatInterface: React.FC = ({ apiKey, apiUrl }) => {
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [streamingResponse, setStreamingResponse] = useState('');
const messagesEndRef = useRef(null);
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
};
useEffect(scrollToBottom, [messages, streamingResponse]);
const sendMessage = async () => {
if (!input.trim() || isLoading) return;
const userMessage: Message = {
role: 'user',
content: input.trim(),
timestamp: new Date()
};
setMessages(prev => [...prev, userMessage]);
setInput('');
setIsLoading(true);
setStreamingResponse('');
const chatMessages = [
...messages.map(m => ({ role: m.role, content: m.content })),
{ role: 'user', content: userMessage.content }
];
try {
await streamChatResponse(
chatMessages,
(chunk) => {
setStreamingResponse(prev => prev + chunk);
},
(fullResponse) => {
const assistantMessage: Message = {
role: 'assistant',
content: fullResponse,
timestamp: new Date()
};
setMessages(prev => [...prev, assistantMessage]);
setStreamingResponse('');
setIsLoading(false);
},
(error) => {
console.error('Chat error:', error);
setIsLoading(false);
setStreamingResponse('');
// Show error message to user
}
);
} catch (error) {
console.error('Chat error:', error);
setIsLoading(false);
}
};
return (
{messages.map((message, index) => (
{message.content}
{message.timestamp.toLocaleTimeString()}
))}
{/* Show streaming response */}
{streamingResponse && (
);
};
export default ChatInterface;
```
## Content Generation
### Blog Post Generator
```typescript theme={null}
interface BlogPostRequest {
topic: string;
tone: 'professional' | 'casual' | 'academic' | 'creative';
length: 'short' | 'medium' | 'long';
audience: string;
}
class ContentGenerator {
private apiKey: string;
private apiUrl: string;
constructor(apiKey: string, apiUrl: string) {
this.apiKey = apiKey;
this.apiUrl = apiUrl;
}
async generateBlogPost(request: BlogPostRequest): Promise {
const systemPrompt = `You are a professional content writer. Write engaging, well-structured blog posts tailored to the specified audience and tone. Include:
- Compelling introduction
- Clear section headers
- Practical insights
- Strong conclusion`;
const lengthGuide = {
short: '500-800 words',
medium: '1000-1500 words',
long: '2000-3000 words'
};
const userPrompt = `Write a ${request.tone} blog post about "${request.topic}" for ${request.audience}.
Target length: ${lengthGuide[request.length]}.`;
const messages: ChatMessage[] = [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: userPrompt }
];
return await this.sendRequest(messages);
}
async generateEmail(purpose: string, recipient: string, tone: string): Promise {
const systemPrompt = `You are a professional email writer. Write clear, effective emails that achieve their purpose while maintaining the appropriate tone.`;
const userPrompt = `Write a ${tone} email to ${recipient} about ${purpose}. Include:
- Clear subject line
- Proper greeting
- Concise body
- Appropriate closing`;
const messages: ChatMessage[] = [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: userPrompt }
];
return await this.sendRequest(messages);
}
async generateMarketingCopy(product: string, audience: string, format: string): Promise {
const systemPrompt = `You are an expert copywriter specializing in conversion-focused marketing content. Write compelling copy that drives action.`;
const userPrompt = `Write ${format} marketing copy for "${product}" targeting ${audience}. Focus on benefits, create urgency, and include a clear call-to-action.`;
const messages: ChatMessage[] = [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: userPrompt }
];
return await this.sendRequest(messages);
}
private async sendRequest(messages: ChatMessage[]): Promise {
const response = await fetch(`${this.apiUrl}/chat`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ messages })
});
if (!response.ok) {
const error = await response.json();
throw new Error(`API Error: ${error.error}`);
}
const reader = response.body!.getReader();
const decoder = new TextDecoder();
let fullResponse = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
fullResponse += decoder.decode(value);
}
return fullResponse;
}
}
// Usage
const generator = new ContentGenerator(config.apiKey, config.apiUrl);
// Generate a blog post
const blogPost = await generator.generateBlogPost({
topic: 'Sustainable Web Development Practices',
tone: 'professional',
length: 'medium',
audience: 'web developers'
});
console.log('Generated blog post:', blogPost);
// Generate an email
const email = await generator.generateEmail(
'following up on our meeting about the new project timeline',
'project stakeholders',
'professional'
);
console.log('Generated email:', email);
```
## Error Handling
### Robust Error Handling
```typescript theme={null}
class APIError extends Error {
constructor(
message: string,
public status: number,
public code?: string
) {
super(message);
this.name = 'APIError';
}
}
class SundayPyjamasClient {
private apiKey: string;
private apiUrl: string;
private maxRetries: number;
constructor(apiKey: string, apiUrl: string, maxRetries = 3) {
this.apiKey = apiKey;
this.apiUrl = apiUrl;
this.maxRetries = maxRetries;
}
async chat(messages: ChatMessage[], options?: { model?: string }): Promise {
return await this.retryRequest(async () => {
const response = await fetch(`${this.apiUrl}/chat`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages,
model: options?.model || 'llama-3.3-70b-versatile'
})
});
await this.handleResponse(response);
const reader = response.body!.getReader();
const decoder = new TextDecoder();
let fullResponse = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
fullResponse += decoder.decode(value);
}
return fullResponse;
});
}
private async handleResponse(response: Response): Promise {
if (response.ok) return;
const errorData = await response.json().catch(() => ({ error: 'Unknown error' }));
switch (response.status) {
case 401:
throw new APIError('Invalid API key', 401, 'INVALID_API_KEY');
case 403:
throw new APIError('Token limit exceeded or insufficient permissions', 403, 'FORBIDDEN');
case 429:
throw new APIError('Rate limit exceeded', 429, 'RATE_LIMITED');
case 500:
throw new APIError('Internal server error', 500, 'INTERNAL_ERROR');
default:
throw new APIError(errorData.error || 'Unknown error', response.status);
}
}
private async retryRequest(request: () => Promise): Promise {
let lastError: Error;
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
try {
return await request();
} catch (error) {
lastError = error as Error;
// Don't retry on authentication or permission errors
if (error instanceof APIError && [401, 403].includes(error.status)) {
throw error;
}
// Don't retry on the last attempt
if (attempt === this.maxRetries) {
throw error;
}
// Exponential backoff
const delay = Math.pow(2, attempt) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
console.log(`Retrying request (attempt ${attempt + 2}/${this.maxRetries + 1})...`);
}
}
throw lastError!;
}
}
// Usage with error handling
const client = new SundayPyjamasClient(config.apiKey, config.apiUrl);
try {
const response = await client.chat([
{ role: 'user', content: 'Hello!' }
]);
console.log('Response:', response);
} catch (error) {
if (error instanceof APIError) {
console.error(`API Error (${error.status}):`, error.message);
switch (error.code) {
case 'INVALID_API_KEY':
console.log('Please check your API key configuration');
break;
case 'RATE_LIMITED':
console.log('Please wait before making more requests');
break;
case 'FORBIDDEN':
console.log('Check your token usage or permissions');
break;
default:
console.log('An unexpected error occurred');
}
} else {
console.error('Unexpected error:', error.message);
}
}
```
## Node.js Server Example
### Express.js API Server
```typescript theme={null}
// server.ts
import express from 'express';
import { SundayPyjamasClient } from './client';
import { streamChatResponse } from './streaming';
const app = express();
app.use(express.json());
const client = new SundayPyjamasClient(
process.env.SUNDAYPYJAMAS_API_KEY!,
process.env.SUNDAYPYJAMAS_API_URL!
);
// Chat endpoint
app.post('/api/chat', async (req, res) => {
try {
const { messages, model } = req.body;
if (!messages || !Array.isArray(messages)) {
return res.status(400).json({ error: 'Messages array is required' });
}
const response = await client.chat(messages, { model });
res.json({ response });
} catch (error) {
console.error('Chat error:', error);
if (error instanceof APIError) {
res.status(error.status).json({ error: error.message });
} else {
res.status(500).json({ error: 'Internal server error' });
}
}
});
// Streaming chat endpoint
app.post('/api/chat/stream', async (req, res) => {
try {
const { messages, model } = req.body;
if (!messages || !Array.isArray(messages)) {
return res.status(400).json({ error: 'Messages array is required' });
}
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
await streamChatResponse(
messages,
(chunk) => {
res.write(chunk);
},
(fullResponse) => {
res.end();
},
(error) => {
res.write(`Error: ${error.message}`);
res.end();
}
);
} catch (error) {
console.error('Streaming error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
```
## Browser Usage
### Frontend Integration
```html theme={null}
AI Chat Interface
SundayPyjamas AI Assistant
Hello! I'm your AI assistant. How can I help you today?
```
## Testing
### Unit Tests
```typescript theme={null}
// tests/client.test.ts
import { SundayPyjamasClient, APIError } from '../src/client';
// Mock fetch
global.fetch = jest.fn();
describe('SundayPyjamasClient', () => {
let client: SundayPyjamasClient;
const mockApiKey = 'spj_ai_test_key';
const mockApiUrl = 'https://test-api.com/v1';
beforeEach(() => {
client = new SundayPyjamasClient(mockApiKey, mockApiUrl);
jest.clearAllMocks();
});
it('should make successful chat request', async () => {
const mockResponse = 'Hello! This is a test response.';
(fetch as jest.Mock).mockResolvedValueOnce({
ok: true,
body: {
getReader: () => ({
read: jest.fn()
.mockResolvedValueOnce({ done: false, value: new TextEncoder().encode(mockResponse) })
.mockResolvedValueOnce({ done: true })
})
}
});
const messages = [{ role: 'user', content: 'Hello' }];
const result = await client.chat(messages);
expect(result).toBe(mockResponse);
expect(fetch).toHaveBeenCalledWith(
`${mockApiUrl}/chat`,
expect.objectContaining({
method: 'POST',
headers: {
'Authorization': `Bearer ${mockApiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ messages, model: 'llama-3.3-70b-versatile' })
})
);
});
it('should handle API errors', async () => {
(fetch as jest.Mock).mockResolvedValueOnce({
ok: false,
status: 401,
json: () => Promise.resolve({ error: 'Invalid API key' })
});
const messages = [{ role: 'user', content: 'Hello' }];
await expect(client.chat(messages)).rejects.toThrow(APIError);
await expect(client.chat(messages)).rejects.toThrow('Invalid API key');
});
it('should retry on server errors', async () => {
// Mock first request to fail, second to succeed
(fetch as jest.Mock)
.mockResolvedValueOnce({
ok: false,
status: 500,
json: () => Promise.resolve({ error: 'Server error' })
})
.mockResolvedValueOnce({
ok: true,
body: {
getReader: () => ({
read: jest.fn()
.mockResolvedValueOnce({ done: false, value: new TextEncoder().encode('Success!') })
.mockResolvedValueOnce({ done: true })
})
}
});
const messages = [{ role: 'user', content: 'Hello' }];
const result = await client.chat(messages);
expect(result).toBe('Success!');
expect(fetch).toHaveBeenCalledTimes(2);
});
});
```
## Beyond Chat: Other APIs
```javascript theme={null}
const apiKey = process.env.SUNDAYPYJAMAS_API_KEY;
// Create a managed agent
const agentRes = await fetch('https://suite.sundaypyjamas.com/api/v1/managed-agents/agents', {
method: 'POST',
headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
name: 'Research Assistant',
config: { capabilities: { deliverables: { enabled: true } } },
}),
});
const { agent } = await agentRes.json();
// Submit a task
const runRes = await fetch(
`https://suite.sundaypyjamas.com/api/v1/managed-agents/agents/${agent.id}/runs`,
{
method: 'POST',
headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ task: 'Summarize the attached Q2 report and list top 3 risks.' }),
}
);
const { runId } = await runRes.json();
```
See the full [Agents API reference](/api-reference/agents/introduction).
```javascript theme={null}
const response = await fetch('https://suite.sundaypyjamas.com/api/v1/image', {
method: 'POST',
headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ prompt: 'A minimalist mountain logo, flat design, teal and white' }),
});
const { image } = await response.json(); // base64 PNG
```
See the full [Image API reference](/api-reference/image/introduction).
```javascript theme={null}
const response = await fetch('https://suite.sundaypyjamas.com/api/v1/artifacts/generate', {
method: 'POST',
headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
scope: { level: 'organization', label: 'Acme Inc.' },
input: { aggregates: { recordCount: 4210, metrics: { churnRate: 0.042 } } },
}),
});
const { data } = await response.json();
console.log(data.outputSnapshot.llm.executiveSummary);
```
See the full [Artifacts API reference](/api-reference/artifacts/introduction).
## Next Steps
Explore comprehensive Python implementations with async support
Command-line examples for testing and automation
Learn about optimization and usage tracking
Master robust error handling patterns
All JavaScript examples can be easily adapted for TypeScript by adding type annotations. The examples shown include full TypeScript support for better development experience.
# Code Examples Overview
Source: https://docs.sundaypyjamas.com/examples/overview
Ready-to-use implementations for integrating the SundayPyjamas AI Suite API in multiple programming languages
## Overview
This section provides comprehensive code examples and implementations for integrating with the SundayPyjamas AI Suite API. All examples are production-ready and include proper error handling, authentication, and best practices.
Choose your preferred programming language to get started with complete, runnable examples.
## Available Examples
Node.js and browser examples with React components, streaming responses, and TypeScript support
Comprehensive Python integration with async support, batch processing, and CLI tools
Command-line examples for testing, automation, and shell scripting
## Quick Start Examples
Get started immediately with these simple examples:
```javascript theme={null}
const response = await fetch('https://suite.sundaypyjamas.com/api/v1/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer spj_ai_your_api_key_here',
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [{ role: 'user', content: 'Hello!' }]
})
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let result = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
result += decoder.decode(value);
}
console.log(result);
```
```python theme={null}
import requests
response = requests.post(
'https://suite.sundaypyjamas.com/api/v1/chat',
headers={'Authorization': 'Bearer spj_ai_your_api_key_here'},
json={'messages': [{'role': 'user', 'content': 'Hello!'}]},
stream=True
)
full_response = ''
for chunk in response.iter_content(chunk_size=None, decode_unicode=True):
if chunk:
full_response += chunk
print(full_response)
```
```bash theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/chat \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "Hello!"}]}'
```
## Example Applications
### Content Generation Tools
Create engaging blog posts with customizable tone, length, and audience targeting.
**Features:**
* Dynamic prompt generation
* Content structure templates
* SEO optimization
* Multiple output formats
[View JavaScript Implementation →](/examples/javascript#content-generation)
[View Python Implementation →](/examples/python#content-generation-tools)
Generate professional emails for various purposes and audiences.
**Features:**
* Template-based generation
* Tone customization
* Recipient personalization
* Follow-up sequences
[View Examples →](/examples/javascript#content-generation)
Create compelling marketing copy that drives conversions.
**Features:**
* Audience targeting
* A/B testing variants
* Call-to-action optimization
* Brand voice consistency
[View Examples →](/examples/python#content-generation-tools)
### Interactive Applications
Build conversational AI interfaces with real-time streaming.
**Features:**
* Real-time streaming responses
* Message history management
* React component examples
* Mobile-responsive design
[View React Implementation →](/examples/javascript#chat-interface-component)
Create powerful CLI applications for batch processing and automation.
**Features:**
* Interactive chat mode
* Batch processing
* Progress tracking
* Configuration management
[View CLI Implementation →](/examples/python#cli-tool-example)
Full-featured web applications with API integration.
**Features:**
* Express.js server examples
* Authentication middleware
* Error handling
* Rate limiting
[View Server Examples →](/examples/javascript#nodejs-server-example)
### Advanced Use Cases
Process multiple requests efficiently with queue management.
**Features:**
* Concurrent request handling
* Progress tracking
* Error recovery
* Performance optimization
[View Implementation →](/examples/python#batch-processing)
Real-time streaming responses for better user experience.
**Features:**
* Progressive response display
* Cancellation support
* Error handling
* Performance monitoring
[View Examples →](/examples/javascript#streaming-responses)
Automated content generation workflows.
**Features:**
* Multi-step processing
* Quality control
* Template management
* Output formatting
[View Pipeline Examples →](/examples/curl#content-generation-pipeline)
## Implementation Features
All examples include these production-ready features:
Comprehensive error handling with retry logic and graceful degradation
Secure API key management and best practices
Built-in rate limiting and usage optimization
Real-time streaming responses for better UX
Full TypeScript definitions and type safety
Unit tests and integration examples
Inline documentation and usage examples
Usage tracking and performance monitoring
## Best Practices Covered
### Security
* Environment variable management
* API key protection
* Input validation and sanitization
* HTTPS enforcement
### Performance
* Connection pooling
* Request batching
* Caching strategies
* Memory optimization
### Reliability
* Exponential backoff
* Circuit breaker patterns
* Timeout handling
* Graceful degradation
### Monitoring
* Usage tracking
* Error logging
* Performance metrics
* Alert systems
## Environment Setup
### Prerequisites
```json package.json theme={null}
{
"name": "sundaypyjamas-ai-examples",
"version": "1.0.0",
"type": "module",
"dependencies": {
"node-fetch": "^3.0.0",
"@types/node": "^20.0.0"
},
"devDependencies": {
"typescript": "^5.0.0",
"@types/jest": "^29.0.0",
"jest": "^29.0.0"
}
}
```
**Installation:**
```bash theme={null}
npm install
# or
yarn install
```
```txt requirements.txt theme={null}
requests>=2.28.0
python-dotenv>=1.0.0
asyncio>=3.7.0
aiohttp>=3.8.0
click>=8.0.0
rich>=13.0.0
```
**Installation:**
```bash theme={null}
pip install -r requirements.txt
# or
poetry install
```
```bash .env theme={null}
# API Configuration
SUNDAYPYJAMAS_API_KEY=spj_ai_your_api_key_here
SUNDAYPYJAMAS_API_URL=https://suite.sundaypyjamas.com/api/v1
# Optional Configuration
MAX_RETRIES=3
REQUEST_TIMEOUT=30
RATE_LIMIT_PER_MINUTE=60
```
## Testing Your Setup
Verify your environment is configured correctly:
```javascript test-setup.js theme={null}
// test-setup.js
import fetch from 'node-fetch';
const API_KEY = process.env.SUNDAYPYJAMAS_API_KEY;
const API_URL = process.env.SUNDAYPYJAMAS_API_URL;
async function testSetup() {
if (!API_KEY) {
console.error('❌ SUNDAYPYJAMAS_API_KEY not set');
return;
}
try {
const response = await fetch(`${API_URL}/chat`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
messages: [{ role: 'user', content: 'Test connection' }]
})
});
if (response.ok) {
console.log('✅ API connection successful');
} else {
console.error('❌ API connection failed:', response.status);
}
} catch (error) {
console.error('❌ Connection error:', error.message);
}
}
testSetup();
```
```python test_setup.py theme={null}
# test_setup.py
import os
import requests
from dotenv import load_dotenv
load_dotenv()
API_KEY = os.getenv('SUNDAYPYJAMAS_API_KEY')
API_URL = os.getenv('SUNDAYPYJAMAS_API_URL')
def test_setup():
if not API_KEY:
print('❌ SUNDAYPYJAMAS_API_KEY not set')
return
try:
response = requests.post(
f'{API_URL}/chat',
headers={
'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json'
},
json={
'messages': [{'role': 'user', 'content': 'Test connection'}]
}
)
if response.ok:
print('✅ API connection successful')
else:
print(f'❌ API connection failed: {response.status_code}')
except Exception as error:
print(f'❌ Connection error: {error}')
if __name__ == '__main__':
test_setup()
```
```bash test-setup.sh theme={null}
#!/bin/bash
# test-setup.sh
if [ -z "$SUNDAYPYJAMAS_API_KEY" ]; then
echo "❌ SUNDAYPYJAMAS_API_KEY not set"
exit 1
fi
response=$(curl -s -w "%{http_code}" -o /dev/null \
-X POST "$SUNDAYPYJAMAS_API_URL/chat" \
-H "Authorization: Bearer $SUNDAYPYJAMAS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "Test connection"}]}')
if [ "$response" = "200" ]; then
echo "✅ API connection successful"
else
echo "❌ API connection failed: HTTP $response"
fi
```
## Contributing Examples
We welcome contributions to improve and expand our examples:
Share your own implementations and use cases
Help us fix bugs and improve documentation
Suggest new examples or improvements
Connect with other developers using the API
## Next Steps
Explore comprehensive JavaScript/TypeScript implementations
View detailed Python examples with async support
Command-line examples for testing and automation
Complete API documentation with schemas
These examples focus on the Chat API. For Agents, RAG, Artifacts, Image, and Vector Store — see the language-specific "Beyond Chat" sections at the end of each [JavaScript](/examples/javascript#beyond-chat-other-apis), [Python](/examples/python#beyond-chat-other-apis), and [cURL](/examples/curl#beyond-chat-other-apis) guide, or jump straight to the [full API Reference](/api-reference/agents/introduction).
All examples are designed to be copied and adapted for your specific use case. Feel free to modify them as needed for your applications.
# Python Examples
Source: https://docs.sundaypyjamas.com/examples/python
Complete examples for integrating the SundayPyjamas AI Suite API with Python applications
## Overview
This guide provides comprehensive Python examples for integrating with the SundayPyjamas AI Suite API, including synchronous and asynchronous implementations, batch processing, and CLI tools.
Examples include both sync and async implementations with comprehensive error handling and production-ready patterns.
## Installation
Install required packages:
```bash theme={null}
pip install requests
pip install python-dotenv # For environment variables
pip install asyncio # For async examples (Python 3.7+)
pip install aiohttp # For async HTTP requests
pip install click # For CLI examples
pip install rich # For beautiful CLI output
```
## Basic Setup
### Environment Variables
Create a `.env` file:
```bash theme={null}
# .env
SUNDAYPYJAMAS_API_KEY=spj_ai_your_api_key_here
SUNDAYPYJAMAS_API_URL=https://suite.sundaypyjamas.com/api/v1
```
### Configuration
```python theme={null}
# config.py
import os
from dotenv import load_dotenv
load_dotenv()
API_KEY = os.getenv('SUNDAYPYJAMAS_API_KEY')
API_URL = os.getenv('SUNDAYPYJAMAS_API_URL', 'https://suite.sundaypyjamas.com/api/v1')
if not API_KEY:
raise ValueError("SUNDAYPYJAMAS_API_KEY environment variable is required")
```
## Simple Chat Request
### Synchronous Implementation
```python theme={null}
import requests
from typing import List, Dict, Optional
from config import API_KEY, API_URL
class SundayPyjamasClient:
def __init__(self, api_key: str, api_url: str):
self.api_key = api_key
self.api_url = api_url
self.session = requests.Session()
self.session.headers.update({
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json'
})
def chat(self, messages: List[Dict[str, str]], model: str = 'llama-3.3-70b-versatile') -> str:
"""
Send a chat request and return the complete response.
Args:
messages: List of message dictionaries with 'role' and 'content' keys
model: AI model to use
Returns:
Complete AI response as string
"""
response = self.session.post(
f'{self.api_url}/chat',
json={
'messages': messages,
'model': model
},
stream=True
)
response.raise_for_status()
# Read streaming response
full_response = ''
for chunk in response.iter_content(chunk_size=None, decode_unicode=True):
if chunk:
full_response += chunk
return full_response
# Usage
client = SundayPyjamasClient(API_KEY, API_URL)
messages = [
{'role': 'user', 'content': 'Write a professional email about a project update.'}
]
try:
response = client.chat(messages)
print("AI Response:")
print(response)
except requests.exceptions.RequestException as e:
print(f"Error: {e}")
```
### Streaming Response Handler
```python theme={null}
import requests
from typing import List, Dict, Callable, Optional
def stream_chat_response(
client: SundayPyjamasClient,
messages: List[Dict[str, str]],
on_chunk: Callable[[str], None],
on_complete: Callable[[str], None],
on_error: Callable[[Exception], None],
model: str = 'llama-3.3-70b-versatile'
) -> None:
"""
Stream chat response with callbacks for real-time processing.
Args:
client: SundayPyjamasClient instance
messages: List of message dictionaries
on_chunk: Callback for each response chunk
on_complete: Callback when response is complete
on_error: Callback for error handling
model: AI model to use
"""
try:
response = client.session.post(
f'{client.api_url}/chat',
json={
'messages': messages,
'model': model
},
stream=True
)
response.raise_for_status()
full_response = ''
for chunk in response.iter_content(chunk_size=None, decode_unicode=True):
if chunk:
full_response += chunk
on_chunk(chunk)
on_complete(full_response)
except Exception as e:
on_error(e)
# Usage with streaming
def print_chunk(chunk: str):
print(chunk, end='', flush=True)
def print_complete(full_response: str):
print(f"\n\nComplete! Total length: {len(full_response)} characters")
def handle_error(error: Exception):
print(f"\nError occurred: {error}")
messages = [
{'role': 'user', 'content': 'Explain quantum computing in simple terms.'}
]
stream_chat_response(
client,
messages,
on_chunk=print_chunk,
on_complete=print_complete,
on_error=handle_error
)
```
## Async Implementation
### Async Client with aiohttp
```python theme={null}
import asyncio
import aiohttp
from typing import List, Dict, Optional, Callable
from config import API_KEY, API_URL
class AsyncSundayPyjamasClient:
def __init__(self, api_key: str, api_url: str):
self.api_key = api_key
self.api_url = api_url
self.headers = {
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json'
}
async def chat(self, messages: List[Dict[str, str]], model: str = 'llama-3.3-70b-versatile') -> str:
"""
Send async chat request and return complete response.
"""
async with aiohttp.ClientSession() as session:
async with session.post(
f'{self.api_url}/chat',
json={
'messages': messages,
'model': model
},
headers=self.headers
) as response:
response.raise_for_status()
full_response = ''
async for chunk in response.content.iter_chunked(1024):
chunk_text = chunk.decode('utf-8')
full_response += chunk_text
return full_response
async def stream_chat(
self,
messages: List[Dict[str, str]],
on_chunk: Callable[[str], None],
model: str = 'llama-3.3-70b-versatile'
) -> str:
"""
Stream chat response with real-time chunk processing.
"""
async with aiohttp.ClientSession() as session:
async with session.post(
f'{self.api_url}/chat',
json={
'messages': messages,
'model': model
},
headers=self.headers
) as response:
response.raise_for_status()
full_response = ''
async for chunk in response.content.iter_chunked(1024):
chunk_text = chunk.decode('utf-8')
full_response += chunk_text
on_chunk(chunk_text)
return full_response
# Usage
async def main():
client = AsyncSundayPyjamasClient(API_KEY, API_URL)
messages = [
{'role': 'user', 'content': 'Write a Python function to calculate fibonacci numbers.'}
]
# Simple async request
try:
response = await client.chat(messages)
print("AI Response:")
print(response)
except Exception as e:
print(f"Error: {e}")
# Streaming async request
print("\n--- Streaming Response ---")
try:
await client.stream_chat(
messages,
lambda chunk: print(chunk, end='', flush=True)
)
print("\n--- Streaming Complete ---")
except Exception as e:
print(f"Streaming error: {e}")
# Run async example
asyncio.run(main())
```
## Batch Processing
### Process Multiple Requests
```python theme={null}
import asyncio
import time
from typing import List, Dict, Tuple
from concurrent.futures import ThreadPoolExecutor, as_completed
class BatchProcessor:
def __init__(self, client: SundayPyjamasClient, max_workers: int = 5):
self.client = client
self.max_workers = max_workers
def process_batch_sync(self, requests: List[Dict]) -> List[Tuple[int, str, Optional[str]]]:
"""
Process multiple requests synchronously with threading.
Returns:
List of tuples (index, result, error)
"""
results = []
def process_single(index_and_request):
index, request = index_and_request
try:
response = self.client.chat(
request['messages'],
request.get('model', 'llama-3.3-70b-versatile')
)
return (index, response, None)
except Exception as e:
return (index, None, str(e))
with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
futures = {
executor.submit(process_single, (i, req)): i
for i, req in enumerate(requests)
}
for future in as_completed(futures):
results.append(future.result())
# Sort by original index
return sorted(results, key=lambda x: x[0])
async def process_batch_async(self, requests: List[Dict]) -> List[Tuple[int, str, Optional[str]]]:
"""
Process multiple requests asynchronously.
"""
async_client = AsyncSundayPyjamasClient(self.client.api_key, self.client.api_url)
async def process_single(index: int, request: Dict):
try:
response = await async_client.chat(
request['messages'],
request.get('model', 'llama-3.3-70b-versatile')
)
return (index, response, None)
except Exception as e:
return (index, None, str(e))
# Create semaphore to limit concurrent requests
semaphore = asyncio.Semaphore(self.max_workers)
async def limited_process(index: int, request: Dict):
async with semaphore:
return await process_single(index, request)
tasks = [
limited_process(i, req)
for i, req in enumerate(requests)
]
results = await asyncio.gather(*tasks)
return sorted(results, key=lambda x: x[0])
# Usage
client = SundayPyjamasClient(API_KEY, API_URL)
processor = BatchProcessor(client, max_workers=3)
# Prepare batch requests
batch_requests = [
{
'messages': [{'role': 'user', 'content': 'Write a haiku about technology.'}]
},
{
'messages': [{'role': 'user', 'content': 'Explain the concept of recursion.'}]
},
{
'messages': [{'role': 'user', 'content': 'Write a product description for a smartphone.'}]
},
{
'messages': [{'role': 'user', 'content': 'Create a motivational quote about learning.'}]
}
]
# Process synchronously
print("Processing batch synchronously...")
start_time = time.time()
sync_results = processor.process_batch_sync(batch_requests)
sync_duration = time.time() - start_time
print(f"Sync processing took {sync_duration:.2f} seconds")
for index, result, error in sync_results:
if error:
print(f"Request {index} failed: {error}")
else:
print(f"Request {index} result: {result[:100]}...")
# Process asynchronously
async def run_async_batch():
print("\nProcessing batch asynchronously...")
start_time = time.time()
async_results = await processor.process_batch_async(batch_requests)
async_duration = time.time() - start_time
print(f"Async processing took {async_duration:.2f} seconds")
for index, result, error in async_results:
if error:
print(f"Request {index} failed: {error}")
else:
print(f"Request {index} result: {result[:100]}...")
asyncio.run(run_async_batch())
```
## Content Generation Tools
### Advanced Content Generator
```python theme={null}
import json
from enum import Enum
from dataclasses import dataclass
from typing import Dict, List, Optional, Union
class ContentTone(Enum):
PROFESSIONAL = "professional"
CASUAL = "casual"
ACADEMIC = "academic"
CREATIVE = "creative"
PERSUASIVE = "persuasive"
class ContentLength(Enum):
SHORT = "short"
MEDIUM = "medium"
LONG = "long"
EXTRA_LONG = "extra_long"
@dataclass
class ContentRequest:
content_type: str
topic: str
tone: ContentTone
length: ContentLength
audience: str
additional_requirements: Optional[str] = None
class ContentGenerator:
def __init__(self, client: SundayPyjamasClient):
self.client = client
# Length guides for different content types
self.length_guides = {
'blog_post': {
ContentLength.SHORT: '500-800 words',
ContentLength.MEDIUM: '1000-1500 words',
ContentLength.LONG: '2000-3000 words',
ContentLength.EXTRA_LONG: '3000-5000 words'
},
'email': {
ContentLength.SHORT: '100-200 words',
ContentLength.MEDIUM: '200-400 words',
ContentLength.LONG: '400-600 words',
ContentLength.EXTRA_LONG: '600+ words'
},
'social_media': {
ContentLength.SHORT: '50-100 words',
ContentLength.MEDIUM: '100-200 words',
ContentLength.LONG: '200-300 words',
ContentLength.EXTRA_LONG: '300+ words'
}
}
def generate_blog_post(self, request: ContentRequest) -> str:
"""Generate a blog post based on the request parameters."""
system_prompt = f"""You are an expert content writer specializing in blog posts.
Write engaging, well-structured blog posts that:
- Have compelling introductions that hook the reader
- Use clear section headers for easy scanning
- Include practical insights and actionable advice
- Have strong conclusions that reinforce key points
- Are optimized for the target audience: {request.audience}
- Match the {request.tone.value} tone throughout"""
length_guide = self.length_guides['blog_post'][request.length]
user_prompt = f"""Write a {request.tone.value} blog post about "{request.topic}" for {request.audience}.
Target length: {length_guide}
Structure requirements:
- Compelling headline/title
- Engaging introduction
- 3-5 main sections with headers
- Practical examples or insights
- Strong conclusion with call-to-action
{f"Additional requirements: {request.additional_requirements}" if request.additional_requirements else ""}"""
messages = [
{'role': 'system', 'content': system_prompt},
{'role': 'user', 'content': user_prompt}
]
return self.client.chat(messages)
def generate_email(self, purpose: str, recipient: str, tone: ContentTone, length: ContentLength) -> str:
"""Generate an email for a specific purpose."""
system_prompt = f"""You are a professional email writer. Write clear, effective emails that:
- Achieve their intended purpose
- Use appropriate tone and formality level
- Are concise yet complete
- Include proper email structure (subject, greeting, body, closing)
- Are tailored to the recipient"""
length_guide = self.length_guides['email'][length]
user_prompt = f"""Write a {tone.value} email to {recipient} about {purpose}.
Target length: {length_guide}
Include:
- Clear, compelling subject line
- Appropriate greeting
- Well-structured body that addresses the purpose
- Professional closing
- Any necessary call-to-action"""
messages = [
{'role': 'system', 'content': system_prompt},
{'role': 'user', 'content': user_prompt}
]
return self.client.chat(messages)
def generate_marketing_copy(self, product: str, audience: str, format_type: str, tone: ContentTone) -> str:
"""Generate marketing copy for a product."""
system_prompt = f"""You are an expert copywriter specializing in conversion-focused marketing content.
Write compelling copy that:
- Focuses on benefits over features
- Creates emotional connection with the audience
- Uses persuasive language appropriate to the format
- Includes strong calls-to-action
- Addresses pain points and desires of the target audience"""
user_prompt = f"""Write {format_type} marketing copy for "{product}" targeting {audience}.
Tone: {tone.value}
Requirements:
- Lead with a strong hook that grabs attention
- Highlight key benefits that matter to {audience}
- Address common objections or concerns
- Create urgency or desire to act
- End with a clear, compelling call-to-action
Focus on what matters most to {audience} and how {product} solves their problems or improves their situation."""
messages = [
{'role': 'system', 'content': system_prompt},
{'role': 'user', 'content': user_prompt}
]
return self.client.chat(messages)
def generate_social_media_content(self, platform: str, topic: str, tone: ContentTone, length: ContentLength) -> str:
"""Generate social media content for specific platforms."""
platform_guides = {
'twitter': 'Keep under 280 characters, use hashtags, be concise and engaging',
'linkedin': 'Professional tone, longer form content allowed, focus on insights',
'instagram': 'Visual-first mindset, engaging captions, use relevant hashtags',
'facebook': 'Community-focused, encourage engagement, mix of formal and casual'
}
system_prompt = f"""You are a social media content creator expert in {platform} content.
Create engaging posts that:
- Follow {platform} best practices: {platform_guides.get(platform, 'General social media guidelines')}
- Match the platform's typical content style and audience expectations
- Encourage engagement through questions, calls-to-action, or discussion starters
- Use appropriate hashtags and formatting for the platform"""
length_guide = self.length_guides['social_media'][length]
user_prompt = f"""Create a {tone.value} {platform} post about {topic}.
Target length: {length_guide}
Requirements:
- Platform: {platform}
- Engaging opening that stops the scroll
- Clear, valuable content about {topic}
- Encourage interaction (comments, shares, likes)
- Include relevant hashtags
- End with engaging question or call-to-action"""
messages = [
{'role': 'system', 'content': system_prompt},
{'role': 'user', 'content': user_prompt}
]
return self.client.chat(messages)
# Usage examples
client = SundayPyjamasClient(API_KEY, API_URL)
generator = ContentGenerator(client)
# Generate a blog post
blog_request = ContentRequest(
content_type="blog_post",
topic="The Future of Remote Work Technology",
tone=ContentTone.PROFESSIONAL,
length=ContentLength.MEDIUM,
audience="business leaders and HR professionals",
additional_requirements="Include statistics and practical implementation tips"
)
print("Generating blog post...")
blog_post = generator.generate_blog_post(blog_request)
print("Blog Post Generated:")
print(blog_post[:500] + "..." if len(blog_post) > 500 else blog_post)
# Generate an email
print("\nGenerating email...")
email = generator.generate_email(
purpose="following up on our product demo and discussing next steps",
recipient="potential enterprise client",
tone=ContentTone.PROFESSIONAL,
length=ContentLength.MEDIUM
)
print("Email Generated:")
print(email[:300] + "..." if len(email) > 300 else email)
```
## Error Handling and Retry Logic
### Robust Error Handling
```python theme={null}
import time
import random
from typing import Dict, List, Optional, Type
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
class APIError(Exception):
"""Custom exception for API errors."""
def __init__(self, message: str, status_code: int, error_code: Optional[str] = None):
super().__init__(message)
self.status_code = status_code
self.error_code = error_code
class RobustSundayPyjamasClient:
def __init__(self, api_key: str, api_url: str, max_retries: int = 3, timeout: int = 30):
self.api_key = api_key
self.api_url = api_url
self.max_retries = max_retries
self.timeout = timeout
# Create session with retry strategy
self.session = requests.Session()
# Configure retry strategy
retry_strategy = Retry(
total=max_retries,
status_forcelist=[429, 500, 502, 503, 504],
backoff_factor=1,
allowed_methods=["POST"]
)
adapter = HTTPAdapter(max_retries=retry_strategy)
self.session.mount("http://", adapter)
self.session.mount("https://", adapter)
# Set headers
self.session.headers.update({
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json'
})
def _handle_response_error(self, response: requests.Response) -> None:
"""Handle API response errors."""
try:
error_data = response.json()
error_message = error_data.get('error', 'Unknown error')
except (ValueError, KeyError):
error_message = f"HTTP {response.status_code} error"
error_mappings = {
400: "Bad request - check your input parameters",
401: "Invalid API key - check your authentication",
403: "Forbidden - token limit exceeded or insufficient permissions",
404: "Endpoint not found",
429: "Rate limit exceeded - please wait before retrying",
500: "Internal server error - please try again later",
502: "Bad gateway - service temporarily unavailable",
503: "Service unavailable - please try again later",
504: "Gateway timeout - request took too long"
}
detailed_message = error_mappings.get(response.status_code, error_message)
raise APIError(detailed_message, response.status_code)
def _exponential_backoff(self, attempt: int, base_delay: float = 1.0, max_delay: float = 60.0) -> None:
"""Implement exponential backoff with jitter."""
delay = min(base_delay * (2 ** attempt) + random.uniform(0, 1), max_delay)
time.sleep(delay)
def chat_with_retry(
self,
messages: List[Dict[str, str]],
model: str = 'llama-3.3-70b-versatile',
custom_retry_attempts: Optional[int] = None
) -> str:
"""
Send chat request with custom retry logic for specific errors.
"""
retry_attempts = custom_retry_attempts or self.max_retries
last_exception = None
for attempt in range(retry_attempts + 1):
try:
response = self.session.post(
f'{self.api_url}/chat',
json={
'messages': messages,
'model': model
},
timeout=self.timeout,
stream=True
)
# Check for HTTP errors
if not response.ok:
self._handle_response_error(response)
# Read streaming response
full_response = ''
for chunk in response.iter_content(chunk_size=None, decode_unicode=True):
if chunk:
full_response += chunk
return full_response
except APIError as e:
last_exception = e
# Don't retry on authentication or client errors
if e.status_code in [400, 401, 403, 404]:
raise e
# Don't retry on the last attempt
if attempt == retry_attempts:
raise e
print(f"API error (attempt {attempt + 1}/{retry_attempts + 1}): {e}")
self._exponential_backoff(attempt)
except requests.exceptions.RequestException as e:
last_exception = e
# Don't retry on the last attempt
if attempt == retry_attempts:
raise APIError(f"Request failed: {str(e)}", 0)
print(f"Request error (attempt {attempt + 1}/{retry_attempts + 1}): {e}")
self._exponential_backoff(attempt)
# This should never be reached, but just in case
raise last_exception or APIError("Max retries exceeded", 0)
# Usage with error handling
client = RobustSundayPyjamasClient(API_KEY, API_URL, max_retries=3)
messages = [
{'role': 'user', 'content': 'Explain machine learning in simple terms.'}
]
try:
# Basic chat with retry
response = client.chat_with_retry(messages)
print("Response received:")
print(response[:200] + "..." if len(response) > 200 else response)
except APIError as e:
print(f"API Error ({e.status_code}): {e}")
# Handle specific errors
if e.status_code == 401:
print("Please check your API key configuration")
elif e.status_code == 403:
print("Check your token usage or account permissions")
elif e.status_code == 429:
print("Rate limit exceeded. Please wait before making more requests")
else:
print("An unexpected error occurred. Please try again later")
except Exception as e:
print(f"Unexpected error: {e}")
```
## CLI Tool Example
### Command-Line Interface
```python theme={null}
#!/usr/bin/env python3
import argparse
import sys
import json
from typing import Dict, List
from rich.console import Console
from rich.progress import Progress, SpinnerColumn, TextColumn
from rich.prompt import Prompt
from rich.markdown import Markdown
from config import API_KEY, API_URL
console = Console()
def create_cli_client():
"""Create and return a CLI-optimized client."""
return RobustSundayPyjamasClient(API_KEY, API_URL)
def interactive_chat():
"""Run interactive chat mode."""
client = create_cli_client()
conversation = []
console.print("[bold blue]SundayPyjamas AI Chat - Interactive Mode[/bold blue]")
console.print("Type 'quit' to exit, 'clear' to clear conversation history")
console.print("-" * 50)
while True:
try:
user_input = Prompt.ask("\n[bold green]You[/bold green]").strip()
if user_input.lower() == 'quit':
break
elif user_input.lower() == 'clear':
conversation = []
console.print("[yellow]Conversation history cleared.[/yellow]")
continue
elif not user_input:
continue
# Add user message to conversation
conversation.append({'role': 'user', 'content': user_input})
with Progress(
SpinnerColumn(),
TextColumn("[progress.description]{task.description}"),
console=console,
transient=True
) as progress:
task = progress.add_task("AI is thinking...", total=None)
try:
response = client.chat_with_retry(conversation)
progress.stop()
console.print(f"\n[bold blue]AI:[/bold blue]")
console.print(Markdown(response))
# Add AI response to conversation
conversation.append({'role': 'assistant', 'content': response})
except APIError as e:
progress.stop()
console.print(f"\n[red]Error: {e}[/red]")
# Remove the user message if AI response failed
conversation.pop()
except KeyboardInterrupt:
console.print("\n\n[yellow]Goodbye![/yellow]")
break
except EOFError:
break
def single_prompt(prompt: str, model: str):
"""Process a single prompt and return response."""
client = create_cli_client()
messages = [{'role': 'user', 'content': prompt}]
try:
with Progress(
SpinnerColumn(),
TextColumn("[progress.description]{task.description}"),
console=console,
transient=True
) as progress:
task = progress.add_task("Processing...", total=None)
response = client.chat_with_retry(messages, model)
progress.stop()
return response
except APIError as e:
console.print(f"[red]Error: {e}[/red]", file=sys.stderr)
sys.exit(1)
def batch_from_file(file_path: str, model: str):
"""Process prompts from a JSON file."""
client = create_cli_client()
try:
with open(file_path, 'r') as f:
data = json.load(f)
if isinstance(data, list):
prompts = data
elif isinstance(data, dict) and 'prompts' in data:
prompts = data['prompts']
else:
console.print("[red]Invalid file format. Expected array of strings or object with 'prompts' array.[/red]", file=sys.stderr)
sys.exit(1)
results = []
with Progress(console=console) as progress:
task = progress.add_task("[cyan]Processing prompts...", total=len(prompts))
for i, prompt in enumerate(prompts):
progress.update(task, description=f"[cyan]Processing prompt {i+1}/{len(prompts)}...")
try:
messages = [{'role': 'user', 'content': prompt}]
response = client.chat_with_retry(messages, model)
results.append({
'prompt': prompt,
'response': response,
'success': True
})
except APIError as e:
results.append({
'prompt': prompt,
'error': str(e),
'success': False
})
progress.update(task, advance=1)
return results
except FileNotFoundError:
console.print(f"[red]File not found: {file_path}[/red]", file=sys.stderr)
sys.exit(1)
except json.JSONDecodeError:
console.print(f"[red]Invalid JSON in file: {file_path}[/red]", file=sys.stderr)
sys.exit(1)
def main():
parser = argparse.ArgumentParser(
description='SundayPyjamas AI CLI Tool',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
%(prog)s chat # Interactive chat mode
%(prog)s prompt "Write a haiku about coding" # Single prompt
%(prog)s batch prompts.json -o results.json # Batch processing
"""
)
parser.add_argument('--model', default='llama-3.3-70b-versatile', help='AI model to use')
subparsers = parser.add_subparsers(dest='command', help='Available commands')
# Interactive chat command
chat_parser = subparsers.add_parser('chat', help='Start interactive chat')
# Single prompt command
prompt_parser = subparsers.add_parser('prompt', help='Process single prompt')
prompt_parser.add_argument('text', help='The prompt text')
prompt_parser.add_argument('--output', '-o', help='Output file (default: stdout)')
# Batch processing command
batch_parser = subparsers.add_parser('batch', help='Process prompts from file')
batch_parser.add_argument('file', help='JSON file with prompts')
batch_parser.add_argument('--output', '-o', help='Output file (default: stdout)')
args = parser.parse_args()
if not API_KEY:
console.print("[red]Error: SUNDAYPYJAMAS_API_KEY environment variable not set[/red]", file=sys.stderr)
sys.exit(1)
if args.command == 'chat':
interactive_chat()
elif args.command == 'prompt':
response = single_prompt(args.text, args.model)
if args.output:
with open(args.output, 'w') as f:
f.write(response)
console.print(f"[green]Response saved to {args.output}[/green]")
else:
console.print(Markdown(response))
elif args.command == 'batch':
results = batch_from_file(args.file, args.model)
output_data = {
'model': args.model,
'total_prompts': len(results),
'successful': sum(1 for r in results if r['success']),
'failed': sum(1 for r in results if not r['success']),
'results': results
}
if args.output:
with open(args.output, 'w') as f:
json.dump(output_data, f, indent=2)
console.print(f"[green]Results saved to {args.output}[/green]")
else:
console.print_json(data=output_data)
else:
parser.print_help()
if __name__ == '__main__':
main()
```
### Usage Examples
```bash theme={null}
# Make the CLI executable
chmod +x sundaypyjamas_cli.py
# Interactive chat
python sundaypyjamas_cli.py chat
# Single prompt
python sundaypyjamas_cli.py prompt "Write a haiku about programming"
# Single prompt with output file
python sundaypyjamas_cli.py prompt "Explain quantum computing" --output quantum_explanation.txt
# Batch processing
echo '[
"Write a short story about AI",
"Explain photosynthesis",
"Create a recipe for chocolate cake"
]' > prompts.json
python sundaypyjamas_cli.py batch prompts.json --output results.json
# Using different model
python sundaypyjamas_cli.py prompt "Hello world" --model llama-3.3-70b-versatile
```
## Testing
### Unit Tests
```python theme={null}
# tests/test_client.py
import pytest
import responses
from unittest.mock import patch, MagicMock
from sundaypyjamas_client import SundayPyjamasClient, APIError
class TestSundayPyjamasClient:
def setup_method(self):
self.api_key = "spj_ai_test_key"
self.api_url = "https://test-api.com/v1"
self.client = SundayPyjamasClient(self.api_key, self.api_url)
@responses.activate
def test_successful_chat_request(self):
# Mock successful API response
responses.add(
responses.POST,
f"{self.api_url}/chat",
body="Hello! This is a test response.",
status=200,
content_type="text/plain"
)
messages = [{"role": "user", "content": "Hello"}]
result = self.client.chat(messages)
assert result == "Hello! This is a test response."
assert len(responses.calls) == 1
assert responses.calls[0].request.url == f"{self.api_url}/chat"
@responses.activate
def test_api_error_handling(self):
# Mock API error response
responses.add(
responses.POST,
f"{self.api_url}/chat",
json={"error": "Invalid API key"},
status=401
)
messages = [{"role": "user", "content": "Hello"}]
with pytest.raises(APIError) as exc_info:
self.client.chat(messages)
assert exc_info.value.status_code == 401
assert "Invalid API key" in str(exc_info.value)
@patch('time.sleep') # Mock sleep to speed up tests
@responses.activate
def test_retry_logic(self, mock_sleep):
# Mock first request to fail, second to succeed
responses.add(
responses.POST,
f"{self.api_url}/chat",
json={"error": "Server error"},
status=500
)
responses.add(
responses.POST,
f"{self.api_url}/chat",
body="Success after retry!",
status=200
)
messages = [{"role": "user", "content": "Hello"}]
result = self.client.chat(messages)
assert result == "Success after retry!"
assert len(responses.calls) == 2
assert mock_sleep.called # Verify backoff was used
def test_input_validation(self):
with pytest.raises(ValueError):
SundayPyjamasClient("", self.api_url)
with pytest.raises(ValueError):
SundayPyjamasClient(self.api_key, "")
# Run tests
if __name__ == "__main__":
pytest.main([__file__])
```
## Beyond Chat: Other APIs
```python theme={null}
import os
import requests
api_key = os.getenv('SUNDAYPYJAMAS_API_KEY')
headers = {'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json'}
agent = requests.post(
'https://suite.sundaypyjamas.com/api/v1/managed-agents/agents',
headers=headers,
json={
'name': 'Research Assistant',
'config': {'capabilities': {'deliverables': {'enabled': True}}},
},
).json()['agent']
run = requests.post(
f"https://suite.sundaypyjamas.com/api/v1/managed-agents/agents/{agent['id']}/runs",
headers=headers,
json={'task': 'Summarize the attached Q2 report and list top 3 risks.'},
).json()
```
See the full [Agents API reference](/api-reference/agents/introduction).
```python theme={null}
response = requests.post(
'https://suite.sundaypyjamas.com/api/v1/rag/query',
headers={'Authorization': f'Bearer {supabase_session_token}'},
json={'query': 'What were the top risks in Q2?', 'workspace_id': workspace_id},
).json()
print(response['result']['answer'])
```
See the full [RAG API reference](/api-reference/rag/introduction).
```python theme={null}
response = requests.post(
'https://suite.sundaypyjamas.com/api/v1/image',
headers=headers,
json={'prompt': 'A minimalist mountain logo, flat design, teal and white'},
).json()
# response['image'] is base64-encoded PNG
```
See the full [Image API reference](/api-reference/image/introduction).
## Next Steps
Explore comprehensive JavaScript/TypeScript implementations
Command-line examples for testing and automation
Learn about optimization and usage tracking
Master robust error handling patterns
All Python examples include comprehensive error handling, retry logic, and are production-ready. The async examples provide better performance for high-throughput applications.
# SundayPyjamas AI Suite API
Source: https://docs.sundaypyjamas.com/introduction
Powerful AI capabilities for your applications with our comprehensive API platform
## Welcome to SundayPyjamas AI Suite
Integrate advanced language models and AI features directly into your applications with our powerful API platform. Built for developers who need reliable, scalable AI solutions.
Get up and running with your first API call in minutes. Generate your API key and make your first request.
Learn how to securely authenticate your requests using API keys and best practices.
## Core Features
Everything you need to build AI-powered applications.
Advanced conversational AI with streaming responses and multiple model support.
Create autonomous or assist-mode AI agents with tools, memory, and streaming runs.
Upload documents and query them with retrieval-augmented, source-cited answers.
Turn your own aggregated data into structured, LLM-authored insight reports.
Generate and edit images with Gemini image models.
Per-app vector collections for custom similarity search and retrieval pipelines.
Secure multi-tenant usage with workspace-level isolation and permissions.
## Code Examples
Ready-to-use implementations in your favorite programming language.
Complete examples for Node.js and browser applications with React components.
Comprehensive Python integration with async support and batch processing.
Command-line examples for testing and shell script automation.
## API Reference
## Essential Resources
Understand token usage, rate limits, and optimization strategies for cost-effective usage.
Comprehensive guide to error codes, troubleshooting, and robust error handling patterns.
## Quick Example
Get started immediately with this simple example:
```javascript JavaScript theme={null}
const response = await fetch('https://suite.sundaypyjamas.com/api/v1/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer spj_ai_your_api_key_here',
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [
{ role: 'user', content: 'Hello! Write me a professional email greeting.' }
]
})
});
```
```python Python theme={null}
import requests
response = requests.post(
'https://suite.sundaypyjamas.com/api/v1/chat',
headers={'Authorization': 'Bearer spj_ai_your_api_key_here'},
json={'messages': [{'role': 'user', 'content': 'Hello! Write me a professional email greeting.'}]}
)
```
```bash cURL theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/chat \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "Hello! Write me a professional email greeting."}]}'
```
API keys use the format `spj_ai_[64-character-string]` and can be generated from your workspace settings.
# Quick Start Guide
Source: https://docs.sundaypyjamas.com/quickstart
Get up and running with the SundayPyjamas AI Suite API in just a few minutes
## Get Your API Key
Start by generating your API key from your workspace settings.
1. Log into your SundayPyjamas workspace
2. Navigate to **Settings** → **API** tab
3. Click **"Generate API Key"**
4. Give your key a descriptive name (optional)
```
spj_ai_a1b2c3d4e5f6789012345678901234567890abcdef123456789012345678901234
```
Copy and store this key immediately - it won't be shown again!
## Make Your First Request
Choose your preferred method to make your first API call:
```javascript theme={null}
const response = await fetch('https://suite.sundaypyjamas.com/api/v1/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer spj_ai_your_api_key_here',
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [
{
role: 'user',
content: 'Hello! Write me a professional email greeting.'
}
]
})
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let result = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
result += decoder.decode(value);
}
console.log(result);
```
```python theme={null}
import requests
response = requests.post(
'https://suite.sundaypyjamas.com/api/v1/chat',
headers={
'Authorization': 'Bearer spj_ai_your_api_key_here',
'Content-Type': 'application/json',
},
json={
'messages': [
{
'role': 'user',
'content': 'Hello! Write me a professional email greeting.'
}
]
},
stream=True
)
full_response = ''
for chunk in response.iter_content(chunk_size=None, decode_unicode=True):
if chunk:
full_response += chunk
print(full_response)
```
```bash theme={null}
curl -X POST https://suite.sundaypyjamas.com/api/v1/chat \
-H "Authorization: Bearer spj_ai_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "Hello! Write me a professional email greeting."
}
]
}'
```
## Understanding the Response
The API returns a streaming text response. You'll receive the AI's response in real-time:
```
Hello! Here's a professional email greeting:
Dear [Recipient's Name],
I hope this email finds you well. I wanted to reach out to...
```
## Common Use Cases
Generate blog posts, emails, and marketing copy with custom prompts.
Build conversational AI interfaces with streaming responses.
Create professional emails for various purposes and audiences.
Get help with programming tasks and code generation.
## Best Practices
### Secure Your API Key
```bash Environment Variables theme={null}
# Use environment variables
export SUNDAYPYJAMAS_API_KEY="spj_ai_your_key_here"
```
```javascript In Your Code theme={null}
// In your code
const apiKey = process.env.SUNDAYPYJAMAS_API_KEY;
```
### Handle Errors Gracefully
```javascript theme={null}
try {
const response = await fetch('/api/v1/chat', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ messages })
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error);
}
// Handle streaming response...
} catch (error) {
console.error('API Error:', error.message);
}
```
### Optimize for Token Usage
Be concise and clear in your prompts to minimize token usage and costs.
```javascript theme={null}
// ❌ Too verbose
const prompt = "I would like you to please help me write a very professional business email that I need to send to my client regarding the project status update...";
// ✅ Concise and clear
const prompt = "Write a professional email to a client with a project status update.";
```
## Next Steps
Learn about API key management, security, and permissions.
Explore complete endpoint documentation with all parameters.
View ready-to-use implementations in multiple languages.
Understand usage guidelines and optimization strategies.
## Troubleshooting
* Check your API key format: `spj_ai_[64-characters]`
* Ensure the key is active and not deleted
* Verify the Authorization header: `Bearer spj_ai_...`
* Check your workspace usage in settings
* Optimize prompts to use fewer tokens
* Consider upgrading your plan
* Verify the API URL is correct
* Check your internet connection
* Ensure HTTPS is used, not HTTP
Ready to build amazing AI-powered applications? Start with the [Chat API documentation](/chat-api) or explore our [code examples](/examples/overview) to see what's possible!
# Rate Limits & Token Usage
Source: https://docs.sundaypyjamas.com/rate-limits
Understand rate limits, token usage, billing, and optimization strategies for cost-effective API usage
## Overview
The SundayPyjamas AI Suite API uses token-based usage tracking with workspace-level limits to ensure fair usage and optimal performance for all users.
All API usage is measured in tokens, which represent units of text processed by the AI models.
## Token-Based Limits
### What are Tokens?
Tokens are the fundamental units used to measure API usage:
Count the text you send to the API (your messages and conversation history)
Count the AI-generated response text
**Token estimation**: Roughly **4 characters = 1 token** for English text.
### Token Counting Example
```javascript theme={null}
// Example token usage calculation
const request = {
messages: [
{ role: "user", content: "Hello, how are you?" } // ~6 tokens
]
};
// Typical response: ~15 tokens
// Total usage: ~21 tokens
```
**Breakdown:**
* Input: "Hello, how are you?" (19 characters ÷ 4) ≈ 6 tokens
* Output: "I'm doing well, thank you for asking!" (36 characters ÷ 4) ≈ 9 tokens
* **Total: \~15 tokens**
## Workspace Limits
### Token Quotas
Each workspace has a monthly token limit based on subscription plan
All API keys in a workspace share the same token pool
Limits reset on your billing cycle date
Usage is tracked in real-time across all requests
### Checking Usage
Monitor your token usage through multiple channels:
View detailed usage in your workspace dashboard:
* Current month usage vs. limit
* Daily usage trends
* API key breakdown
* Historical usage data
```bash theme={null}
# Check workspace token usage (requires session auth)
curl -X GET "https://suite.sundaypyjamas.com/api/workspace/WORKSPACE_ID/token-usage" \
-H "Authorization: Bearer YOUR_SESSION_TOKEN"
```
**Response:**
```json theme={null}
{
"totalTokens": 45230,
"tokenLimit": 100000,
"resetDate": "2024-02-15T00:00:00Z",
"dailyUsage": [
{"date": "2024-01-14", "tokens": 1250},
{"date": "2024-01-15", "tokens": 2100}
]
}
```
Token usage information in response headers is coming soon!
Future response headers will include:
```http theme={null}
X-Token-Usage-Input: 15
X-Token-Usage-Output: 42
X-Token-Usage-Total: 57
X-Monthly-Usage: 45230
X-Monthly-Limit: 100000
```
## Credit-Based Billing
The base [Chat API](/chat-api) (`/api/v1/chat`) is metered by the token-quota model described above. Newer endpoints — [Agents](/api-reference/agents/introduction), [Artifacts](/api-reference/artifacts/introduction), [Image](/api-reference/image/introduction), and [Apps chat](/api-reference/apps/chat) — are metered by **workspace credit balance** instead, and return `402 Payment Required` with `code: "INSUFFICIENT_CREDITS"` when the balance runs out. See [Error Handling](/errors#402-payment-required) for the exact response shape.
Credits are consumed per request based on model, tokens, and (for Image) generation cost. Top up or manage auto-recharge from your workspace billing settings.
## Rate Limiting
### Request Limits
Multiple simultaneous requests are supported
No hard rate limits, but usage is monitored for abuse
Excessive usage may be temporarily throttled
Rate limits are applied per workspace
### API Key Limits
10 active API keys per workspace
Only workspace owners and admins can create keys
All keys share the workspace token pool
Usage tracked separately for each API key
## Error Responses
### Token Limit Exceeded
When your workspace exceeds its token limit:
```json theme={null}
{
"error": "Token limit exceeded"
}
```
**HTTP Status:** `403 Forbidden`
**Solutions:**
Your token limit will reset on your next billing cycle date. Check your workspace settings for the exact reset date.
Increase your monthly token limit by upgrading to a higher tier plan with more tokens.
Reduce tokens per request by:
* Writing more concise prompts
* Trimming conversation history
* Using more efficient message structures
### Rate Limited
If you're making too many requests:
```json theme={null}
{
"error": "Rate limit exceeded"
}
```
**HTTP Status:** `429 Too Many Requests`
**Solutions:**
```javascript theme={null}
async function makeRequestWithBackoff(request, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fetch('/api/v1/chat', request);
} catch (error) {
if (error.status === 429 && attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s, 8s
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
}
```
Space out your requests or implement a queue system to manage request timing.
Combine multiple prompts into single requests when possible to reduce the total number of API calls.
## Optimization Strategies
### Efficient Prompting
```javascript theme={null}
// ❌ Inefficient - Too verbose (150+ tokens)
const verbosePrompt = `
I would like you to please help me write a very professional business email
that I need to send to my client regarding the project status update that we
discussed in our previous meeting last week. The email should be formal and
include all the necessary details about the progress we have made so far and
what the next steps will be. Please make sure it sounds professional and
includes appropriate business language.
`;
// ✅ Efficient - Concise and clear (15-20 tokens)
const efficientPrompt = `Write a professional email to a client with a project status update. Include progress made and next steps.`;
```
```javascript theme={null}
// ✅ Good - Set context once with system message
const messages = [
{
role: 'system',
content: 'You are a professional email writer. Write clear, polite emails.'
},
{
role: 'user',
content: 'Write a follow-up email after a job interview.'
}
];
// ❌ Less efficient - Repeat instructions in every user message
const messagesVerbose = [
{
role: 'user',
content: 'You are a professional email writer. Write a clear, polite follow-up email after a job interview.'
}
];
```
```javascript theme={null}
function trimConversation(messages, maxTokens = 2000) {
let totalTokens = 0;
const trimmedMessages = [];
// Always keep system message if present
if (messages[0]?.role === 'system') {
trimmedMessages.push(messages[0]);
totalTokens += estimateTokens(messages[0].content);
}
// Add messages from the end, working backwards
for (let i = messages.length - 1; i >= 1; i--) {
const message = messages[i];
const messageTokens = estimateTokens(message.content);
if (totalTokens + messageTokens > maxTokens) break;
trimmedMessages.unshift(message);
totalTokens += messageTokens;
}
return trimmedMessages;
}
function estimateTokens(text) {
return Math.ceil(text.length / 4);
}
```
### Smart Request Management
```javascript theme={null}
// ❌ Multiple separate requests (3 API calls)
const requests = [
'Write a haiku about coding',
'Write a haiku about design',
'Write a haiku about teamwork'
];
for (const prompt of requests) {
await apiCall(prompt);
}
// ✅ Single batch request (1 API call)
const batchPrompt = `Write three haikus about:
1. Coding
2. Design
3. Teamwork
Format each haiku clearly with numbers.`;
await apiCall(batchPrompt);
```
```javascript theme={null}
class APIQueue {
constructor(maxConcurrent = 3, delayBetweenRequests = 100) {
this.queue = [];
this.running = 0;
this.maxConcurrent = maxConcurrent;
this.delay = delayBetweenRequests;
}
async add(requestFn) {
return new Promise((resolve, reject) => {
this.queue.push({ requestFn, resolve, reject });
this.process();
});
}
async process() {
if (this.running >= this.maxConcurrent || this.queue.length === 0) {
return;
}
this.running++;
const { requestFn, resolve, reject } = this.queue.shift();
try {
const result = await requestFn();
resolve(result);
} catch (error) {
reject(error);
} finally {
this.running--;
setTimeout(() => this.process(), this.delay);
}
}
}
// Usage
const apiQueue = new APIQueue(3, 100); // Max 3 concurrent, 100ms delay
```
```javascript theme={null}
class ResponseCache {
constructor(ttl = 3600000) { // 1 hour default
this.cache = new Map();
this.ttl = ttl;
}
generateKey(messages) {
return JSON.stringify(messages);
}
get(messages) {
const key = this.generateKey(messages);
const cached = this.cache.get(key);
if (cached && Date.now() - cached.timestamp < this.ttl) {
return cached.response;
}
if (cached) {
this.cache.delete(key); // Remove expired entry
}
return null;
}
set(messages, response) {
const key = this.generateKey(messages);
this.cache.set(key, {
response,
timestamp: Date.now()
});
}
}
// Usage
const cache = new ResponseCache();
async function cachedApiCall(messages) {
// Check cache first
let response = cache.get(messages);
if (response) {
console.log('Cache hit!');
return response;
}
// Make API call
response = await makeApiCall(messages);
cache.set(messages, response);
return response;
}
```
## Usage Monitoring
### Track Token Usage
```javascript JavaScript Token Tracker theme={null}
class TokenTracker {
constructor() {
this.dailyUsage = new Map();
this.currentUsage = 0;
}
estimateTokens(text) {
return Math.ceil(text.length / 4);
}
trackRequest(inputText, outputText) {
const inputTokens = this.estimateTokens(inputText);
const outputTokens = this.estimateTokens(outputText);
const totalTokens = inputTokens + outputTokens;
this.currentUsage += totalTokens;
const today = new Date().toDateString();
const dailyTotal = this.dailyUsage.get(today) || 0;
this.dailyUsage.set(today, dailyTotal + totalTokens);
console.log(`Request used ${totalTokens} tokens (${inputTokens} input + ${outputTokens} output)`);
console.log(`Daily usage: ${this.dailyUsage.get(today)} tokens`);
return { inputTokens, outputTokens, totalTokens };
}
getDailyUsage(date = new Date().toDateString()) {
return this.dailyUsage.get(date) || 0;
}
getProjectedMonthlyUsage() {
const today = new Date();
const daysInMonth = new Date(today.getFullYear(), today.getMonth() + 1, 0).getDate();
const dayOfMonth = today.getDate();
const dailyAverage = this.currentUsage / dayOfMonth;
return Math.ceil(dailyAverage * daysInMonth);
}
}
// Usage
const tracker = new TokenTracker();
async function chatWithTracking(messages) {
const inputText = messages.map(m => m.content).join(' ');
const response = await makeChatRequest(messages);
const outputText = await response.text();
tracker.trackRequest(inputText, outputText);
return outputText;
}
```
```python Python Usage Monitor theme={null}
import time
from typing import Dict, List
from collections import defaultdict
class UsageMonitor:
def __init__(self):
self.daily_usage = defaultdict(int)
self.total_usage = 0
def estimate_tokens(self, text: str) -> int:
return len(text) // 4 + 1
def track_request(self, input_text: str, output_text: str) -> Dict[str, int]:
input_tokens = self.estimate_tokens(input_text)
output_tokens = self.estimate_tokens(output_text)
total_tokens = input_tokens + output_tokens
self.total_usage += total_tokens
today = time.strftime('%Y-%m-%d')
self.daily_usage[today] += total_tokens
return {
'input_tokens': input_tokens,
'output_tokens': output_tokens,
'total_tokens': total_tokens,
'daily_total': self.daily_usage[today]
}
def get_usage_summary(self) -> Dict:
return {
'total_usage': self.total_usage,
'daily_usage': dict(self.daily_usage),
'avg_daily': self.total_usage / max(len(self.daily_usage), 1)
}
# Usage
monitor = UsageMonitor()
def chat_with_monitoring(messages):
input_text = ' '.join(msg['content'] for msg in messages)
response = make_chat_request(messages)
output_text = response.text
usage = monitor.track_request(input_text, output_text)
print(f"Request used {usage['total_tokens']} tokens")
return output_text
```
### Usage Alerts
```javascript theme={null}
class UsageAlerts {
constructor(monthlyLimit, alertThresholds = [50, 75, 90, 95]) {
this.monthlyLimit = monthlyLimit;
this.alertThresholds = alertThresholds;
this.alertsSent = new Set();
}
checkUsage(currentUsage) {
const usagePercentage = (currentUsage / this.monthlyLimit) * 100;
for (const threshold of this.alertThresholds) {
if (usagePercentage >= threshold && !this.alertsSent.has(threshold)) {
this.sendAlert(threshold, currentUsage, usagePercentage);
this.alertsSent.add(threshold);
}
}
}
sendAlert(threshold, currentUsage, percentage) {
const message = `⚠️ Token Usage Alert: ${percentage.toFixed(1)}% of monthly limit used (${currentUsage}/${this.monthlyLimit} tokens)`;
console.warn(message);
if (threshold >= 95) {
console.error('🚨 Critical: Approaching token limit! Consider upgrading plan or optimizing usage.');
}
// In production, you might:
// - Send email notifications
// - Post to Slack/Discord
// - Show in-app notifications
// - Log to monitoring service
}
resetAlerts() {
this.alertsSent.clear();
}
}
// Usage
const alerts = new UsageAlerts(100000); // 100K monthly limit
function checkUsageAlerts(currentUsage) {
alerts.checkUsage(currentUsage);
}
```
## Subscription Plans
### Token Limits by Plan
| Plan | Monthly Tokens | API Keys | Features |
| ---------------- | -------------- | --------- | ------------------------------------- |
| **Free** | 10,000 | 2 | Basic API access |
| **Starter** | 50,000 | 5 | Standard support |
| **Professional** | 200,000 | 10 | Priority support, analytics |
| **Enterprise** | Custom | Unlimited | Custom limits, SLA, dedicated support |
### Upgrading Plans
Upgrade your subscription to get more monthly tokens
Reduce tokens per request with better prompting
Custom limits and pricing for high-volume usage
Detailed analytics to understand and optimize usage
## Fair Usage Policy
### Acceptable Use ✅
* Content generation for business purposes
* Integration into applications and services
* Automated workflows and batch processing
* Educational and research projects
* Commercial use within subscription limits
### Prohibited Use ❌
* Reselling API access to third parties
* Overwhelming the service with excessive requests
* Using the API for illegal or harmful content
* Attempting to reverse engineer the service
* Bypassing rate limits or usage restrictions
## Troubleshooting
### Common Issues
```javascript theme={null}
// Check usage before making requests
async function safeApiCall(messages) {
try {
// Check usage first (implement based on your tracking)
const usage = await getCurrentUsage();
if (usage.percentage > 95) {
throw new Error('Approaching token limit. Request not sent.');
}
return await chatAPI(messages);
} catch (error) {
if (error.message.includes('Token limit exceeded')) {
return 'Sorry, the workspace has reached its monthly token limit. Please try again next month or upgrade your plan.';
}
throw error;
}
}
```
```javascript theme={null}
function optimizeConversation(messages, maxTokens = 2000) {
// Keep system message and recent conversation
const systemMsg = messages.find(m => m.role === 'system');
const otherMessages = messages.filter(m => m.role !== 'system');
// Calculate tokens and trim if needed
let totalTokens = systemMsg ? estimateTokens(systemMsg.content) : 0;
const optimizedMessages = systemMsg ? [systemMsg] : [];
// Add messages from most recent backwards
for (let i = otherMessages.length - 1; i >= 0; i--) {
const message = otherMessages[i];
const messageTokens = estimateTokens(message.content);
if (totalTokens + messageTokens > maxTokens) break;
optimizedMessages.push(message);
totalTokens += messageTokens;
}
// Reverse to maintain chronological order (except system message)
if (systemMsg) {
return [systemMsg, ...optimizedMessages.slice(1).reverse()];
}
return optimizedMessages.reverse();
}
```
```javascript theme={null}
// More accurate token estimation
function estimateTokens(text) {
// Account for different token patterns
const words = text.split(/\s+/);
const avgTokensPerWord = 1.3; // More accurate estimate
return Math.ceil(words.length * avgTokensPerWord);
}
// Pre-flight token check
function preflightCheck(messages, maxTokens = 4000) {
const totalTokens = messages.reduce((sum, msg) =>
sum + estimateTokens(msg.content), 0
);
if (totalTokens > maxTokens) {
throw new Error(`Request too large: ${totalTokens} tokens (max: ${maxTokens})`);
}
return totalTokens;
}
```
## Next Steps
Learn about API key management and security best practices
Explore the complete Chat API documentation and examples
Comprehensive guide to handling API errors and edge cases
See real-world implementations with usage tracking and optimization
# What the Core Agent Can Do
Source: https://docs.sundaypyjamas.com/user-guide/agents/capabilities
Chat, tools, multi-step work, and files — documents, spreadsheets, and PDFs — on the AI Suite platform.
## More than answers
A plain chat writes replies. The **Core Agent** gets work done. Here are its main powers, in plain words.
## 1. You chat with it to assign work
Talk to the Agent like a teammate. Describe the **outcome** you want. It plans the steps and reports back in the same conversation.
> *"Prepare a one-page brief for our spring launch and give me a document I can download."*
Start here: [Chat with your first Agent](/user-guide/first-agent).
## 2. It creates documents, spreadsheets, and PDFs
This is the everyday win. The Core Agent can produce **actual files** — not only text in the chat — and open them in the preview panel so you can download them.
*"Write this up as a document I can download."* · *"Make a spreadsheet…"* · *"Create a PDF…"*
The file opens on the right so you can read it in full.
Use the copy or download buttons to take the file with you.
Full guide: [Create docs, sheets & PDFs](/user-guide/agents/create-files).
"Artifact" just means **a thing the Agent made** — usually a file. If you see that word, think "the finished document."
## 3. It uses tools to take action
Tools let the Agent do real things beyond writing — searching, looking something up, or working with a service you've connected.
* Ask *"Find the latest info on X and summarize it"* and it can search, then write.
* Connect tools in [Integrations](/user-guide/integrations).
## 4. It takes multiple steps on its own
For a bigger request, the Agent breaks the work into steps and does them in order — so you don't guide every move.
> You: *"Gather this week's sign-ups, group them by country, and write a short summary document."*
> The Agent: gathers → groups → writes → gives you the file.
Watch the steps as it works. See [Run & share](/user-guide/agents/run-and-share).
## 5. It can run sub-tasks
For complex jobs, the Agent can spin off smaller helper tasks and bring results together. You don't manage these — you'll just see it working through the pieces.
## 6. It remembers your sessions
Every conversation is saved as a **session**. Reopen one later and continue. See [Using the Agents tab](/user-guide/agents/using-the-agents-tab).
## Putting it together — an example
> **You:** *"Look up our top 5 selling products this month, then write a one-page summary I can download, with a short recommendation for each."*
The Core Agent will: use tools to gather data → work step by step → write the summary → drop a downloadable file in the preview panel. One request, a finished document.
## Next step
Concrete recipes for documents, spreadsheets, and PDFs.
# Configure the Core Agent
Source: https://docs.sundaypyjamas.com/user-guide/agents/configure
Adjust how your Core Agent behaves — its instructions, model, tools, and knowledge.
## Why configure it?
The Core Agent works great out of the box. But you can **tune how it behaves** so it fits your workspace — its tone, its rules, which tools it may use, and what documents it can read.
## Opening the settings
Click **Agents** in the left menu.
It's at the top of the session list, near the search icon. This opens the agent's configuration.
## The settings, one by one
Describe, in plain words, how the agent should act — its tone and any rules. Example: *"Always answer in a friendly, concise way. Use our company's product names. Never share internal pricing."* Think of it as the agent's standing orders.
Choose which AI powers the agent. Faster models for everyday work, smarter models for harder tasks. See [Choosing a model](/user-guide/choosing-models). The default is a solid choice.
Turn on the abilities the agent may use — like search or a connected service. Many tools come from your [integrations](/user-guide/integrations). Give it only what it needs.
Add documents so the agent answers from **your** information — policies, product docs, notes. See [Ask your own documents](/user-guide/knowledge).
**Give the agent only the tools it needs.** More tools is not better — it can slow the agent down or confuse it. Add one, test, then add another if needed.
## Test after every change
After you adjust a setting, open a **new chat** in the Agents tab and try a task. Tweak the instructions until the agent behaves exactly how you want. Small, clear rules work best.
Changes to the Core Agent apply across your workspace. If several people share the workspace, agree on the instructions together so everyone gets consistent results.
## Next step
Give the agent a task, watch it work, and share the result.
# Create Docs, Sheets & PDFs
Source: https://docs.sundaypyjamas.com/user-guide/agents/create-files
Ask the Core Agent for documents, spreadsheets, and PDFs you can preview and download.
## Files, not just replies
The Core Agent can produce **real files** you open, download, and share — documents, spreadsheet-style tables, and PDFs. You ask in plain English; the file shows up in the preview panel.
Say what you want **and** the format. *"…as a document I can download"* or *"…as a spreadsheet"* or *"…as a PDF"* steers the Agent clearly.
Documents
Use documents for briefs, summaries, proposals, emails drafts, meeting notes, and write-ups.
Click **Agents**, then **+** for a new chat.
Include the purpose, length, and sections. Example:
> *Write a one-page campaign brief with Goal, Audience, Key Message, and 3 Content Ideas. Make it a document I can download.*
Open the file on the right. Copy or download when you're happy.
> *Add a Risks section and shorten the Key Message to one sentence.*
**Good document asks**
* *"Turn these notes into a status update document for leadership."*
* *"Draft a simple SOW for a website redesign, as a downloadable doc."*
* *"Rewrite this email as a short internal announcement document."*
Spreadsheets
Use spreadsheets for lists, trackers, comparisons, and anything with rows and columns.
Paste a list, CSV-ish text, or describe the columns you need.
> *Turn this into a spreadsheet with columns: Task, Owner, Priority, Due date. \[paste list]*
Preview the sheet. Ask for fixes: *"Sort by Due date"* or *"Add a Status column with Not started / In progress / Done."*
**Good spreadsheet asks**
* *"Build a content calendar sheet for April with Date, Channel, Topic, Owner."*
* *"Make a budget tracker with Category, Planned, Actual, and Variance."*
* *"Convert this messy list of leads into a clean sheet."*
PDFs
Use PDFs when you need something polished to send, print, or attach.
> *Create a one-page PDF handout of our office welcome guide, clear and friendly.*
Paste text, or point the Agent at [knowledge](/user-guide/knowledge) you've uploaded.
Preview the PDF, download it, then send it or store it in [Storage](/user-guide/workspace-tour) if you use files there.
**Good PDF asks**
* *"Make a PDF one-pager for our workshop agenda."*
* *"Turn this policy into a short PDF customers can read."*
* *"Create a PDF checklist for event day setup."*
## Tips that always help
| Do this | Why it helps |
| --------------------------------------- | --------------------------------------- |
| Name the **format** (doc / sheet / PDF) | The Agent aims at the right deliverable |
| Name the **audience** | Tone and detail stay appropriate |
| List **sections or columns** | Structure appears without extra rounds |
| Paste **your real content** | Less guessing, more accuracy |
| Ask for **one change at a time** | Easier to steer refinements |
## Related tools (optional)
* [Reports](/user-guide/reports) — another path to polished written summaries if you prefer that flow.
* [Images](/user-guide/images) — generate pictures to pair with a doc or PDF.
* [Run & share](/user-guide/agents/run-and-share) — share the whole Agent session with a link.
## Next step
Adjust how the Agent behaves so file work matches your style.
# Run & Share an Agent
Source: https://docs.sundaypyjamas.com/user-guide/agents/run-and-share
Give the Core Agent a task, watch the steps it takes, and share a session with others.
## Running a task
Using the agent is easy — you chat with it like anything else, but you can hand it bigger jobs.
Click **Agents** in the left menu and start a new chat (the **+** button).
Type your request in plain words. Be clear about the outcome you want.
> *Look up this month's top 5 products and write a one-page summary I can download.*
The agent shows the **steps** it takes — like *searching* or *reading a file*. This is normal and helps you trust the result.
If it created a file, it opens in the preview panel on the right — read it, copy it, or download it. See [What the Core Agent can do](/user-guide/agents/capabilities).
## Looking back at a session
Every task lives in a saved **session** in the list on the left. Reopen any session to re-read what the agent did, continue the conversation, or grab a file it made earlier.
If an agent gives a surprising result, reopen the session and read its steps. You'll usually see exactly where it went off track — often a missing instruction or the wrong tool. Fix it in [the settings](/user-guide/agents/configure) and try again.
## Sharing a session
Made something useful? Share the whole conversation with a link.
Hover the session in the list (or use the session menu) and choose **Share**.
Click **Copy**, then paste the link into an email or message.
Anyone with the link can see the shared session, including any files it produced. Only share with people you trust, and turn the link off from the same panel if you change your mind. If the agent used private documents or connected tools, make sure you're comfortable sharing what's in the conversation.
## Where to go next
Ready for a custom helper? Build an Assistant.
See complete examples for different kinds of work.
# Using the Agents Tab
Source: https://docs.sundaypyjamas.com/user-guide/agents/using-the-agents-tab
Find your way around the Agents workspace — sessions, tabs, search, and switching agents.
## The layout
The Agents tab has three simple areas. Once you know them, you'll feel at home.
Every conversation you've had with the agent is saved here, newest first. Click one to reopen it.
Where you talk to the agent and read its replies.
When the agent creates a file or artifact, it opens here so you can read and download it. It stays hidden until there's something to show.
## Working with sessions
A **session** is one saved conversation. Think of each session as a separate notepad for a separate task.
Click the **+** (New tab) to begin a fresh conversation. Great for starting a new task.
Click any item in the session list to continue where you left off.
Click the search icon and type to find an old conversation by its words.
Hover a session for options: rename it, [share it](/user-guide/agents/run-and-share), or delete it.
## Open several chats at once (tabs)
The Agents tab lets you keep **multiple conversations open in tabs**, just like a web browser. Each tab is its own session.
Click the **+** button in the tab strip. A fresh "New chat" tab appears.
Click any tab to jump to that conversation. Your place in each one is kept.
Click the small **×** on a tab to close it. The session is still saved in your list — closing a tab doesn't delete it.
Tabs are perfect when you're juggling a few things — keep one agent working on a report while you start a different task in another tab.
## Choosing which agent you're talking to
At the top of the session list there's an **agent picker**. By default you're talking to the **Core Agent** — that's what most people use every day. If you've built an [Assistant](/user-guide/assistants), you can switch to it here.
Custom Assistants are optional and covered under [More Ways to Work](/user-guide/assistants). You do not need them to create documents, sheets, or PDFs.
## The agent settings button
Next to the search icon is a **settings** (gear) button. It opens the agent's configuration, where you can adjust how it behaves. See [Configure the Core Agent](/user-guide/agents/configure).
## It's also a side panel
The same agent is available as a **slide-out panel** on other pages, so you can get help without leaving what you're doing. Look for the assistant button (often a sparkle icon) in the top bar to open or close it.
## Next step
Chat, tools, multi-step tasks, and docs / sheets / PDFs.
# Meet the Agent
Source: https://docs.sundaypyjamas.com/user-guide/agents/what-are-agents
The Agents tab is home to your Core Agent — the primary way to get work done on the AI Suite platform.
## What is an agent?
An **agent** doesn't only answer questions. It can *do things*: use tools, take several steps, and create files (documents, spreadsheets, PDFs) toward a clear goal.
> Asking a quick question in [plain Chat](/user-guide/first-chat) is like texting a knowledgeable friend.
> Working with an **agent** is like briefing a teammate who then **delivers the work**.
On AI Suite, **Agents are the primary experience**. Plain Chat and Assistants are useful extras once you're comfortable here.
## The Core Agent lives in the Agents tab
Click **Agents** in the left menu. You'll meet your **Core Agent** — built into every workspace and ready immediately. No setup required. Open the tab and give it a task.
## What makes the Core Agent special
Describe the outcome in plain English — the Agent plans and executes.
Produce documents, spreadsheets, and PDFs you can preview and download.
Search, look things up, and use connectors you've turned on.
Break a task into steps and work through them without you micromanaging.
Details: [What the Core Agent can do](/user-guide/agents/capabilities).
## Agent vs plain Chat
| | Plain Chat | Core Agent |
| ----------------------------- | ---------- | ---------- |
| Quick answers & brainstorming | ✅ | ✅ |
| Remembers the conversation | ✅ | ✅ |
| Creates docs / sheets / PDFs | — | ✅ |
| Uses connected tools | — | ✅ |
| Multi-step tasks | — | ✅ |
**Default to the Core Agent** whenever you want a file, a multi-step job, or tools. Use [plain Chat](/user-guide/first-chat) only for fast Q\&A.
## Custom helpers (later)
Most people stay with the Core Agent. When you want a specialist with fixed instructions, you can build an [Assistant](/user-guide/assistants). That is optional and covered under **More Ways to Work**.
## Next step
Sessions, tabs, search, and switching agents.
# Build an Assistant
Source: https://docs.sundaypyjamas.com/user-guide/assistants
Optional: create a custom specialist agent. Most people can stay with the Core Agent.
## What is an Assistant?
The [Core Agent](/user-guide/agents/what-are-agents) is general-purpose and is the **main** way to work on AI Suite. An **Assistant** is an optional **custom agent** tuned for one recurring job. You give it a name, instructions, a model, and — if you like — tools and documents. Then you pick it in the [Agents tab](/user-guide/agents/using-the-agents-tab).
If you've read [Build an app](/user-guide/build-an-app), this will feel familiar. An Assistant is like an app that can also **use tools** and **take action**.
New here? Learn [chat with the Core Agent](/user-guide/first-agent) and [create files](/user-guide/agents/create-files) first. Come back to Assistants when you need a reusable specialist.
## Build it step by step
Go to **Agents** (or the Platform area) and choose **Create** → **Assistant**.
Give it a clear name, like *"Travel Planner"* or *"Support Helper"*.
Tell it who it is and how to behave, in plain words:
> *You are a friendly travel planner. Ask the user for their destination, dates, and budget. Then suggest a simple day-by-day plan. Keep it short and cheerful.*
Good instructions = clear job + clear tone + any rules. Imagine training a new assistant on day one.
Pick the AI brain — see [Choosing a model](/user-guide/choosing-models). Default is fine to start.
Turn on any tools it should use (like a connected calendar or search). See [Connect your tools](/user-guide/integrations).
Upload documents so it answers from your own information — see [Ask your own documents](/user-guide/knowledge).
Use the built-in preview to chat with your Assistant. Try a real question and see how it responds.
Save it. Now open the [Agents tab](/user-guide/agents/using-the-agents-tab), pick your Assistant from the agent picker, and put it to work — or [share it](/user-guide/agents/run-and-share) with your team.
## Making it better
* **If it forgets a rule**, add that rule to the instructions.
* **If it needs to do something**, give it the right tool.
* **If it needs facts**, add documents to its knowledge.
* **Test the hard cases** — the awkward questions it will really get.
## Do you need an Assistant?
Most people should stay with the **[Core Agent](/user-guide/agents/what-are-agents)** for everyday work (docs, sheets, PDFs, multi-step tasks). Build an Assistant only when you want a **named specialist** with fixed instructions you reuse often.
Managed agents are an [advanced option](/user-guide/managed-agents) — skip them unless you specifically need more autonomous automation.
## Next step
Back to the primary Agent workflow most teams use daily.
# Build Your Own AI App
Source: https://docs.sundaypyjamas.com/user-guide/build-an-app
Optional: create a reusable AI helper. Most everyday work belongs in Agents.
## What is an "app"?
An **app** is an optional custom AI helper you save and reuse. For most day-to-day work on the AI Suite platform — documents, sheets, PDFs, multi-step tasks — use the **[Core Agent](/user-guide/first-agent)** first.
Build an app when you want a dedicated helper with fixed instructions that people open like a mini product.
> The **Core Agent** is your main teammate. An **app** is a specialized notebook that already has instructions on page one.
**Examples of apps people build:**
* A "Customer Reply Writer" that always answers in your brand's friendly tone.
* A "Meeting Notes Summarizer" that turns notes into action items.
* A "Product Q\&A" app that answers from your uploaded product manual.
Prefer something that can also use tools inside the Agents tab? See [Build an Assistant](/user-guide/assistants).
## How to build one
Click **Apps** in the left menu, then click **New app** (or the create button).
Something clear, like *"Customer Reply Writer"*. This is just so you can find it later.
This is the important part. In plain words, tell the app how to behave — its job, its tone, and any rules. For example:
> *You help our support team reply to customers. Always be warm and friendly. Keep replies under 100 words. Never promise refunds — instead, offer to escalate to a manager.*
Think of the instructions as training a new team member on their first day. What would you tell them so they do the job right every time?
Choose the AI brain for your app — see [Choosing a model](/user-guide/choosing-models). The default is a fine starting point.
Want the app to answer from your documents? Upload them to the app's knowledge area — see [Ask your own documents](/user-guide/knowledge).
Most apps have a **playground** or **preview** where you can try it out. Send a test message and see how it responds.
Happy with it? Save it. Now it's ready whenever you need it — and you can share it with your team.
## Making your app better
* **Refine the instructions.** If it does something you don't like, add a rule about it.
* **Give examples in the instructions.** "Here's an example of a good reply: ..." helps a lot.
* **Add documents** so it answers from real, accurate information.
* **Test with tricky cases** — try the awkward questions your app will really get.
## Next step
Let your apps and agents work with tools you already use.
# Choosing a Model
Source: https://docs.sundaypyjamas.com/user-guide/choosing-models
What an AI 'model' is, and how to pick the right one — explained with a simple analogy.
## What is a "model"?
A **model** is the "brain" the AI uses to answer you. AI Suite offers several models, and you can switch between them.
Here's the easy way to think about it:
> Choosing a model is like choosing a **worker for a task**. Some workers are super fast and great for everyday jobs. Others are slower but think more carefully — perfect for hard problems. You pick the right one for what you need.
You don't need to understand the technology. You just need to know: **fast models for quick, simple things; smart models for hard, important things.**
## The simple rule
You want quick answers, simple writing, short summaries, or casual brainstorming. Great for everyday use.
The task is tricky, detailed, or important — deep analysis, careful reasoning, or long, high-quality writing.
Smart models can take a little longer and may use more of your credits. That's normal — they're doing more thinking. See [Team & billing](/user-guide/team-and-billing) to understand credits.
## How to switch the model
In Chat (and in apps and agents), there's a small dropdown showing the current model's name, usually near the message box or at the top of the conversation.
Click it. You'll see the models you can choose from, each with a short label.
Click the model you want. Your next message will use it. You can change it again any time — even mid-conversation.
## Which one should I start with?
If you're not sure, **use the default model**. It's chosen to work well for most everyday tasks. Only switch to a smarter model if an answer feels too shallow, or to a faster one if you just want quick replies.
## A quick way to compare
Not sure if a smarter model is worth it? Ask the same question with two different models and compare the answers. You'll quickly get a feel for which one suits your work.
## Next step
The way you ask matters even more than the model. Here's how to ask well.
# Create Your Account
Source: https://docs.sundaypyjamas.com/user-guide/create-account
Sign up, log in, and get into your workspace for the first time.
## What is a "workspace"?
A **workspace** is your private area on the AI Suite **platform**. It holds your Agent sessions, files, apps, and settings. If you work with a team, you can all share one workspace. Think of it like your desk at work — your stuff lives there, and you decide who else can sit at it.
## Sign up (first time only)
Go to your AI Suite web address (for example, **suite.sundaypyjamas.com**). You'll see a box asking you to sign in or sign up.
Switch to the **Sign up** tab at the top of the box.
Use an email you can check right now. Pick a password you'll remember (or save it in your password manager).
Check your inbox for a message from AI Suite and click the confirmation link. This proves the email is really yours.
No email after a minute? Check your spam or junk folder, then try again.
## Log in (every time after that)
On the **Sign in** tab, type the email and password you used to sign up, then click **Sign in with email**.
You're in! You'll see your **Dashboard** — the home screen of your workspace. The next page gives you a full tour.
## Forgot your password?
No problem — this happens to everyone.
It's the small link next to the password box.
We'll send you a reset link.
Open the link and choose a new password. Then log in as usual.
Keep your password private. AI Suite staff will **never** ask you for it. If you get an email asking for your password, don't reply — it's not from us.
## Next step
Now let's look around and see where everything is.
# FAQ & Troubleshooting
Source: https://docs.sundaypyjamas.com/user-guide/faq
Quick answers and simple fixes for the most common questions.
## Getting in
* Double-check your email and password for typos.
* Use **Forgot password?** on the login screen to reset it.
* Make sure you confirmed your email when you signed up (check spam).
* Still stuck? Contact your workspace admin or support.
Wait a minute, then check your **spam/junk** folder. If it's still missing, ask the person who invited you to resend it, or try signing up again.
Use the **workspace switcher** in the top-right corner to change to the right one. See [the workspace tour](/user-guide/workspace-tour).
## Using Agents
Start with **[Agents](/user-guide/first-agent)**. That is the primary experience on AI Suite. Use [plain Chat](/user-guide/first-chat) only for quick Q\&A when you don't need a file or tools.
Open **Agents**, ask for the format you want, then download from the preview panel. See [Create docs, sheets & PDFs](/user-guide/agents/create-files).
Usually **no**. Managed agents are [advanced](/user-guide/managed-agents). Non-technical users should stick with the Core Agent.
## Using the AI
AI can sometimes sound confident but be incorrect. For anything important, double-check it. You can reply *"Are you sure? Please double-check,"* try a [smarter model](/user-guide/choosing-models), or give it the real facts as [knowledge](/user-guide/knowledge).
Just ask for a change: *"Make it shorter,"* *"More formal,"* *"Add bullet points."* The AI remembers the conversation and adjusts.
Make sure you're in the **same Agent session** (or chat). A **new** session starts fresh. Keep one topic per session.
Ask more clearly — say who it's for, how long, the tone, and the **file format** if you need one. See [Writing good prompts](/user-guide/writing-prompts).
Use the model picker. See [Choosing a model](/user-guide/choosing-models).
## Credits, limits, and speed
Check **Settings → Usage** to see your balance. You can add credits or upgrade your plan from **Billing** (admins only). See [Team & billing](/user-guide/team-and-billing).
Smarter models think more and take longer. For quick tasks, switch to a faster model. Long documents also take more time to process.
Use faster models for simple work, keep prompts concise, and reuse apps/agents instead of re-explaining. See [Team & billing](/user-guide/team-and-billing).
## Documents, apps, and agents
Give it a moment to finish processing after upload (look for a *ready* status). Make sure you uploaded it to the right place — the app or agent's knowledge area. See [Ask your own documents](/user-guide/knowledge).
Update its **instructions** with a clear rule about what to do differently, then test again. See [Build an app](/user-guide/build-an-app) and [Configure the Core Agent](/user-guide/agents/configure).
Open [Integrations](/user-guide/integrations) and check the tool is still connected. You may need to reconnect and approve access again.
## Sharing and privacy
Only people with the **share link** can see a shared item — and only that item, not the rest of your workspace. You can turn any link off. See [Sharing your work](/user-guide/sharing).
Open the same **Share** panel and disable or delete the link. It stops working for everyone immediately.
Still stuck? Ask your workspace admin, or reach out through your organization's support channel.
## Next step
Every tricky word explained in one plain sentence.
# Chat with Your First Agent
Source: https://docs.sundaypyjamas.com/user-guide/first-agent
Open the Agents tab and get a real deliverable — a document you can download — in a few minutes.
## This is where work gets done
**Agents** are the main way to use AI Suite. You describe what you want in plain English; the **Core Agent** plans the steps, uses tools when needed, and can hand you a finished **document, spreadsheet, or PDF** — not only a chat reply.
Let's do one task together.
## Step by step
Click **Agents** in the menu on the left. You'll see your sessions on the left, the chat in the middle, and space for a file preview on the right.
Click **+** (new tab or new chat) so you have a clean session for this task.
Type something concrete. Try copying this:
> *Write a friendly one-page project brief for a coffee shop loyalty program. Include Goal, Audience, Key Message, and Next Steps. Give me a document I can download.*
Name the **outcome** (a downloadable document) and the **sections** you want. Clear outcomes get better results. More tips in [Writing good prompts](/user-guide/writing-prompts).
The Agent may show steps as it goes — drafting, formatting, creating a file. That is normal.
When a file is ready, it appears in the **preview panel** on the right. Read it, then use **copy** or **download**.
Stay in the same session and refine:
> *Make the Key Message shorter and add a simple timeline for the next two weeks.*
The Agent remembers the brief and updates the file.
## Try these starter tasks
*"Summarize these notes into a one-page status update I can download: \[paste notes]"*
*"Turn this list into a spreadsheet with columns Name, Owner, and Due date: \[paste list]"*
*"Create a short PDF handout of our return policy in plain language: \[paste policy]"*
*"Draft a 5-step launch checklist as a document I can share with the team."*
## Common questions
No. You can't break the platform by chatting with an Agent. If the result isn't right, ask again in clearer words.
Use [plain Chat](/user-guide/first-chat) for quick Q\&A with no file or tools needed. For real deliverables, stay in **Agents**.
See [Create docs, sheets & PDFs](/user-guide/agents/create-files).
## Next step
Understand what the Core Agent can do — and why it's the center of AI Suite.
# Plain Chat (Optional)
Source: https://docs.sundaypyjamas.com/user-guide/first-chat
Quick Q&A without files or tools — handy, but secondary to Agents on the AI Suite platform.
## When to use plain Chat
**Chat** is a simple conversation with the AI: you type, it replies. It is great for quick questions, rewrites, and brainstorming when you **don't** need a downloadable file or tools.
For real work — documents, spreadsheets, PDFs, multi-step tasks — use **[Agents](/user-guide/first-agent)** instead. That is the primary experience on AI Suite.
New to the platform? Start with [Chat with your first Agent](/user-guide/first-agent), then come back here if you want a lighter Q\&A surface.
## Step by step
Click **Chat** in the menu on the left. You'll see an empty box at the bottom, waiting for your message.
Click in the box and type something simple. Try:
> *Write a friendly thank-you email to a customer named Sam who just bought our coffee subscription.*
The clearer you are, the better the answer. More on this in [Writing good prompts](/user-guide/writing-prompts).
Press **Enter**, or click the send button. The AI will start typing within a second or two.
Keep typing in the same chat. For example:
> *Make it a bit shorter and add a 10% discount code: WELCOME10.*
## Handy things you can do with an answer
* **Copy it** — hover over the answer to find the copy button.
* **Ask for changes** — "make it shorter", "more formal", "add bullet points".
* **Start fresh** — click **New chat** when you change the subject.
## Prefer a file instead?
If you need a document, spreadsheet, or PDF, switch to [Agents](/user-guide/agents/create-files) — that's what the Core Agent is for.
## Next step
Back to the primary path — files from the Core Agent.
# For Developers
Source: https://docs.sundaypyjamas.com/user-guide/for-developers
Ready to go beyond the buttons? Everything in this guide is also available through our API.
## You've mastered the app — here's what's next
Everything you've done by clicking — chatting, generating reports and images, querying documents, running agents — can also be done **programmatically**, through the AI Suite **API**. That means you (or a developer on your team) can build these capabilities directly into your own websites, apps, and automations.
This page is a bridge. The pages it links to are written for developers and include code. If that's not you, no problem — the [User Guide](/user-guide/welcome) has everything you need.
## Where to go
Generate an API key and make your first request in minutes.
How API keys work and how to keep them secure.
The developer reference for conversational AI.
Every endpoint: Chat, Agents, RAG, Artifacts, Image, Vector Store, and more.
## How the app features map to the API
| What you clicked in the app | The API that does the same thing |
| ------------------------------ | ------------------------------------------------------------ |
| Chat | [Chat API](/api-reference/chat/introduction) |
| Agents & Assistants | [Agents API](/api-reference/agents/introduction) |
| Ask your documents (Knowledge) | [RAG API](/api-reference/rag/introduction) |
| Reports | [Artifacts API](/api-reference/artifacts/introduction) |
| Images | [Image API](/api-reference/image/introduction) |
| Advanced document search | [Vector Store API](/api-reference/vector-store/introduction) |
| Connected tools | [Integrations](/api-reference/integrations/introduction) |
## Getting your API key
In your workspace, go to **Settings**, then the **API** tab.
Click **Generate API Key** and copy it somewhere safe — it's shown only once.
Head to the [Quickstart](/quickstart) to make your first call.
Not a developer but want this built? Share the [Quickstart](/quickstart) and [API Reference](/api-reference/chat/introduction) links with your engineering team — they'll have what they need to get started.
# Plain-Language Glossary
Source: https://docs.sundaypyjamas.com/user-guide/glossary
Every AI Suite word explained in one simple sentence.
Bookmark this page. Whenever a word trips you up, it's here in one plain sentence.
An AI teammate on the AI Suite **platform** that can *do things* — chat to assign work, use tools, take steps, and create files. See [Meet the Agent](/user-guide/agents/what-are-agents).
Your own custom AI helper with saved instructions, so it behaves the same way every time. Optional; see [Build an app](/user-guide/build-an-app).
A finished thing the Agent produces — often a document, spreadsheet, or PDF. See [Create docs, sheets & PDFs](/user-guide/agents/create-files).
An optional custom agent with your own instructions, model, and tools. Most people can stay with the Core Agent. See [Build an Assistant](/user-guide/assistants).
The 'fuel' each AI action uses, a bit like data on a phone plan. See [Team & billing](/user-guide/team-and-billing).
A safe link between AI Suite and another tool you use, so the AI can work with it. See [Connect your tools](/user-guide/integrations).
Documents you upload so the AI can read them and answer questions about them. See [Ask your own documents](/user-guide/knowledge).
An advanced, more autonomous custom agent. Most non-technical users should skip this and use the Core Agent. See [Managed agents (advanced)](/user-guide/managed-agents).
The technology behind connectors — a standard, safe way to link AI to other tools. You don't need to know the details; just think 'integration.'
The 'brain' the AI uses to answer — some are faster, some are smarter. See [Choosing a model](/user-guide/choosing-models).
Just your message to the AI. Writing a 'good prompt' means asking clearly. See [Writing good prompts](/user-guide/writing-prompts).
The technical name for 'read my documents, then answer from them.' See [Ask your own documents](/user-guide/knowledge).
A tidy, written document the AI creates from your information. See [Create a report](/user-guide/reports).
One time an agent does a task — you can look back at past runs to see what it did. See [Run & share an agent](/user-guide/agents/run-and-share).
A tiny piece of text the AI counts to measure usage; more text means more tokens. Closely related to credits.
An ability you give an agent so it can take an action, like searching or checking a calendar. See [Configure the Core Agent](/user-guide/agents/configure).
Your private area on the AI Suite platform that holds your Agent sessions, files, apps, and settings. See [Create your account](/user-guide/create-account).
The built-in agent in the Agents tab — the primary way to work on AI Suite. Chat with it to create docs, sheets, PDFs, and run multi-step tasks. See [Meet the Agent](/user-guide/agents/what-are-agents).
AI Suite as a whole — Agents, tools, files, and APIs — not just a single webpage. You work in your workspace on the platform.
## Next step
Ready to go beyond the buttons and use the API? Start here.
# Make Images
Source: https://docs.sundaypyjamas.com/user-guide/images
Describe a picture in words and the AI draws it for you. No art skills needed.
## What is this?
The **Images** tool lets you create pictures by describing them in plain words. Type "a cozy coffee shop on a rainy evening, warm lights" and the AI draws it. You can make logos, illustrations, backgrounds, social media pictures, and more.
> It's like having an artist who paints whatever you describe — in seconds.
## How to make your first image
Click **Images** in the left menu. You'll find a box to describe your picture.
Type what you want to see. Be visual. For example:
> *A friendly orange cat sitting on a stack of books, cartoon style, soft colors, white background.*
Click **Generate** and wait a few seconds while the AI draws it.
Like it? Download it. Want something different? Change your description and generate again.
## How to describe a good image
The trick is to paint a picture with words. Include:
What's in it? *"a mountain lake", "a smiling robot".*
How should it look? *"photo", "cartoon", "watercolor", "minimal".*
*"warm and cozy", "bright and playful", "dark and moody".*
*"white background", "top-down view", "soft lighting".*
## Editing an image
Some image tools let you **change an existing picture** — for example, "make the background blue" or "add a hat." Look for an edit option after you generate. Describe the change and the AI updates the picture.
If the first result isn't quite right, add more detail rather than starting over. "Make it brighter and add a blue sky" often gets you there faster.
## Next step
Upload files and ask the AI questions about what's inside them.
# Connect Your Tools
Source: https://docs.sundaypyjamas.com/user-guide/integrations
Let AI Suite work with the other tools you use, like chat apps and calendars.
## What is an integration?
An **integration** is a connection between AI Suite and another tool you already use — like Slack, a calendar, or a database. Once connected, your AI can *use* that tool: read information from it, or take actions in it.
> Think of integrations as **giving your AI helper the keys** to the other tools on your desk, so it can actually help with them.
You may also hear the word **connector** or **MCP** — these mean the same idea: a safe bridge between AI Suite and another service.
## What can this do for me?
* Let an agent read your calendar and suggest meeting times.
* Pull recent messages from a chat tool to summarize them.
* Connect a data source so your app can answer from live information.
## How to connect a tool
Click **Integrations** in the left menu. You'll see the tools you can connect.
Find the tool you want and click **Connect** (or **Add**).
You'll be asked to sign in to that tool and allow AI Suite to use it. This is normal and safe — you're giving permission on purpose.
Only connect tools you trust and actually need. You can disconnect any tool later from this same screen.
Once connected, the tool becomes available to your apps and agents. When you build one, you can turn the tool on so the AI can use it. See [Configure the Core Agent](/user-guide/agents/configure).
## Staying in control
* **You choose** what to connect — nothing connects on its own.
* **You can disconnect** any tool at any time from the Integrations screen.
* **Permissions are specific** — a connection only allows what you approved.
If a tool you need isn't listed, your workspace admin may be able to add it, or it may require the developer setup. See [For developers](/user-guide/for-developers).
## Next step
Send a chat, report, or app to someone with a simple link.
# Ask Your Own Documents
Source: https://docs.sundaypyjamas.com/user-guide/knowledge
Upload files, then ask the AI questions about what's inside them.
## What is this?
Normally the AI answers from what it already knows. But you can also **give it your own documents** — a PDF, a report, meeting notes — and then ask questions about *those*. The AI reads your files and answers based on what's actually in them.
The technical name for this is **RAG** (retrieval-augmented generation), but you don't need to remember that. Just think of it as:
> **"Read this, then answer my questions about it."**
## When is this useful?
* "What does our return policy say about damaged items?" (from a policy PDF)
* "Summarize the key decisions from these meeting notes."
* "Find every mention of pricing in this contract."
* "What are the main points across these 5 documents?"
Instead of reading everything yourself, you ask, and the AI finds the answer inside your files.
## How to use it
Go to the place where you add knowledge — this is usually inside an **App** (on its **Knowledge** or **Data** section) or your **Storage** area. Click **Upload** and choose your file.
After uploading, the AI needs a moment to "read" the document (you might see a status like *processing* or *ready*). This usually takes a few seconds to a minute.
Now ask about the content in plain words:
> *According to the uploaded handbook, how many vacation days do new employees get?*
Good answers often show **where** they came from in your document, so you can trust and verify them. Look for a citation or a highlighted snippet.
## Tips for good results
* **Upload clean files.** Clear, text-based PDFs work best. Very blurry scans are harder to read.
* **Ask specific questions.** "What's the refund window?" beats "tell me about refunds."
* **Add more documents** for broader questions — the AI can look across all of them.
* **Ask for the source.** "Which section is that from?" helps you double-check.
Your uploaded documents stay in your workspace. They're used to answer *your* questions, not shared publicly.
## Next step
Package all of this — a model, instructions, and your documents — into a reusable helper.
# Managed Agents (Advanced)
Source: https://docs.sundaypyjamas.com/user-guide/managed-agents
Optional, more technical custom agents. Most people should use the Core Agent instead.
**Most people do not need Managed agents.** If you are non-technical, stay with the [Core Agent](/user-guide/agents/what-are-agents) in the Agents tab — chat with it, create [docs, sheets, and PDFs](/user-guide/agents/create-files), and you're done. Managed agents are an advanced option for teams that need heavily automated, hands-off runs.
## What is a Managed agent?
A **Managed agent** is a custom agent that runs in a **managed environment** — AI Suite handles more of the infrastructure behind the scenes. It is aimed at bigger, more independent tasks that may take several steps and several tools.
> An [**Assistant**](/user-guide/assistants) is a specialist you chat with.
> A **Managed agent** is closer to a **worker you hand a whole job to**, which then runs with less back-and-forth.
## Who this is for
| You want… | Use this |
| --------------------------------------------- | ----------------------------------------- |
| Everyday docs, sheets, PDFs, and tasks | **[Core Agent](/user-guide/first-agent)** |
| A reusable specialist with fixed instructions | **[Assistant](/user-guide/assistants)** |
| Advanced, more autonomous automated runs | **Managed agent** (this page) |
## How to create one
Go to **Agents** (or the Platform area) and choose **Create** → **Managed agent**.
Give it a name and describe the **outcome**:
> *Every week, gather our new customer sign-ups, group them by country, and produce a short summary report.*
Pick the model and turn on only the tools it needs (see [Connect your tools](/user-guide/integrations)).
Start the agent and watch progress.
Check what it produced. Refine the goal or tools and run again if needed.
## Good practices
* **Prefer the Core Agent first** — prove the workflow in a normal Agent chat before automating it.
* **Describe the goal clearly** — managed agents need a well-defined outcome.
* **Give it only the tools it needs.**
* **Check its work** before relying on it for anything important.
## Next step
Return to the primary path most people use every day.
# Create a Report
Source: https://docs.sundaypyjamas.com/user-guide/reports
Another way to turn information into a clean written summary — Agents can also create docs directly.
## What is a Report?
A **Report** is a tidy, written document that the AI creates from your information — a summary, analysis, or write-up ready to read or share.
For most everyday documents, start in **[Agents](/user-guide/agents/create-files)** and ask for a downloadable document. Use **Reports** when you prefer this dedicated flow.
> Think of an Agent chat as the **workspace**, and a Report (or Agent-created document) as the **finished write-up**.
## When would I use a Report?
* Summarize a month of activity into a one-page overview.
* Turn raw notes into a clean write-up.
* Create a recap you can send to your team or a client.
* Analyze a set of information and get the key takeaways in order.
## How to create one
Click **Reports** in the left menu.
Click **New report** (or the create button). You'll be asked what you want it to cover.
Tell the AI, in plain words, what the report should include. For example:
> *Create a one-page summary of this month's customer feedback. Group it into "What people loved" and "What to improve", and end with 3 suggested next steps.*
The same clear-asking habits from [Writing good prompts](/user-guide/writing-prompts) apply here. Say what to include, how to group it, and how long it should be.
Click generate and wait a few seconds. The AI writes the full report for you.
Read it over. Want changes? Ask for them — "add a section on shipping", "make the summary shorter" — and the report updates.
## Sharing your report
Once you're happy with it, you can share it with others using a link. See [Sharing your work](/user-guide/sharing) for how that works and what people can see.
## Tips for great reports
* **Be specific about sections.** Tell it the headings you want.
* **Give it the source material.** Paste or upload the information it should summarize.
* **Ask for a length.** "one page", "5 bullet points", "a short executive summary".
* **Iterate.** The first version is a draft — refine it until it's right.
## Next step
Now let's create some pictures just by describing them.
# Real-World Scenarios
Source: https://docs.sundaypyjamas.com/user-guide/scenarios
Copy-me examples centered on Agents — docs, sheets, PDFs, and everyday tasks on the AI Suite platform.
## How to use this page
Find the scenario closest to your work and follow the steps. Each one uses the **Core Agent** first. Links point to deeper pages when you need them.
***
Marketer: campaign brief document
**Goal:** Turn messy notes into a downloadable campaign brief.
Go to [Agents](/user-guide/first-agent) and start a new session.
> *Turn these rough notes into a one-page campaign brief document with Goal, Audience, Key Message, and 3 Content Ideas. I need to download it. \[paste notes]*
*"Make the key message punchier"* or *"Add a catchy campaign name."*
Download from the preview panel, or [share the session](/user-guide/agents/run-and-share).
***
Ops: tracker spreadsheet
**Goal:** Turn a messy task list into a clean sheet.
Start a new Agent session.
> *Turn this list into a spreadsheet with columns Task, Owner, Priority, and Due date. \[paste list]*
*"Sort by Due date and add a Status column."*
More recipes: [Create docs, sheets & PDFs](/user-guide/agents/create-files).
***
Support lead: policy PDF
**Goal:** A short PDF customers (or the team) can read.
Start a new session in **Agents**.
> *Rewrite this return policy in plain, friendly language as a one-page PDF handout. \[paste policy]*
Upload longer help docs as [knowledge](/user-guide/knowledge), then ask the Agent to answer only from them and produce the PDF.
Need a reusable specialist later? See [Build an Assistant](/user-guide/assistants) — optional, not required.
***
Analyst: ask questions of a document
**Goal:** Get answers out of a long report without reading every page.
Add your file as [knowledge](/user-guide/knowledge), or paste key sections into an Agent chat.
> *What are the payment terms?* · *List every deadline.* · *Summarize the risks in 5 bullets as a short document I can download.*
Ask *"Which section is that from?"* before you rely on it.
***
Small business owner: weekly summary
**Goal:** A tidy weekly recap as a downloadable document.
Paste the week's numbers and notes into a new Agent session.
> *Create a one-page weekly summary document with Wins, Problems, and Next Week's Focus. Make it downloadable.*
Open a new session, paste new numbers, and ask for the same structure — or save good instructions in an [Assistant](/user-guide/assistants) later if you want.
***
Anyone: tidy writing, then a doc
**Goal:** Clean up a draft and leave with a file.
> *Rewrite this to be clear, friendly, and under 80 words, then put the final version in a short document I can download: \[paste draft]*
For tiny edits with no file needed, [plain Chat](/user-guide/first-chat) is fine — but Agents covers both rewrite and deliverable.
Pattern for almost everything on AI Suite: **open Agents → describe the outcome and format → refine → download.**
## Next step
Quick answers to the questions people ask most.
# Share Your Work
Source: https://docs.sundaypyjamas.com/user-guide/sharing
Send a chat, report, agent, or app to other people with a simple link.
## What can I share?
Almost anything you make in AI Suite can be shared with a link:
* A **chat** conversation
* A **report** you generated
* An **agent** or **app** you built
When you share, AI Suite creates a web link. Anyone you send it to can open it in their browser — no account needed (unless you choose otherwise).
## How to share
Open the thing you want to share (a chat, a report, an app, or an agent) and look for a **Share** button, usually near the top right.
Click **Share**. AI Suite gives you a link. Click **Copy**.
Paste the link into an email, a message, or a document — wherever you want. Whoever opens it sees your shared item.
## What will people see?
* They see the **content you shared** — for example, the report or the conversation.
* They generally **cannot** see the rest of your workspace, your other chats, or your settings.
* For a shared **app or agent**, they can usually *use* it (send it messages), but not change how it's built.
A share link works for anyone who has it. Only send it to people you trust, and don't post private information in something you're about to share publicly. If you shared something by mistake, you can turn the link off from the same Share panel.
## Turning off sharing
Changed your mind? Open the same **Share** panel and disable or delete the link. Once it's off, the link stops working for everyone.
## Next step
Bring teammates into your workspace and understand credits.
# Team & Billing
Source: https://docs.sundaypyjamas.com/user-guide/team-and-billing
Invite teammates and understand credits and usage — without the stress.
## Inviting your team
A workspace can be shared with others, so your whole team uses the same chats, apps, and agents.
Click **Settings** in the left menu, then find **Team** or **Members**.
Enter your teammate's email address.
Roles decide what someone can do — for example, a member who can use everything, or an admin who can also manage settings and billing. Pick the right level.
They'll get an email with a link to join your workspace. Once they accept, they're in.
Give people the **least access they need** to do their job. You can always upgrade someone's role later.
## Understanding credits and usage
Using AI costs a small amount of computing each time — measured in **credits** (or **tokens**). Don't overthink it:
> Every message, image, or report uses a little bit of your credit balance — like using a little data on a phone plan.
* **Short, simple tasks** use fewer credits.
* **Long tasks and smarter models** use more.
* You can **see your usage** any time so there are no surprises.
## Checking your usage
Click **Settings**, then **Usage** or **Billing**.
You'll see how much you've used and how much remains. Some plans show a chart over time.
Running low? You can add credits or move to a bigger plan from the **Billing** screen.
## Keeping costs sensible
* **Use faster models** for everyday tasks — they cost less.
* **Be concise** in your prompts (see [Writing good prompts](/user-guide/writing-prompts)).
* **Reuse apps and agents** instead of re-explaining every time.
* **Check usage** now and then so nothing surprises you.
Only workspace admins can change billing or the plan. If you can't see billing options, ask your workspace admin.
## Next step
See how real people use AI Suite to get real work done.
# Welcome 👋
Source: https://docs.sundaypyjamas.com/user-guide/welcome
A friendly, no-jargon guide to the AI Suite platform — centered on Agents that get real work done.
## What is AI Suite?
AI Suite is a **platform** where **Agents** do real work for you — write documents, build spreadsheets, create PDFs, use your tools, and finish multi-step tasks — in plain English, without writing code.
Think of an Agent as a capable teammate: you describe the outcome, and it takes the steps to deliver it.
**You do not need to be technical to use this guide.** Every step has plain-language explanations. Start with Agents — that is the heart of the platform.
## What you'll do most
Open the Agents tab and ask for a task — the Core Agent works it through for you.
Ask the Agent for a brief, proposal, summary, or any write-up you can download.
Turn lists and numbers into a clean sheet you can open and share.
Produce polished PDFs ready to send or print.
Upload files so the Agent can answer from *your* material.
Let Agents work with the apps you already use.
## The 3-minute version
If you only read one thing, read this:
Open your AI Suite workspace and sign in. See [Create your account](/user-guide/create-account).
Click **Agents** in the menu on the left.
For example: *"Write a one-page project brief for a summer campaign and give me a document I can download."*
Watch the Agent work, then open the file in the preview panel. That's the platform in action.
## Your learning path
Create your account, tour the platform, and chat with your first Agent.
Use the Core Agent, create docs/sheets/PDFs, configure, run, and share.
Better prompts, models, knowledge, images, and reports.
Plain Chat, Assistants, and apps — useful extras once Agents feel natural.
Stuck on a word like *agent*, *artifact*, or *model*? Check the [Glossary](/user-guide/glossary) — one plain sentence each.
# A Tour of Your Workspace
Source: https://docs.sundaypyjamas.com/user-guide/workspace-tour
A calm walk around the AI Suite platform so you always know where to click.
## The big picture
When you log in, the screen has three main parts. Once you know these three, you'll never feel lost.
This is how you move around the platform. Each item opens a different area. **Agents** is where you'll spend most of your time.
Shows where you are right now, and holds your profile, notifications, and workspace switcher on the right.
This is where the work happens — your Agent chats, files, settings, and more.
## What's in the left menu?
Here's what each item does, in plain words. Don't memorize it — just know it's here.
| Menu item | What it's for |
| ---------------- | ---------------------------------------------------------------------------------------- |
| **Agents** | **Start here.** Chat with the Core Agent, create docs/sheets/PDFs, run multi-step tasks. |
| **Dashboard** | Your home screen. A quick overview and shortcuts. |
| **Chat** | Quick Q\&A without files or tools — useful, but secondary to Agents. |
| **Images** | Make pictures by describing them. |
| **Reports** | Another way to turn information into written summaries. |
| **Apps** | Build reusable mini AI helpers (optional). |
| **Storage** | Your files and uploaded documents live here. |
| **Integrations** | Connect other tools so Agents can use them. |
| **Settings** | Your account, team, billing, and API keys. |
On a phone, the menu moves to the **bottom** of the screen and shows the most-used items (often including **Agents**). Tap them the same way.
## The top-right corner
* **Workspace switcher** — if you belong to more than one workspace, switch between them here.
* **Notifications (the bell)** — updates and alerts show up here.
* **Your profile** — click your picture or initials to reach account settings and to log out.
## A handy shortcut
Press **Ctrl + K** (or **Cmd + K** on a Mac) anywhere to open a search box. Start typing where you want to go — like "agents" or "settings" — and jump straight there.
## Next step
Open Agents and get a downloadable document in a few minutes.
# Writing Good Prompts
Source: https://docs.sundaypyjamas.com/user-guide/writing-prompts
A 'prompt' is just your message to the AI. Here's how to ask so you get great answers.
## What is a "prompt"?
A **prompt** is simply the message you send to the AI. That's it. When people say "write a good prompt," they just mean "ask clearly."
The AI is smart, but it can't read your mind. The more clearly you describe what you want, the better the result. Good news: you already know how to do this — it's the same as giving clear instructions to a helpful person.
## The 4 ingredients of a great prompt
You don't need all four every time, but the more you include, the better:
What do you want? *"Write an email", "Summarize this", "Make a plan".*
Who is it for? How long? What tone? *"...to a new customer, friendly, under 100 words."*
Give it the material to work with. *Paste the text, notes, or a sample you like.*
How should the result look? *"As 3 bullet points", "As a spreadsheet", "As a PDF I can download."*
Working in **[Agents](/user-guide/first-agent)**? Always name the deliverable: document, spreadsheet, or PDF. See [Create docs, sheets & PDFs](/user-guide/agents/create-files).
## See the difference
> *write about our product*
The AI has to guess everything: which product, who's reading, how long, what tone. You'll get something generic.
> *Write a short product description (about 50 words) for our reusable water bottle. The reader is a busy parent. Friendly and warm tone. Mention that it keeps drinks cold for 24 hours.*
Now the AI knows exactly what you want. You'll get something you can almost use as-is.
## Simple habits that instantly help
* **Say who it's for.** "for beginners", "for my boss", "for kids".
* **Say how long.** "one sentence", "a short paragraph", "under 100 words".
* **Say the tone.** "friendly", "professional", "playful", "serious".
* **Give an example.** Paste something you like and say "in this style".
* **Ask for a format.** "as bullet points", "as a table", "step by step".
## You can always fix it afterwards
You don't have to get the perfect prompt on the first try. Just reply with a small change:
* *"Make it shorter."*
* *"More formal, please."*
* *"Add a friendly closing line."*
* *"Turn this into bullet points."*
The AI remembers the conversation and adjusts. This back-and-forth is often faster than writing one perfect prompt.
Stuck? Ask the AI to help you ask: *"What details do you need from me to write a great LinkedIn post?"* It will tell you exactly what to add.
## Next step
Turn your information into a polished written summary.