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

MethodEndpointAuthDescription
POST/runsAPI KeyCreate a single-turn agent run
GET/runs/{job_id}API KeyPoll run status and result
POST/conversationsAPI KeyCreate a multi-turn conversation
GET/conversations?user_id=API KeyList conversations for a user
GET/conversations/{conv_id}API KeyGet conversation with full message history
POST/conversations/{conv_id}/messagesAPI KeySend a user message, get pending_id
GET/conversations/{conv_id}/poll/{pending_id}API KeyPoll for assistant reply
DELETE/conversations/{conv_id}API KeyDelete a conversation
POST/appsBearerCreate agents config for an app
GET/apps/{app_id}/agents-configBearerGet agents config
PUT/apps/{app_id}/agents-config/llmBearerConfigure LLM provider + API key
PUT/apps/{app_id}/agents-config/workflows/{name}/promptBearerOverride system prompt for a workflow
GET/apps/{app_id}/agents-config/workflows/{name}/promptBearerGet current prompt override
DELETE/apps/{app_id}/agents-config/workflows/{name}/promptBearerRemove prompt override (revert to default)
GET/healthNoneService 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

WorkflowInputOutput
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

StatusMeaning
pendingQueued, worker not yet started
runningWorker is executing the agent
completeAgent succeeded, result is available
failedAgent 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

WorkflowDescription
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

StatusMeaning
idleNo turn in flight — ready for next message
processingWorker is generating the assistant reply
completeLast turn completed successfully
failedLast 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.

iOS only. ChatGPT Plus connection requires an iOS app using WKWebView. Web connect is not supported. Android support is planned.

ChatGPT Connect Endpoints

MethodEndpointAuthDescription
POST/chatgpt/connectAPI KeyGet OAuth authorize URL + session_id
POST/chatgpt/callbackNoneComplete OAuth exchange after WKWebView intercept
GET/chatgpt/statusAPI KeyCheck connection state (connected / expired)

iOS Integration

Add a "Connect ChatGPT" button in your iOS app. When tapped:

  1. Call POST /chatgpt/connect — get auth_url and session_id
  2. Open auth_url in a WKWebView sheet
  3. Intercept the localhost:1455 redirect in WKNavigationDelegate
  4. Send the intercepted URL to POST /chatgpt/callback with session_id
  5. 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