POST /api/v1/agents/{agent_id}/runs. Connectors and schedules run it for you.
- A connector binds the agent to a Telegram bot or a WhatsApp Business phone number. Every allowed inbound message becomes an agent run, and the reply goes back to the chat it came from.
- A schedule starts runs on a timer: once at a fixed time, every N seconds, or on a cron expression. The timer survives service restarts.
The
mka1 CLI reference does not cover connector or schedule commands yet, so this guide has no CLI tab. Use an SDK or curl.Before you start
Connector management is team-level. Every connector endpoint returns403 when the request carries X-On-Behalf-Of, so leave the header off all connector calls. Beyond that:
- Create and activate need an API key. They reject session authentication, because the connector keeps running in the background under that key. List, retrieve, delete, and the WhatsApp setup call also accept a console session.
- The key needs
write:agentsplus the runtime scopeswrite:conversations,write:responses, andread:responses. Listing and retrieving needread:agents. The key’s scopes at creation time are a ceiling: a scope you add to the key later does nothing until you activate the connector, and each message runs with the ceiling minus anything the key has since lost. - Optional scopes:
write:feedbackturns thumbs-up and thumbs-down reactions into response feedback.write:fileslets the connector receive photos and documents,read:fileslets it deliver managed files, and a generated image needs both, because it is saved as a managed file before delivery. Text-only connectors need neither file scope.
X-On-Behalf-Of.
Each provider needs its own credentials:
Every connector also needs an
access allowlist with 1 to 100 entries: Telegram chat IDs (integers) or WhatsApp sender IDs (digit strings without a leading +). The connector acknowledges and drops messages from anyone else, and stores nothing. There is no public mode.
To find a Telegram chat ID, message the bot before you bind it, then open https://api.telegram.org/bot<telegram-bot-token>/getUpdates and read message.chat.id. Group and supergroup IDs are negative.
The console has the same controls under Agents → your agent → Connectors (/agents/{agent_id}/connectors on your cluster’s console, for example platform.mka1.com).
Connectors
Connect a Telegram bot
Create a bot with BotFather, collect the chat IDs you want to allow, then bind the bot to the agent.201 Created with a connector object (con_...). It does all of this before returning:
- Validates the token with Telegram’s
getMe. A token Telegram rejects returns400. Telegram being unreachable returns502withretryablein the error details. - Stores the credentials, encrypted, and reserves the bot. Responses never include credentials;
credentials_configuredtells you they are stored. Binding the same bot a second time, from any team, returns409. - Registers the webhook with
drop_pending_updatesfixed attrue, so messages sent to the bot before this binding are discarded. - Sets
statustoactive.
status: "error" with last_error, and the call returns 502. Call activate to retry once Telegram is reachable.
To check it works, send the bot a message from an allowed chat. The reply comes back in that chat, and in a private chat you see a draft that updates while the model streams. The run is scoped to the chat, not to your API key, so a plain listing of the agent’s runs does not show it. Pass the chat’s synthetic ID as X-On-Behalf-Of, using the connector’s provider_account_id as the bot ID. The thread segment is root unless the message came from a forum topic, in which case it is the topic ID:
Connect a WhatsApp number (Beta)
WhatsApp needs an app-level webhook in Meta before the first phone connector exists, so this takes two calls. First, ask for the webhook values for your Meta app:callback_url and verify_token into the Meta app under WhatsApp → Production setup → Configure Webhooks, pick the WhatsApp Business Account object, and subscribe to the messages field. The verify token is stable: call setup again whenever you need it, since the connector object does not carry it. The response is sent with Cache-Control: no-store.
Then create the connector:
app_id, carries both WhatsApp permissions, and can see phone_number_id under the business account. It then subscribes the app to the WABA, if it is not already subscribed, and registers a callback for this phone number. Messages that Meta still sends to the app-level webhook reach the connector too, matched by phone_number_id.
WhatsApp replies are session messages: Meta accepts them inside the 24-hour window that a user’s message opens. The connector never sends template messages, so it cannot start a conversation on its own.
To check it works, send a message from an allowed number to the business number; the reply comes back in the same WhatsApp chat. Meta’s test number is enough for this while you develop. Connector runs are scoped to the sender, so list them with X-On-Behalf-Of: whatsapp:<phone-number-id>:user:15551234567.
List, retrieve, and delete connectors
List the agent’s connectors, or read one to see itsstatus and last_error:
deleted. Telegram drops its pending updates. WhatsApp clears only this number’s callback and leaves the app subscription in place for other numbers. If the provider cannot confirm the cleanup, for example because the token was already revoked, the connector moves to error with last_error.operation set to unregister_webhook and the call returns 502. Repeat the DELETE to retry; the bot or number stays reserved until cleanup is confirmed.
There is no update call. To change the allowlist or the credentials, delete the connector and create it again.
An agent with connectors cannot be deleted. DELETE /api/v1/agents/{agent_id} returns 409 until every connector is gone.
Activate a connector
Activate re-runs credential validation and webhook registration with the stored credentials, under the calling key:error (webhook registration failed, or the key behind it was revoked) and after you rotate the API key. The rules:
- Only the user who created the connector can activate it; anyone else gets
403. Teammates can list, inspect, and delete a team connector, but to run it under another user’s key, delete and recreate it. - The stored credentials must still resolve to the same bot or phone number. If they do not, the call returns
409and the connector moves toerrorwithlast_error.operationset toregister_webhook. - A connector that is being deleted, or that has no stored credentials, returns
409. - Success sets
statustoactive, clearslast_error, and replaces the stored scope ceiling with the calling key’s current scopes.
How a message becomes a run
- The provider posts to the connector’s webhook. Telegram requests must carry the secret header set at registration (
401otherwise). WhatsApp requests must carry a validX-Hub-Signature-256HMAC over the raw body. These signatures are the only authentication on the webhook paths. - The connector checks its status.
provisioningoractivatingreturns503, so the provider retries after the binding commits. Any other non-activestatus, or a deleted agent, is acknowledged and dropped. - The connector acknowledges and drops a message from any chat or sender outside the allowlist, and stores nothing.
- The message is stored as an event, unique per connector and provider update ID. A redelivered update is ignored. Each chat gets one thread: a Telegram chat or forum topic, or a WhatsApp sender under the phone number.
- Events in one thread are processed one at a time, in arrival order, so replies in one chat never overtake each other. A message that could not be queued when it arrived is picked up within 30 seconds.
- Before running, the connector re-checks its stored API key. A revoked, disabled, or expired key, a removed team membership, a suspended organization, or a missing runtime scope moves the connector to
errorwithlast_error.codeset toconnector_identity_revokedand fails the event. If the check itself cannot complete, the event waits and is retried. - Photos and documents are downloaded from the provider (Telegram
getFile, WhatsApp media IDs; 20 MiB cap for both) and uploaded to the Files API. The run input carriesinput_imageandinput_fileitems that reference those files. Provider URLs and tokens are never persisted. - Each thread gets one gateway conversation, created on its first message and reused afterwards. Sending
/startin a private Telegram chat resets the thread before the next run, so the reply starts a fresh conversation. - The connector creates an agent run against the agent’s current configuration, with
metadatacarryingagent_connector_id,connector_event_id,connector_provider, andconnector_actor_id. The run is stamped with a synthetic end-user ID for the chat:telegram:<bot-id>:chat:<chat-id>:thread:<topic-id>(rootwhen the chat has no topic) orwhatsapp:<phone-number-id>:user:<sender-id>.GET /api/v1/agents/{agent_id}/runsfilters by that scope, so a plain API key withoutX-On-Behalf-Ofdoes not list connector runs. Org admins see them in the team-wide list, and any caller can pass the synthetic ID asX-On-Behalf-Ofto read one chat’s runs. - The final text is split into provider-sized parts and sent, followed by any generated images or files. Private Telegram chats also get a live draft preview while the model streams; Telegram groups and supergroups get a typing indicator instead. Delivery is at-least-once: a crash between the provider accepting a part and its message ID being committed can produce one duplicate on recovery.
- When the key has
write:feedback, a thumbs-up or thumbs-down reaction on any part of the reply becomes feedback on the gateway response, and removing the reaction clears it. Only the user whose message produced the reply can rate it.
Telegram albums are not merged: each item in a media group is its own update and therefore its own run. Managed files created for connector input and output are kept so retries and conversation history keep working; the connector does not delete them.
Connector reference
Connector object fields:
Connector statuses:
Schedules
A schedule stores a run request (input, optional conversation, previous_response_id, context_management, and metadata) plus a timing rule, and starts a normal agent run each time the rule fires. The page calls one firing a tick.
Create a schedule
201 Created with a schedule object (sched_...) in status active. A 502 means nothing was saved; retry the create.
The schedule object picks the kind:
timezone defaults to UTC and only affects cron. run_at and start_at are absolute instants: write them with an offset or a trailing Z.
input takes the same shapes as a run: a string, or an array of input items. metadata is a string map; every run the schedule starts gets it, plus agent_schedule_id.
A schedule remembers the identity that created it, including X-On-Behalf-Of, and every run it starts executes as that identity. Runs from a schedule created for an end user are listed under that end user’s run scope; org admins also see them in the team-wide run list.
List, retrieve, and update
schedule re-creates the timer and sets status back to active, even if the schedule was paused. Changing any other field leaves the timer and the status alone.
A 502 from an update means the new spec was saved but the timer may not have been replaced; the status is unchanged. Repeat the update until it returns 200.
Pause, resume, and delete
{ "object": "agent.schedule.deleted", "id": "sched_123", "deleted": true }. Delete succeeds even when the scheduler is unreachable; a timer left behind that way is harmless. A repeated DELETE returns 404.
Delete an agent’s schedules before you delete the agent. Deleting the agent does not delete its schedules, and afterwards they cannot be reached, because a schedule is only addressable through /agents/{agent_id}/schedules/....
How runs are recorded
- Each tick creates a normal agent run, so it appears in the agent’s run history with
metadata.agent_schedule_idset. - After the run, the schedule’s
last_run_at,last_run_id, andrun_countare updated. A run that fails at the gateway is still recorded as a run (withstatus: "failed") and still counts. - There is no per-schedule run listing. To see a schedule’s runs, list the agent’s runs with the same
X-On-Behalf-Ofthe schedule was created with and keep the ones whosemetadata.agent_schedule_idmatches. The console does the same to label a run as “schedule”.
Durability and failure
- Pause and resume act on the timer named by
temporal_schedule_id; when that field isnull, they return409. - If a tick fires while the previous run is still executing, that tick is dropped rather than queued. Expect gaps in
run_counton long-running agents with short intervals. - Missed ticks are backfilled for up to 10 minutes after their scheduled time, for example after a service restart. Older misses are dropped.
- A tick that fails before its run is recorded is retried, up to 3 attempts. A retry can duplicate a run if the failure happened after the gateway accepted the request. A run that reaches the gateway and fails is recorded as failed and is not retried.
- A tick does nothing when the schedule is not
active, or its agent has been deleted.
Schedule reference
Troubleshooting
API reference
Every field and status code is in the Agent Connectors and Agent Schedules groups of the API Reference.See also
- Manage agents - agent definitions, runs, and version rollback.
- Conversations - the conversation object each connector thread keeps.
- Files and vector stores - the Files API that connector media flows through.