HTTP API

POST /v1/ui

An AG-UI run (protocol 1.0), what the MorphScreen SDKs read.

View as Markdown

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:

Terminal
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 alt 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.