A task prompt is a different craft from an agent prompt. Nobody is listening, so none of the speech rules apply — but the run has to finish, and the prompt is what makes it finish. The two failures almost every task hits, no_output and step_limit, both trace back to something the prompt did or did not say. Prompting an agent covers the conversational half and deliberately does not cover this.

You do not write the ending

When a run starts, the task gains a generated tool named submit_result, built from your output fields, and a generated paragraph appended to your prompt that starts:
It goes on to say to send every field, to send null rather than omitting or guessing, and then lists each field with its type and its description. The tool and the paragraph are both written for you, on every run, however your prompt is written. The full shape is in inputs and output.
Never write that paragraph into your prompt, and never declare a tool named submit_result. The name is reserved workspace-wide — creating a tool with it is refused with 'submit_result' is reserved by the platform — and a hand-written copy of the paragraph is not an extra instruction, it is a second one. The generated text is appended after yours on every run, so the model ends up reading two accounts of how to finish, and no surface shows you that.
So your prompt carries the work, not the exit. Everything about how the run ends is handled. Everything about what the run is for, where to look, and what counts as a good answer is yours.

The shape that works

Five things, in this order. The last one is the section people leave out, and it is the one that costs steps. A model with no permission to fail keeps searching, and a task that keeps searching runs out of rounds. Here is a complete prompt for a research task:
Note what is not in it: no instruction to call submit_result, no list of the output fields, no explanation of nulls beyond the policy. All three are generated from output.

Field descriptions are part of the prompt

Every output field is required and nullable, so the model has to say something about every one of them — including “I could not find it”. The field’s description is the only instruction it gets about what belongs there, because that description becomes the argument’s description in the generated tool schema. It is literally the prompt for that value. Which means a vague description does not produce a vague value. It produces a different value on every run. The pattern: name the vocabulary or the length, name where to look, and name the null case. Inputs and output has more pairs.
Descriptions are not templated. {{vars.our_product}} in a field description reaches the model as those literal braces — only the prompt is substituted. Anything a description needs to know about this run’s inputs has to be said in the prompt instead.

Writing for the two failure modes

no_output and step_limit are the same event underneath: the run ended without calling submit_result. They are reported apart because the fix is different, and doing the other one is wasted work. Do not rewrite a prompt that reported step_limit. The prompt was not at fault, and the fix is a number in the task’s Limits section. Rewriting it is the one piece of work these two error types exist to stop you doing. attempts tells you how much the platform already tried on your behalf. A run that ends with no result is re-prompted rather than failed, up to twice, so attempts is 1, 2 or 3 — and each re-prompt starts the step budget over. Read it beside the status:
  • attempts: 1 on a completed run is the healthy shape. It worked first go.
  • attempts: 2 or 3 on a completed run means one or two earlier attempts ended with no result and were thrown away. It worked, at two or three times the tokens, and it will start failing as soon as the work gets slightly harder. Fix it now rather than when it breaks.
  • A no_output failure means every attempt ended in prose. The re-prompts already told the model to produce the result and it still did not, so the problem is upstream of that: look for a sentence in your prompt pulling it towards an essay — “write a summary”, “produce a report”, “explain”.
  • A step_limit failure with attempts: 3 is three separate attempts that each burned the full step budget, so a task with max_steps: 25 did up to 75 rounds of work before anything gave up. The clock stopped it, not max_steps.
Every error.type, and what each one sends you to change, is in errors and limits.

Prompting for tools and MCP servers

The model can only call what is attached, and it decides what to call from the tool’s own name and description — the same as an agent. What a task’s prompt adds is order, because a task’s work usually has one.
  • Name tools by the name the model sees. For a tool that is its name. For an MCP tool it is the namespaced exposed_name — an integration with a tools_namespace of crm turns the server’s search into crm_search, and a prompt saying “use search” names nothing. GET /v1/integrations/{integration_id}/mcp-tools lists both names for every tool a server offers. See MCP servers.
  • Say the order, and say when to stop. “Call crm_search first with the domain. Only if it returns no rows, fall back to web_lookup.” A model given three tools and no order will often call all three.
  • Say what an empty result means. This is the single highest-value sentence in most task prompts. “If crm_search returns an empty list, the company is not a customer — say so and move on. Do not search again with a different query.” Without it, an empty result reads to the model as a failed attempt, and it retries until the steps run out.
  • Say what not to call. A task attached to a write-capable MCP server should be told which of its tools are off limits, in the prompt, as well as narrowed through allowed_tools on the integration.
A task can be told to think. Nobody is waiting on silence, so reasoning_effort on the model costs seconds and tokens rather than a caller’s patience — and a prompt that says “work out which source is more likely to be current before you read either” is a reasonable instruction here in a way it never is on a voice call. See reasoning and builtin tools.

Iterating

Run it, read the trace, change one thing. The trace is every tool call and everything the model wrote, in order, and it answers the question a prompt edit should be based on. Four things it tells you: That last one is worth reading closely: when the arguments do not match the schema the model is told what is wrong and may call the tool again, so a run can finish successfully having wasted two rounds on a description nobody wrote properly. Change one thing between runs. A task run is cheap and reproducible in a way a call is not — the same vars produce a comparable run every time — so there is no reason to bundle three edits and guess which one worked.

Complete examples

max_steps is raised above the default 25 because a research task genuinely searches, and timeout_seconds is raised with it — the step budget is the runaway guard and the clock is the real bound.
max_steps: 3 is deliberate. The task calls no tools, so a healthy run uses one step — the submit_result call itself — and a run that somehow needs thirty rounds has gone wrong in a way that should fail fast rather than burn tokens.Notice needs_human says “Send null never; decide”. Every field is nullable in the generated tool and there is no way to turn that off, so a field that must always have a value says so in its description.
The whole prompt is about what not to do, because extraction fails by the model being helpful: converting a currency, annualising a figure, reading a term off a date. total_amount is a number and payment_terms_days an integer, so neither can come back as "₹4,20,000" or "net 30".

Next

Errors and limits

Every error.type, and the two budgets that bound a run.

Inputs and output

The generated submit_result tool, and required-and-nullable.

Running and runs

Read a run, its trace, attempts and steps_used.

Tools and MCP servers

Attach the things the prompt tells the model to call.