Configure ChatKit over Tilde Global MCP
Usehttps://api.trytilde.ai/mcp. Call tilde_whoami first. Team ChatKit sessions, routines, and agents use team ownership; private resources use user_team, retaining both the effective owner and execution team. A private session may grant conversation access to selected team users. Every message, attachment, event, task, reply, and queued turn inherits its session audience.
Agents, sessions, routines, signal providers, and signal rules have independent visibility and ownership modes. Visibility governs discovery, conversation/delivery reads, messaging, and realtime delivery. Ownership governs settings, membership, grants, target policy, and deletion. Ownership and administrator authority never imply visibility. Use the standard REST mode and user/group grant operations under the exact resource path published by OpenAPI.
Build the endpoint first
Start from the Hello World agent. For a provider-rich implementation, use the code review bot. Browse the full examples repository before creating a new pattern. For Linq, create one common-provider installation from either ChatKit or Tools. Obtain the Partner API token fromhttps://dashboard.linqapp.com/api-tooling; Tilde then provisions ChatKit, tools, Signals, and Reverse Proxy together and creates the signed Linq webhook subscription automatically. Do not ask the user to create a second webhook for the managed path. Use /guides/linq for phone-line scoping and the standalone Signals fallback.
In @trytilde/sdk-vercel-ai-node, use context.linq / LinqChatKitMessageMetadata for inbound Linq ChatKit metadata and LinqSignalByType["linq.message.received"] (or another LinqSignalType) for event-narrowed Signal handlers under onUnprocessed.linq.
With AI SDK 7, move server-authored system context into instructions before calling the model. Keep ordinary conversation and explicitly untrusted recalled memory in messages. Do not discard the server context to avoid a prompt validation error.
Use Vercel AI SDK and Tilde SDK chatKitEndpoint. Preserve webhook signature verification, context.session.history(), convertToAiSdkMessages, streaming, and server-side secrets.
Durable conversation work
All work routes are bound to one agent and active session:.../agents/{agent_id}/sessions/{session_id}/goals.../agents/{agent_id}/sessions/{session_id}/tasks.../agents/{agent_id}/sessions/{session_id}/jobs.../agents/{agent_id}/sessions/{session_id}/runs
agent_id or session_id into request bodies.
Goals accept objective; updates may set status, progress_percent, progress_note, and status_reason. Tasks accept summary, optional goal_id, dependency_task_ids, plan, and metadata; update the same progress/status fields as work advances.
Delegate a job with child_agent_id, objective, idempotency_key, optional model_id, optional metadata, and optional budget containing max_duration_seconds, max_input_tokens, max_output_tokens, and max_cost_microusd. Use the exact action suffixes /steer, /stop, /resume, and /collect-result. Discover a real child agent ID first; never invent one.
AgentRun hosts use /active, /claim, /{run_id}/steps, /{run_id}/transition, and owner /{run_id}/control. Record tool intent with /effects/prepare, look it up with /effects/lookup, and finish it with /effects/finish. Reuse committed outputs. Never automatically repeat an effect whose receipt is uncertain.
Use the verified context.execution discriminated union: agent_job supplies jobId, generation, childSessionId, optional modelId, and optional budget; agent_run supplies hidden, runId, workerId, and generation. Ignore model-, budget-, run-, or worker-selection headers from callers. chatKitAgentRunIdempotencyKey(triggerId, context.execution) preserves ordinary trigger keys and qualifies job keys by trusted job ID and generation. Reuse the active run for a hidden continuation.
For a hidden continuation, append the step with the supplied worker lease before transitioning. The transition’s expected_generation and worker_id fence an atomic acknowledgement of only the matching planned receipt, and only after a same-generation step newer than that receipt exists. Owner /control cannot finalize it, and an uncertain receipt is not automatically committed. Do not fabricate accounting to clear a failed invocation.
Agent-owned context compaction
POST lifecycle reports to/api/v1/team/{team_id}/chatkit/sessions/{session_id}/compaction-events with agent_id, compaction_id, and one lifecycle payload:
started:input_message_count,estimated_input_tokens,compacted_through_message_idended:summary,compacted_message_ids,retained_message_ids,input_tokens,output_tokensfailed:error,retryable
/compaction-events/latest?agent_id=... for the last successful checkpoint. Read /messages/from-last-compaction?agent_id=...&page_size=... for that checkpoint plus retained/newer messages. The canonical transcript is never deleted or rewritten.
Read context.agent when the endpoint needs its own canonical Tilde identity. It provides id, displayName, providerId, status, optional principalUserId, optional authenticated avatar.url, and lifecycle timestamps. Do not hardcode or separately fetch the receiving agent’s name or avatar. Treat context.agent as optional during mixed-version rollout, and authenticate avatar requests with the same server-side Tilde credential.
Set the required top-level responseMode to agentLoop or tool. In tool mode, assistant text is private reasoning and only sendMessage produces a visible ChatKit message. Use context.session.tools or context.$provider.tools; routing identifiers are server-bound and must never be requested from the model. context.session.createMCPClient({ serverId }) exposes the same session-bound tools through MCP. For a private owner-workspace session, the authorized session also contributes the authenticated human’s personal Memory and Wiki tools. The agent remains the credential actor; arbitrary personal connections are not federated and callers cannot nominate a user ID.
Use context.mcp.connect({ serverId }) for speaker-bound personal-tool federation on a shared agent. ChatKit supplies the verified speaker capability privately. Never accept a model- or caller-supplied user ID, account ID, or delegated capability. Unmapped external speakers receive no personal tools.
Inspect outbound delivery
UseGET /api/v1/team/{team_id}/chatkit/session/{session_id}/message/{message_id}/deliveries with the organization header and a credential that can read the message and session. The response is an array of channel_inbox_id, provider_id, status, optional external_message_id, last_error, and delivered_at. The optional provider_status is pending, delivered, or failed when final provider status can be queried. It overrides initial acceptance; Telnyx WhatsApp carrier failures return dead_letter with an error. Without that status, delivered only confirms provider acceptance. Neither is a read receipt. Wait on pending work and retain the same canonical message ID for uncertain notifications. A new attempt is safe only after a confirmed provider rejection.
Manage multiplayer rooms
A room is a ChatKit session. Use/api/v1/team/{team_id}/chatkit/sessions/{session_id}/participants for the roster and /invitations for invite/list operations. Use /invitations/{invitation_id}/decision to accept or decline, DELETE /invitations/{invitation_id} to revoke, and DELETE /participants/{participant_instance_id} to leave or remove.
Only session ownership authority can create, inspect, or revoke invitations. Only the canonical invitee_user_id may accept or decline. Participant roles are owner, admin, or member; they are collaboration metadata and never override visibility/ownership authorization. Pending, declined, and revoked invitations expose no room transcript.
Do not promise an OpenBot owner room UI yet. The typed API/SDK contract is available, but OpenBot keeps the UI dormant until canonical human identity discovery replaces raw user IDs.
Provider actions currently include Slack/GitHub reactions and thread reads, Linq reactions and poll operations, and AgentMail thread reads. AgentMail sendMessage accepts to, cc, bcc, subject, HTML, and reply-all. Non-message actions appear as canonical tool.execution realtime events; sendMessage uses normal ChatKit message streaming.
Participant visibility changes emit durable participant.joined / participant.left events with compact participant handles, display names, and external IDs when available. Workspace conversation snapshots return the same records in participant_events; keep them separate from messages and render them as session activity, not chat bubbles. Tilde includes the lifecycle context in agent history without invoking an agent turn.
Register an agent
Calltilde_register_chatkit_agent with:
team_id- optional
access_scope:team(default) oruser_team; private agent ownership is inferred from the effective caller display_nameendpoint_url: an HTTPS URL in production, or an endpoint path such asapi/agentfor local developmentlocal_running_endpoint: truefor a Dev Tunnel endpoint- optional
concurrency_policy:queue,interrupt, orqueue_and_batch(defaults toqueue) - optional
memory_bank_idsto ingest this agent’s conversations continuously
message_tool_provider_id. Give both secrets to the human for secure storage in the agent’s server environment. Never print them into source, state, logs, or chat history. The message provider is credentialless and already bound to the new agent.
Team agent create, update, and delete events are broadcast to authenticated ChatKit realtime connections for the team. Private agent lifecycle events are sent to current user or group visibility grantees.
Realtime audiences are derived from current visibility and session membership on every event. A private visibility grant may admit an authorized user or group to the root, while private-session members receive that session’s message and agent-turn stream. Client payloads do not expose authorization grants or internal audience identifiers.
Human-created private workspace sessions belong to the authenticated human, including when a deployment service owns the selected agent. Tilde binds the workspace participant to that human for personal tool access. Delegated child sessions inherit the parent session’s ownership.
For owner-only WhatsApp/Linq ingress, update the channel with provider_configuration.external_participant_policy set to { "join": "linked_only", "agent_invocation": "linked_only", "personal_tools": "linked_participants", "session_scope": "personal" }, preserving its other provider configuration. Shared verification uses the actual WhatsApp phone-number ID or configured Linq line as provider_account_id, not the destination channel ID. Ingress validates the real global link’s route and current membership, materializes the channel’s tenant identity, and stores the canonical verification namespace in the message actor. No duplicate verification link is created. Unverified or revoked senders do not join or wake the agent. Replies retain the incoming provider route; this setting does not select a background notification channel.
Manage private session members
Private sessions useuser_team ownership. The creator is inserted as the owner automatically, and an optional member_user_ids list may add other users from the same team during creation. Use the private-session membership API to list, add, or remove non-owner members later. Do not confuse these authorization members with ChatKit inbox participants.
Owners and team, organization, or system administrators manage membership. Members may list/read the session, send messages and attachments, and receive its ChatKit realtime message, delta, queue, turn, task, and error events. Members cannot change ownership or manage other members. A grant stops authorizing new access when the user is removed from the execution team.
Realtime clients consume the closed agent.*, session.*, participant.*, message.*, queue_item.*, turn.*, activity.*, task.*, and chat.error union. They must refresh the workspace projection after access.changed. Use PUT /api/v1/team/{team_id}/chatkit/workspace/sessions/{session_id}/read-state with { "unread": false } after presenting a session and { "unread": true } for a manual unread override. Read state is per user and must never be copied into shared session metadata.
Enable agent-to-agent messaging
- Take
message_tool_provider_idfrom the child agent’s registration response, or find itschatkit_agent_messageprovider withtilde_search_enabled_capabilities. - Add both
chatkit_agent_message_sendandchatkit_agent_message_wait_for_responsefrom that provider to the parent agent’s runtime MCP server withtilde_set_mcp_server_tool_enabled. - Call the exposed
messagetool withmessage.partsand optionalmessage.metadata. Passsession_idonly to continue an existing child conversation. - Immediately call the exposed
wait_for_responsetool with the returnedticket_id. - Keep the MCP request open. Consume
message_streamingandagent_turn_statusprogress notifications. Clients that omit an MCP progress token receive the same structured payload throughtilde.agent_responselogging notifications. - Use the final
responseas the canonical persisted ChatKit message. Terminalstatusiscompleted,failed, orcancelled; queue notifications reportpendingorrunning, the applied concurrency policy, trigger count, and whether the turn was batched.
Propose a missing capability safely
Agents can propose but cannot approve or execute capability changes.- POST the secret-free intent to
/api/v1/team/{team_id}/chatkit/self-extension-proposals. Supply the exactrequesting_agent_id, optional originatingsession_idandrun_id, a stableidempotency_key, one supportedcategory, a short title and rationale, and credential-freedesired_state. Credential-shaped fields are rejected even when named as references or IDs; complete provider authentication only through the owner-authenticated setup continuation after approval. - Stop the agent turn after the client renders the returned capability-change Human Approval. Never treat a free-text yes as approval and never ask for API keys, passwords, OAuth codes, tokens, or signing keys in chat.
- The owner client posts
approval_id,proposal_hash,proposal_generation, anddecision: "approve" | "reject"to/api/v1/team/{team_id}/chatkit/self-extension-proposals/{proposal_id}/decisionusing the authenticated human credential. The requesting agent’s human owner or a team/system administrator may decide; agent credentials and unrelated humans are rejected. - Poll the proposal resource. Approved work moves through
approved,executing, andexecuted; denied work becomesrejected. Leased retries are idempotent. - If the executed proposal contains a
provider_setupcontinuation, hand itssetup_item_idto the owner-authenticated generic credential setup flow. Resolve OAuth or credential next actions there; the agent must not receive authorization state or credential values. - Resume the original task only after the durable decision. Use the returned resource receipts for verification, not as authority to delete shared resources.
context.body.session.parentAgentId. Direct sessions omit it. Use this server-authored value only when a specialist must continue caller-owned runtime context; never ask the model or client to provide the parent identity.
Configure a ChatKit provider
- Call
tilde_search_available_capabilitieswithkinds: ["chatkit_provider"]andinclude_schemas: true. - Select the provider ID from the results.
- Call
tilde_configure_chatkit_providerwithprovider_id,display_name, the registered agent inbox ID, and any provider-specific configuration from the returned schema. - If setup requires human authorization, present the returned approval URL and wait with the returned continuation tool.
- Call
tilde_search_enabled_capabilitieswithkinds: ["chatkit_channel", "chatkit_agent"]to verify both resources.
Search ChatKit conversations
UseGET /api/v1/team/{team_id}/chatkit/workspace/search with the selected workspace’s team_id and a required q parameter. Authenticate with the same API key or bearer token used for ChatKit workspace.
- Omit
session_idto search visible session titles, visible agent IDs and display names, and message bodies across the workspace. Private resources require a matching visibility grant. - Pass
session_idto search messages only inside that session. - Set
page_sizefrom 1 to 100. The default is 25. - Pass the returned opaque
next_page_tokenunchanged to fetch the next relevance-ordered page. - Inspect each result’s
kind:session_title,agent, ormessage. Every result carries session context; agent and message details appear only for their matching kinds.
404 without revealing whether it exists elsewhere.
Configure coding-agent audit hooks
Useopenbot plugin --cli <codex|claude|cursor|opencode|gemini> --agent-id <chatkit_agent_id>.
The command keeps MCP server and skill-registry setup in the same flow and
installs native lifecycle hooks for the selected harness. Codex uses a packaged
Tilde plugin; OpenCode uses a fail-open global plugin; Claude Code, Cursor, and
Gemini CLI use their user hook settings. Gemini hooks return valid JSON on
stdout, as required by Gemini CLI, while audit failures remain non-blocking.
The adapters map one harness session to one tenant-scoped ChatKit session by a
stable lookup key. They persist user prompts and final responses as ordinary
ChatKit messages, then report tool start/completion/failure through
POST /api/v1/team/{team_id}/chatkit/agents/{agent_id}/tool-executions.
When a hook discovers a local tool one call at a time, its report includes the
tool display name and immutable source identity; ChatKit activates that entry
without treating unobserved catalog entries as removed.
Do not create a separate audit table or transcript store. Search coding-agent
messages through /chatkit/workspace/search, and read canonical tool execution
events under the same session. Preserve the harness session ID and tool call ID
when adapting another coding agent. Canonical tool details may contain sensitive
inputs and outputs, so keep agent/session visibility narrow and rely on the
ChatKit observability projection for browser disclosure.
Trigger work with Signals
Signals turn provider events into ChatKit messages. Signal providers and rules may be personaluser resources with no owning team. A personal rule must supply target_team_id, and the owner must belong to that team. It cannot bind a fixed shared session; sessions it creates are user_team sessions for the same owner. Personal webhook providers currently use polling ingress, while team providers may use webhook or polling ingress.
Provider/rule visibility controls discovery and delivery reads. Ownership controls configuration, target/session policy, grants, state-changing retries, and deletion. Deliveries inherit their rule; do not grant individual delivery rows.
- Call
tilde_list_signal_providersand inspect the selected provider’s signal schemas and authentication requirements. - Call
tilde_create_signal_providerwith the provider-specificbody. - Call
tilde_create_signal_rulewith abodythat selects the event type, target agent, action, and stable session-key mapping. - Use one stable session key when related events should continue the same body of work, such as all updates to one Sentry issue or GitHub pull request.
- Call
tilde_trigger_fake_signalto test routing where the provider supports it. - Inspect execution with
tilde_list_signal_deliveries. Usetilde_retry_signal_deliveryonly for a failed delivery that is safe to repeat.
tilde_list_signal_provider_instances and tilde_list_signal_rules before updating or deleting resources. Their mutation functions are tilde_update_signal_provider, tilde_delete_signal_provider, tilde_update_signal_rule, and tilde_delete_signal_rule.
In application code, handle typed GitHub, Slack, Sentry, and Firecrawl metadata as shown in the human ChatKit guide. onUnprocessed runs once per unprocessed message; later conversions reuse its cached result.
Agent-owned realtime audio
Use the selected tenant host and explicitteam_id for these REST operations:
GET /api/v1/chatkit/audio/profilesreturns supported profile defaults and server-authored fields. Render these descriptors rather than generating provider-specific setup instructions in frontend code.- Register an HTTP agent with optional
audioconfiguration, or usePUT /api/v1/team/{team_id}/chatkit/agents/{agent_id}/audiowith{ "audio": <configuration> }. Setaudioto null on the PUT route to disable voice. - Configuration fields are
mode(pipeline,realtime, ortelnyx_relay),credential_id(optional),stt_model,tts_model,realtime_model,voice,instructions,language(defaulten-US),interruptible(default true), andmax_duration_seconds(10–1800). The OpenAI Audio credential source ischatkit_openai_audio; omitting it uses the server OpenAI key for OpenAI modes. Relay usesstt_model: "deepgram/nova-3",voice: "Telnyx.Ultra.Callie", and nullcredential_id; its phone route owns the Telnyx credential. POST /api/v1/team/{team_id}/chatkit/agents/{agent_id}/audio/sessionscreates a normal browser session for OpenAI modes and returnsaudio_session,websocket_path, and a one-time token. Connect with WebSocket subprotocolschatkit-audioandtoken.<token>. This endpoint rejectstelnyx_relay; relay starts from an incoming call. Send mono signed PCM16 little-endian audio at 24 kHz as base64audioframes.PUT /api/v1/team/{team_id}/chatkit/agents/{agent_id}/audio/telnyxacceptscredential_id(sourcechatkit_telnyx_voice),public_key,phone_number,connection_id, and public HTTPSmedia_base_url. It returnsrouteandwebhook_url; successful setup also returns the assignedroute.channel_inbox_id. Use the webhook URL in the dedicated Telnyx application.- The generic channel catalog entry is
chatkit.chat_channel.telnyx_voice, providerchatkit.channel.telnyx_voice. Use auth methodchatkit.channel.telnyx_voice.auth.self_managedfor a returned URL orchatkit.channel.telnyx_voice.auth.managedfor Tilde to update the existing Voice API application’s webhook. Both use your existing encryptedchatkit_telnyx_voicecredential and existing number/application. Pass the same five setup fields and the normal default agent selection. Managed setup updates webhook configuration; it does not buy, assign, or fund numbers. Calls use the resulting channel as their participant origin.
context.audio.mode = "telnyx_relay"; Telnyx transcribes and synthesizes,
while Tilde exchanges text frames with the carrier. Realtime mode owns spoken generation;
transcript observations must not trigger another model turn or external send.
context.audio and context.telnyx come from typed, server-authored speech
provenance rather than client message metadata. Both persisted text and UI
messages can carry speech. Interrupted generated speech retains its original
text with interrupted: true, optional played_audio_ms, and optional
reported_spoken_text supplied by the carrier. Preserve the reported prefix
separately; do not rewrite it as the complete generated response. SDK history
conversion adds the corresponding annotation before the original content.
The manual browser/carrier example is examples/realtime-voice in trytilde/dispatch
(the @trytilde/sdk packages). It never buys phone numbers or changes existing carrier routing. Agent
settings and credential setup references are portable; live connections and
media tokens are not exported. Configure Telnyx number/application bindings
again in the destination installation. Native mode does not inherit endpoint
tools, and browser voice does not establish personal-tool federation.
Change resources through native tools
Agents use native Tilde API/MCP operations under their existing permissions. The capability proposal API has been retired. Chain dependent operations using returned resource IDs, reconcile partial failures before retrying, and read back the resulting resource. Do not widen permissions or switch credentials after an authorization failure. Before enabling a connector, read the managed enable-connections skill. Discover existing user and agent access and verify the correct account first. Choose personal/user or bot ownership explicitly; when unclear, ask whether other bots should be able to use the account. Native brokering returns aconnector_setup_required descriptor for the pending
resource. API clients render an enable-provider event outside message bubbles
and open secure configuration modals. In external channels, invoke sendMessage
with the server-returned hosted setup URL. Credentials stay in native secure
setup operations, outside chat and persisted client workflow snapshots.
Recover missing conversation context
Session-scoped MCP connections providechatkit_search_history. The query
searches the current conversation by default. Set include_related_sessions to
search other conversations that the authenticated agent actively participates
in with the current session’s verified human owner. Ordinary search permissions
also apply. Models cannot supply a different agent, tenant or user identity to
this tool. Follow next_page_token, even after an empty filtered page.
Manage custom ChatKit backends
Calltilde_manage_custom_chatkit_provider in the resolved team scope. Supported
action values are create, list, get, update, refresh, enable,
disable, delete, and rotate_signing_key. Use provider_id for an existing
definition. Create/update use display_name, discovery_url, and optional
local_running_endpoint.
Create returns a pending definition and one-time signing key. Configure the
customer-hosted endpoint with that key and definition ID before refreshing.
Never place signing keys or runtime credentials in shared conversation history.
Use the generic provider setup catalog/start/resume operations with domain
chatkit and the definition ID; follow the returned next_action.
Session tools are discovered against the authenticated agent’s current turn.
Do not fabricate session coordinates, use a provider runtime token as a user
credential, or expose transport context as model inputs. Preserve tool-call IDs
for replay and rely on canonical execution receipts and reconciliation.
A failed discovery refresh preserves the last valid manifest. Disabled
providers pause runtime work. Definition deletion fails while connections refer
to it. Portable imports remain pending until fresh credentials are bound.
See custom ChatKit providers
for the public SDK authoring contract.