Agents Service
Mushu Agents lets your app trigger AI agent workloads asynchronously. Two modes: single-turn runs (photo in, structured JSON out) and multi-turn conversations (chat with an LLM agent across multiple messages, with history).
Bring your own LLM key (OpenAI, Anthropic, or any OpenAI-compatible endpoint). You can also let end-users connect their ChatGPT Plus subscription (BYOS) so they fund their own inference — iOS only, no code change required after setup.
Endpoints Overview
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST | /runs | API Key | Create a single-turn agent run |
GET | /runs/{job_id} | API Key | Poll run status and result |
POST | /conversations | API Key | Create a multi-turn conversation |
GET | /conversations?user_id= | API Key | List conversations for a user |
GET | /conversations/{conv_id} | API Key | Get conversation with full message history |
POST | /conversations/{conv_id}/messages | API Key | Send a user message, get pending_id |
GET | /conversations/{conv_id}/poll/{pending_id} | API Key | Poll for assistant reply |
DELETE | /conversations/{conv_id} | API Key | Delete a conversation |
POST | /apps | Bearer | Create agents config for an app |
GET | /apps/{app_id}/agents-config | Bearer | Get agents config |
PUT | /apps/{app_id}/agents-config/llm | Bearer | Configure LLM provider + API key |
PUT | /apps/{app_id}/agents-config/workflows/{name}/prompt | Bearer | Override system prompt for a workflow |
GET | /apps/{app_id}/agents-config/workflows/{name}/prompt | Bearer | Get current prompt override |
DELETE | /apps/{app_id}/agents-config/workflows/{name}/prompt | Bearer | Remove prompt override (revert to default) |
GET | /health | None | Service health check |
Authentication
Data plane endpoints (/runs, /conversations) require a Tenant
API key as X-API-Key. Management endpoints (/apps/*) use Bearer
token auth. See the API Keys guide for setup.
Single-Turn Runs
Submit a media URL and workflow name. Poll until complete.
Best for stateless tasks like food photo analysis.
Workflows
| Workflow | Input | Output |
|---|---|---|
food-log | mushu-media R2 URL (food photo) | 39-nutrient breakdown JSON for HealthKit write |
Submit a run
POST /runs
X-API-Key: ms_your_key
{
"user_id": "usr-123",
"workflow": "food-log",
"media_url": "https://media.mushucorp.com/..."
} Response:
{
"job_id": "550e8400-e29b-41d4-a716-446655440000"
} Poll for result
GET /runs/550e8400-e29b-41d4-a716-446655440000
X-API-Key: ms_your_key Run status values
| Status | Meaning |
|---|---|
pending | Queued, worker not yet started |
running | Worker is executing the agent |
complete | Agent succeeded, result is available |
failed | Agent failed, error message included |
Multi-Turn Conversations
Conversations maintain history across multiple messages. Each user message is enqueued
asynchronously — send a message, get a pending_id, poll until the assistant
replies.
Conversation workflows
| Workflow | Description |
|---|---|
meal-plan | Multi-turn meal planning assistant. Maintains diet preferences, goals, and past meals across messages. |
Create a conversation
POST /conversations
X-API-Key: ms_your_key
{
"user_id": "usr-123",
"workflow": "meal-plan"
}
// Response:
{
"conv_id": "c7a1e3f2-..."
} Send a message
POST /conversations/c7a1e3f2-.../messages
X-API-Key: ms_your_key
{
"content": "I want to lose 10 pounds in 3 months, I'm vegetarian"
}
// Response:
{
"pending_id": "p9b2d4a1-...",
"status": "pending"
} Poll for the assistant reply
GET /conversations/c7a1e3f2-.../poll/p9b2d4a1-...
X-API-Key: ms_your_key
// Pending:
{
"status": "pending"
}
// Complete:
{
"status": "complete",
"result_text": "Great! Here's a 7-day plan...",
"structured_result": { ... }
}
Poll every 2–3 seconds until status is complete or failed.
A typical LLM response takes 3–15 seconds.
Conversation status values
| Status | Meaning |
|---|---|
idle | No turn in flight — ready for next message |
processing | Worker is generating the assistant reply |
complete | Last turn completed successfully |
failed | Last turn failed — conversation still usable |
Concurrent message guard
Sending a message while a turn is still in flight returns 409 Conflict:
{
"detail": {
"error": "conversation_busy",
"active_pending_id": "p9b2d4a1-..."
}
}
Use the returned active_pending_id to resume polling — don't send a new message
until the current turn completes.
Get conversation with history
GET /conversations/c7a1e3f2-...
X-API-Key: ms_your_key
{
"conv_id": "c7a1e3f2-...",
"workflow": "meal-plan",
"status": "idle",
"created_at": "2026-04-14T10:00:00Z",
"messages": [
{ "role": "user", "content": "I want to lose...", "created_at": "..." },
{ "role": "assistant", "content": "Great! Here's...", "created_at": "..." }
]
} List conversations for a user
GET /conversations?user_id=usr-123
X-API-Key: ms_your_key Python SDK — send and wait
The Python SDK ships a send_and_wait helper that handles the poll loop for you:
from mushu.agents.api.conversations import send_and_wait, create_conversation
from mushu import AuthenticatedClient
client = AuthenticatedClient(base_url="https://agents.mushucorp.com", token="ms_your_key")
# Create conversation
conv = create_conversation.sync(client=client, user_id="usr-123", workflow="meal-plan")
# Send message and block until complete
result = send_and_wait.sync(
client=client,
conv_id=conv.conv_id,
content="I want a high-protein plan for muscle gain",
poll_interval=2.0,
timeout=60.0,
)
print(result.result_text) LLM Configuration
Register your LLM provider once per app. The API key is stored encrypted in AWS SSM Parameter Store — it is never returned via API.
PUT /apps/app-123/agents-config/llm
Authorization: Bearer <token>
{
"llm_base_url": "https://api.anthropic.com",
"llm_model": "claude-haiku-4-5-20251001",
"api_key": "sk-ant-..."
}
Any OpenAI-compatible endpoint works. Set llm_base_url to the base URL
(e.g., https://api.openai.com) and llm_model to the model name
you want to use.
Custom workflow prompts
Override the built-in system prompt for any workflow on a per-app basis. Useful for personalizing tone, adding brand context, or restricting topics.
PUT /apps/app-123/agents-config/workflows/meal-plan/prompt
Authorization: Bearer <token>
{
"system_prompt": "You are a nutrition coach for FitApp users. Focus on whole foods, avoid ultra-processed ingredients, and always include macro breakdowns."
}
Pass an empty string or DELETE the override to revert to the built-in default.
DELETE /apps/app-123/agents-config/workflows/meal-plan/prompt
Authorization: Bearer <token> BYOS — ChatGPT Plus Connect (iOS)
BYOS (Bring Your Own Subscription) lets end-users connect their ChatGPT Plus subscription so they fund their own inference instead of your OpenAI API credits. Runs automatically route through the connected subscription — no code change required after setup.
ChatGPT Connect Endpoints
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST | /chatgpt/connect | API Key | Get OAuth authorize URL + session_id |
POST | /chatgpt/callback | None | Complete OAuth exchange after WKWebView intercept |
GET | /chatgpt/status | API Key | Check connection state (connected / expired) |
iOS Integration
Add a "Connect ChatGPT" button in your iOS app. When tapped:
- Call
POST /chatgpt/connect— getauth_urlandsession_id - Open
auth_urlin a WKWebView sheet - Intercept the
localhost:1455redirect inWKNavigationDelegate - Send the intercepted URL to
POST /chatgpt/callbackwithsession_id - Get
{ connected: true }— show confirmation, dismiss sheet
WKWebView Intercept (Swift)
func webView(_ webView: WKWebView,
decidePolicyFor action: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if let url = action.request.url,
url.host == "localhost", url.port == 1455 {
decisionHandler(.cancel)
onRedirect(url.absoluteString) // send to /chatgpt/callback
return
}
decisionHandler(.allow)
} Handling Token Expiry
ChatGPT Plus OAuth tokens expire periodically (typically every 10–30 days). When a token expires, the next run will fail with:
status: "failed"
error: "ChatGPT subscription expired or token revoked. Reconnect via your iOS app."
Call GET /chatgpt/status to check. When expired: true,
prompt the user to reconnect via the ChatGPT connect flow in your iOS app.
Check connection status with the CLI:
mushu agents chatgpt-status --api-key ms_your_key API Reference
Interactive docs: agents.mushucorp.com/docs