Skip to content

Calling agents from other systems ​

An agent you publish in Console is not locked inside Studio. Any system that can send an HTTP request, or that already speaks the OpenAI API, can call it: a chat front end such as Open WebUI, a workflow tool such as n8n, a CI pipeline, a cron job, another agent.

This section shows how, one system at a time, with copy-paste examples. This page is the map: which surface to use, what the calls have in common, and the words you will see everywhere (runs, classes, ids, callbacks).

Ways to call an agent ​

Every surface below starts the same engine and produces the same agent run. They differ only in how the request and the answer are shaped.

SurfaceEndpointUse it when
Runs API (canonical)POST /api/client/v1/agents/{agentKey}/runsYou write the client. It is the most complete surface: async-first, scheduling, classes, callbacks, metadata, idempotency, listing, cancel. Start here for CI, workflows and backends.
OpenAI ResponsesPOST /api/client/v1/responses with model: "agent:<key>"You already use the OpenAI SDK (client.responses.create). Streaming, background, text.format, retrieve and cancel all work.
OpenAI chat completionsPOST /api/client/v1/chat/completions with model: "agent:<key>"The client only knows chat completions: Open WebUI, LiteLLM, LangChain's ChatOpenAI, most chat front ends.
A2APOST /api/client/v1/a2a/{agentKey} (JSON-RPC)Another agent framework talks to yours with the Agent2Agent protocol. Switch on Publish via A2A in the agent's Publish tab first (how).
TriggersConfigured per agentNobody calls the agent: it starts itself on a schedule, once at a given time, by hand, or when a webhook arrives. An agent can have several triggers. See Triggers.
Webhook triggersPOST /api/public/hooks/{tenantId}/{hookId}, the URL of a webhook triggerA system should push events to the agent (a pull request, a failed build, an alert) and you do not want to write the glue. GitHub, GitLab, Azure DevOps and any sender of JSON. See Webhook triggers.

/api/client/v1/agents/responses stays as an agents-only alias of the Responses endpoint.

One base URL, one key

All of these live under https://<your-console>/api/client/v1 and take the same API token (Authorization: Bearer cpeer_…). The token needs the agents service; for the OpenAI endpoints a token that has only models can still call models but not agents. The run must belong to the token's project.

Choosing the target on the OpenAI endpoints ​

/responses and /chat/completions serve both models and agents. Console resolves the model field in this order:

  1. agent:<key> is an agent.
  2. Otherwise a model key in the project is a model.
  3. Otherwise an agent key in the project is an agent.
  4. Otherwise 404 model_not_found.

Prefer the explicit agent:<key> form: it cannot collide with a model that has the same key. GET /api/client/v1/models lists every active agent of the project as agent:<key> next to the models, so a client that discovers models (Open WebUI, LiteLLM) finds your agents on its own.

Agents run their published version. An agent that was never published runs its draft. Send "version": 3 to pin a version, or "version": "draft" to test the draft explicitly.

Variables: values next to the message ​

An agent can declare variables: named, typed values (text, number, integer, yes/no or JSON; required or optional; with allowed values and defaults) that the caller sends as variables next to the message. Console checks them before the model is called, so a missing required value or a wrong type is a 400 invalid_variables and nothing is spent. The agent's instructions can use them as {{variables.name}}, and the model also receives them as a Variables block appended to the message. GET /api/client/v1/agents/{agentKey} returns the declaration as variables_schema, so a client knows what to send.

Use variables when a program makes the call: the runs API from your code, triggers, n8n and CI pipelines, an agent called by another agent. Do not rely on them in chat: Open WebUI and the other chat clients send only the person's text, so a required variable without a default makes every chat message fail with 400 invalid_variables. For an agent that is also used in chat, keep variables optional or give each one a default.

One request, three outcomes ​

Whatever the surface, a request ends in one of three ways.

You getMeaning
200 with the answerThe turn finished inside the wait window.
202 with a runThe window ended and the turn is still going, or the run is queued, scheduled or deferred. The run keeps going in the background. Follow it with the runs API.
4xxThe request was refused before any work started (validation, permission, a cap, a conflict).

The wait window is how long the server holds the connection for you:

  • On the OpenAI endpoints and A2A the window is the server's AGENT_SYNC_TIMEOUT_MS (10 minutes by default).
  • On the runs API you ask: "wait": 30 (seconds, at most the window). The default is 0, which means async-first: you get 202 and a run id at once. Prefer: wait=30 and Prefer: respond-async are honoured too.

When the window ends, the turn is not cut off. Before this release a slow synchronous call was terminated with 504; now it is detached, you get 202, and the run finishes in the background. The ceiling for any run is AGENT_BACKGROUND_MAX_DURATION_MS (30 minutes by default), and a run that exceeds it fails with max_duration_exceeded. A 202 answer carries a Location header (the run in the runs API) and Retry-After: 2. If the run a waiting call is attached to is ended elsewhere before it finishes (the server shut down and handed its runs over, or the worker was lost), the call answers 503 agent_run_interrupted (retryable; tool side effects may have happened) instead of a partial answer, and a stream ends with an error event and response.failed.

bash
curl -sS -X POST "$CONSOLE_URL/api/client/v1/agents/support-bot/runs" \
  -H "Authorization: Bearer $COGNIPEER_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "input": "Summarise yesterday’s open tickets", "wait": 30 }'
bash
RUN=$(curl -sS -X POST "$CONSOLE_URL/api/client/v1/agents/support-bot/runs" \
  -H "Authorization: Bearer $COGNIPEER_API_TOKEN" -H "Content-Type: application/json" \
  -d '{ "input": "Summarise yesterday’s open tickets" }')
