Send Message
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 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
Fraction of context window used (0–1)
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" }'
{
"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)
POST /api/v1/apps/{appId}/chat/message/stream
Same request body as Send Message, plus:
Response
Content-Type: text/event-stream. Each data: event is one of:
{ "type": "token", "content": "Refunds " }
{
"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
}
}
{ "type": "error", "code": "MODEL_ERROR", "message": "..." }
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 |