Build your own helper¶
A helper is a feature of your product that writes one thing from a description: a summary of a
support ticket, a reply to an e-mail, a search query from a question in plain language. You build
one on POST /responses with your own instructions and, where your code reads the answer, your own
JSON Schema. Name the helper in the helper field and Core meters and traces every call as that
helper's work, so you can see what each helper costs. Leave out model and the turn runs on your
organisation's helper model.
A helper turn is an ordinary turn otherwise. Everything on Make a model call
applies; this page covers what helper adds.
Name the helper¶
helper is a Core extension on the request: a name you choose, which Core carries on everything the
turn spends. Core keeps no list of your helpers and checks a name only against these rules:
- two to four segments separated by dots, so there is always at least one dot;
- each segment starts with a lowercase letter, followed by lowercase letters, digits or
_; - at most 64 characters;
- not starting with
assist., which is reserved for Core's own helpers.
Put your product or company in the first segment, so the name says whose helper it is:
acme.summary, acme.ticket_triage, acme.mail.reply. A name that breaks a rule, such as
summary, Acme.Summary or assist.summary, is refused with 400 and helper in error.param.
Use one name per helper and keep it stable. The name is what you filter usage by later, so a helper renamed halfway through a month shows up as two.
Use the organisation's helper model¶
Your organisation can choose a helper model: the model that writes for helpers, the console's
own Describe what you need included. An administrator sets it on the organisation's
settings, and a key holding management sets it as
assist_model with PATCH /organisation
(Manage organisations through the API).
Without a choice of its own, the organisation uses the deployment's default.
Leave out model on a turn that names a helper, and the turn runs on that helper model. Your
code then does not need to know which model the organisation chose, and an administrator can switch
models without a change to your product.
| Request | Model that answers |
|---|---|
helper, no model, no agent |
the organisation's helper model |
helper and model |
the model you named |
helper and an agent that fixes a model |
the agent's model |
helper and an agent that fixes no model, no model |
the organisation's helper model |
When no helper model is available, the turn is refused with 409 before any model is called:
| Message | Meaning | What to do |
|---|---|---|
No helper model is configured for this organisation. |
neither the organisation nor the deployment has chosen a helper model | ask an administrator to choose one, or name a model on the turn |
The helper model this organisation chose is no longer available. |
the organisation chose a model that its catalogue no longer holds; Core does not fall back to the deployment's default | ask an administrator to choose another model |
A key holding the assist capability can check in advance with GET /assist, which answers
{"model": ..., "source": ...}: the helper model in force and whether the organisation or the
deployment chose it, or null for both when there is none. A helper turn itself needs no
capability beyond what any turn needs.
Send a helper turn¶
This helper writes a short summary of a support ticket and gives it a priority. The answer follows a
JSON Schema, so your code reads fields instead of parsing text
(Structured output). store: false keeps
nothing of the turn: a summary like this is shown once and never continued.
curl -s "$CORE_URL/responses" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-d '{
"helper": "acme.summary",
"instructions": "Summarise the support ticket in at most two sentences and rate its priority.",
"input": "Since this morning no customer can log in to the portal. We get error 502.",
"store": false,
"text": {
"format": {
"type": "json_schema",
"name": "ticket_summary",
"strict": true,
"schema": {
"type": "object",
"properties": {
"summary": { "type": "string" },
"priority": { "type": "string", "enum": ["low", "normal", "high"] }
},
"required": ["summary", "priority"],
"additionalProperties": false
}
}
}
}'
The answer is an ordinary response object. output_text holds the JSON your schema describes, and
model names the helper model that answered. The response does not repeat helper: neither this
answer nor GET /responses/{id} carries it. When the model's answer does not match the schema, the
turn fails with invalid_structured_output and you get no partial answer.
The OpenAI Python SDK does not know helper, so pass it through extra_body:
import json
from openai import OpenAI
client = OpenAI(base_url="https://<your-deployment>/api/v1", api_key="ack_...")
response = client.responses.create(
instructions="Summarise the support ticket in at most two sentences and rate its priority.",
input="Since this morning no customer can log in to the portal. We get error 502.",
store=False,
text={
"format": {
"type": "json_schema",
"name": "ticket_summary",
"strict": True,
"schema": {
"type": "object",
"properties": {
"summary": {"type": "string"},
"priority": {"type": "string", "enum": ["low", "normal", "high"]},
},
"required": ["summary", "priority"],
"additionalProperties": False,
},
}
},
extra_body={"helper": "acme.summary"},
)
ticket = json.loads(response.output_text)
helper exists only on POST /responses. POST /messages has no such field.
Keep the turn, or not¶
store works as on any turn (Storage settings). For a
one-off helper, such as a summary, a translation or a draft your user edits further in your own
product, send store: false: the call answers 200 and Core keeps no conversation. Leave store
as it is when you want to read the turn back or continue it, for instance a helper your user can ask
to try again. Usage is metered in both cases.
Rules that apply as on any turn¶
Naming a helper does not loosen anything. The turn is your key's own turn, under your own instructions:
- Policy. Your key's policy decides whether the helper model, or the model you named, may be
used and where it may run. A model the policy refuses is answered
403, as on any turn. - Caps. Your key's and your organisation's monthly caps
admit the turn. A reached cap answers
429. - Personal data. When your key holds
pseudonymisation, personal data in the description is hidden from the model as on every turn (Pseudonymisation). - Tools, fallback and streaming work as on any turn. A helper turn may call tools, falls back along the model's fallback chain and can stream.
Helpers beyond one call¶
The helper name stays with the work wherever the turn takes it:
- A background run (
"background": true, Run a turn in the background) keeps the helper for every round. - A task an agent starts from a helper turn is the helper's work too: the task's run and what it spends carry the same helper name.
- An agent called as a tool during a helper turn spends for the helper as well. Its usage rows name both the agent and the helper.
- A schedule stores
helperwith the request it fires, but that request must name amodel, or an agent version that fixes one. Core checks a schedule's request when you write it, and the helper model is only looked up when a turn starts, so a request with a helper and no model is refused with422onrequest.model:Name a model, or an agent version that fixes one.(Schedules).
See what a helper costs¶
Every usage row the turn writes carries the helper's name in helper: the model calls, the tools it
ran and the turns of agents it called. Core's own helpers show up beside yours under their own
names, such as assist.json_schema for the JSON Schema draft in the console.
GET /usage?helper=acme.summarylists one helper's rows. The match is exact, never a prefix:acmedoes not findacme.summary.GET /usage/summarysplits every group by helper, so each total carrieshelperbesidemodelandagent. Rows no helper wrote form groups withhelper: null.- On the console's Usage section, the Cost tab has a Per helper card: one line per helper with what its work cost in the chosen period (Usage in the console).
- On the console's Traces section, enter the helper's name under More filters in Job kind to see only that helper's model calls (Read a trace). This filter is in the console only; no trace list a key reads takes it.
curl -s "$CORE_URL/usage?helper=acme.summary&from=2026-10-01T00:00:00Z" \
-H "Authorization: Bearer $CORE_KEY"
More on the usage routes is in Usage and audit through the API.