HTTP API
POST /v1/ui
An AG-UI run (protocol 1.0), what the MorphScreen SDKs read.
Keys
Your MorphScreen key in X-MorphScreen-Key: the call counts against your plan. Your own OpenRouter key in Authorization: Bearer sk-or-... when you want a model other than the one in your plan: it runs on that key, and goes to OpenRouter and nowhere else. Leave it out to use the model in your plan.
Example
An AG-UI run, which connectAgent sends for you; the model and the options go in forwardedProps:
curl -N https://api.morphscreen.com/v1/ui \
-H "X-MorphScreen-Key: $MORPHSCREEN_KEY" \
-H "Content-Type: application/json" \
-d '{"threadId": "t1", "runId": "r1", "tools": [], "context": [], "state": {},
"messages": [{"id": "m1", "role": "user", "content": "Quiz me on photosynthesis"}],
"forwardedProps": {"model": "deepseek/deepseek-v4.1-flash", "packs": ["interactive"]}}'Request
RunAgentInput
AG-UI's RunAgentInput (protocol 1.0). Tools aren't supported yet: tools must be empty.
| Field | Type | Required | What it is |
|---|---|---|---|
threadId |
string | ||
runId |
string | ||
messages |
list of object | ||
tools |
list of any | At most 0. | |
state |
any | ||
context |
list of any | ||
forwardedProps |
object | yes | The model and the options. output: agui (AG-UI events, the default) or ops (the core's op stream). |
forwardedProps
The model and the options. output: agui (AG-UI events, the default) or ops (the core's op stream).
| Field | Type | Required | What it is |
|---|---|---|---|
model |
string | yes | |
packs |
list of string | ||
components |
list of object | ||
image_hosts |
list of string | ||
images |
list of object | At most 200. | |
photos |
one of the shapes below | The app's own photo key, for one provider: with it the model may ask for photos, searched there on the app's quota, each loading from the provider's own address with its credit. Without one (or false) the model isn't asked for photos. | |
output |
agui, ops |
||
repair |
boolean |
AppImage
One of your own pictures. The model shows it as and the API puts its URL and credit in; its host is allowed with it.
| Field | Type | Required | What it is |
|---|---|---|---|
id |
string | yes | Letters, digits, '-', '_' or '.'. |
url |
string | yes | Where it loads from, http(s). |
alt |
string | What it shows, told to the model. | |
credit |
string | Shown under it, as written (Markdown links allowed). |
Responses
| Status | What comes back |
|---|---|
| 200 | Server-sent events: AG-UI events (output agui), or the core's ops (output ops). |
| 400 | The request can't be run as written; model_needs_your_key: a model other than the included one, asked for without your own OpenRouter key. |
| 401 | No key, or one turned down: your MorphScreen key (morphscreen_key_required, morphscreen_key_invalid) or your OpenRouter key (openrouter_key_required, openrouter_key_invalid). |
| 402 | This month's calls on your plan are used (plan_quota_reached), a paid plan's month and as many again as extra (extra_limit_reached), the free plan's calls with the model included (included_calls_used, or included_calls_paused while our credit is low), or the OpenRouter account is out of credit (openrouter_out_of_credit). |
| 403 | The account is suspended (account_suspended), or the model's moderation refused the request (model_refused). |
| 404 | No such model. |
| 413 | The body is over 1 MB (payload_too_large), or a call with the model included has more than about 8,000 tokens of conversation (conversation_too_long). |
| 429 | A limit reached, with Retry-After saying how long to wait: requests a minute (rate_limited), answers at once (too_many_streams), the free tier's day (daily_limit_reached), renders this month (render_limit_reached), or OpenRouter's own (openrouter_rate_limited). |
| 502 | The model failed or couldn't be reached, after the retries. |
| 503 | Accounts can't be read just now; try again. |
| 504 | The model took too long to start. |
Errors come back as {"error": {"code", "message", "type", "metadata": {"error_type", "reason"}}}: type is the reason to act on (rate_limited, plan_quota_reached...). Limits and errors says what to do about each.