RUN_ID=$(echo "$RUN" | jq -r .id)

# The server holds this request for up to 60 s and answers as soon as the run settles.
curl -sS "$CONSOLE_URL/api/client/v1/agents/runs/$RUN_ID?wait=60" \
  -H "Authorization: Bearer $COGNIPEER_API_TOKEN" | jq '{status, output_text}'

Streaming ​

"stream": true on the runs API, the Responses API and chat completions answers with Server-Sent Events. The runs API and Responses use the Responses event vocabulary (response.created, response.output_text.delta, …, response.completed); chat completions uses the usual chat.completion.chunk frames and [DONE]; A2A uses message/stream. A keep-alive comment (: ping) arrives every 15 seconds so proxies do not close an idle stream.

If the client disconnects from a chat completions or Responses stream, the turn is cancelled, which is what the OpenAI API does too. To keep it running, send "background": true (Responses). On the runs API a dropped stream continues by default ("on_disconnect": "cancel" stops it instead). The run then continues detached, and you follow it with GET /agents/runs/{id}. On A2A a dropped message/stream does not cancel the task either: reattach with tasks/resubscribe, or stop it with tasks/cancel.

Background, deferred and scheduled runs ​

WantSendNotes
Answer later, without holding a connectionruns API with the default wait: 0, or "background": true on ResponsesReturns a run id at once.
Low priority, can wait hours"class": "deferred" (runs API) or "service_tier": "flex" (OpenAI endpoints)Own queue and caps. See Run classes.
Start at a given time"run_at": "2026-10-18T06:00:00Z" or "delay_seconds": 3600Status scheduled until due, at most 30 days ahead.
Repeat on a scheduleA trigger of type scheduleCron with an IANA timezone. The same agent can also have one-shot, manual or webhook triggers.

Ids ​

IdLooks likeMeaning
Runrun_65f1c0…One turn of an agent. The unit you poll, cancel and list.
Responseresp_65f1c0…The same run, seen through the Responses API: resp_<runId>. GET /responses/{id} and previous_response_id accept it.
Session6f1c0a2b… (also session_id, conversation_id)The conversation a run belongs to. Continue it by sending the id back.
Task (A2A)run_65f1c0…A2A task ids for new tasks are run ids.

A run id and its resp_ id differ only in the prefix. Older ids still work: resp_<conversationId> as previous_response_id, and task_<conv>_<n> as an A2A task id.

A session holds one active run at a time. A second request on a session that is still busy is refused with 409 agent_run_conflict.

Idempotency ​

Send an Idempotency-Key header (at most 255 characters) on any call, synchronous or not, to make a retry safe:

  • The same key with the same request returns the stored run: 200 with the result if it finished, otherwise it waits for it like a new call.
  • The same key with a different request is 409 idempotency_key_conflict.

The request is compared as a whole (agent, session, message, version, named attachments), not only by the key. Pick a key that names the intent: a build number, a ticket id, a date. Scheduled trigger fires use trigger:<triggerId>:<fireTime> themselves, which is why a trigger fires exactly once even with several replicas of Console; webhook deliveries use a key made from the delivery, so a sender's retry starts one run (de-duplication).

Cancel ​

There is one cancel semantic and several ways to reach it.

SurfaceCall
Runs API (canonical)POST /api/client/v1/agents/runs/{runId}/cancel
SessionPOST /api/client/v1/agents/sessions/{sessionId}/cancel
OpenAI ResponsesPOST /api/client/v1/responses/{id}/cancel
A2Atasks/cancel

A queued or scheduled run is cancelled at once. A running one is asked to stop and ends canceled at its next checkpoint (about a second, keeping its partial answer). A run waiting on a person (requires_action) is cancelled right away, and so is one whose pause is dropped another way (a new message to an agent set to cancel pending requests, a session truncate, a timeout of onTimeout: cancel), with its callback and agent_run.canceled event. Cancelling a finished run is 409 agent_run_already_terminal on the runs API, and a plain 200 with the final object on the OpenAI endpoint, as OpenAI does. See Stopping a turn.

Callback or webhooks ​

Two ways to be told that an async run is done, instead of polling.

Per-request callbackWebhook subscription
Set with"callback": { "url", "secret" } on the request, a trigger, or the agent's defaultOne subscription per project, to agent_run.*
ScopeThat one runEvery run of the project
Good forA workflow that waits for one specific job, such as the n8n Wait node resume URLOne service that hears about everything
SignedYes, when a secret is givenAlways

Both use the same HMAC signature scheme and fire only for async runs: runs queued, scheduled or deferred, and inline runs that detached after the window. A turn answered inside the wait window sends neither. Details: Background runs and Webhooks.

Pick your integration ​

SystemPage
Open WebUIOpen WebUI
n8nn8n
OpenAI SDK (Python, Node)OpenAI SDK
LiteLLM proxy, A2A clientsLiteLLM and A2A
Azure DevOps Server / ServicesAzure DevOps
GitHub ActionsGitHub Actions
Scheduled and one-off runs, or an agent with several ways to startTriggers
Events from GitHub, GitLab, Azure DevOps or monitoring starting an agentWebhook triggers
Capacity and priorityRun classes
End-to-end scenariosUse cases

The request and response shapes are in the Agents API, the Triggers API and the OpenAPI spec.

Studio · Pulse · Console · Agent SDK and more — the Cognipeer documentation hub