# 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 timestamp ISO 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 ID Idempotency/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 8601 Echoes request `scope` Echoes request `period` `"customer_payload"` 120–400 word summary 1–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 used Resolved prompt actually sent to the model `"generate"` or `"edit"` Base64-encoded PNG. Present when S3 storage isn't enabled. Profile used, if any Present only with S3 storage enabled Present only with S3 storage enabled Signed URL to the raw image. Present only with S3 storage enabled Signed 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 longer Per-component cost breakdown Up 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 Schema JSON 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 answer 0–1 confidence score for the answer Relevant text excerpt Source document name Token/quota usage for this query Timing/processing metrics ISO 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 label App ID, run ID, or upload ID that produced this file Signed S3 URL, valid 15 minutes Logical path in the workspace tree (`type: "file"` only) `none` · `queued` · `processing` · `ready` · `failed` True if this file was imported from GitHub GitHub 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 && (
{streamingResponse}
...
)}
setInput(e.target.value)} onKeyPress={(e) => e.key === 'Enter' && sendMessage()} placeholder="Type your message..." disabled={isLoading} />
); }; 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. A generated file shown in the preview panel next to the chat 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). An agent using a tool while working on a task ## 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 Core Agent configuration screen with its settings ## 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.* A chat with the agent, giving it a task The agent shows the **steps** it takes — like *searching* or *reading a file*. This is normal and helps you trust the result. An agent run showing a list of steps it performed 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**. The share panel for an agent session with a copyable link 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. The Agents tab with the session list, chat tabs, and preview panel labelled 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. A dropdown for choosing which agent to use 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. The Agents tab showing the session list, chat, and the Core Agent ## 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**. The create-assistant screen 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. Form for the assistant name and instruction text 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. A test chat with the new assistant 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). The Apps section with a list of apps and a 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. An app playground showing a test conversation 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. A dropdown menu showing a list of available AI models 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. AI Suite login screen with Sign in and Sign up tabs 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**. Sign in form with email and password fields 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. The Agents tab showing sessions, chat, and the Core Agent 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. An agent run showing steps it performed When a file is ready, it appears in the **preview panel** on the right. Read it, then use **copy** or **download**. A generated file in the preview panel next to the chat 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. Empty chat screen with a message box at the bottom 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. Chat showing the user's message and the AI's written reply 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. The Images tool with a description box and a generate button 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. A generated image shown in the Images tool 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. A gallery of available integrations and connectors 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. An upload area for adding documents to the AI's knowledge 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?* A chat answer that cites the uploaded document 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**. The create managed agent screen 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. A managed agent showing its run steps and 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. The Reports section listing existing reports and a create button 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. A generated report with headings and organized sections 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. A Share button on a report, with a sharing panel 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. A copyable share link with a copy button ## 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**. A team members list with an invite button 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**. A usage dashboard showing credits used and remaining 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. Full workspace with the sidebar, top bar, and main area labelled 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 Top bar right-hand side with workspace switcher, notifications bell, and profile menu * **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. Command search box open with a list of destinations ## 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.