Connectors API
Kheish exposes connectors through two different HTTP surfaces:- runtime CRUD for operators
- ingress routes for external systems
Runtime CRUD endpoints
GET /v1/runtime/connectorsGET /v1/runtime/connectors/{kind}/{name}PUT /v1/runtime/connectors/{kind}/{name}DELETE /v1/runtime/connectors/{kind}/{name}
telegramslackhttpexternal
GET returns projected connector views with:
source:fileordaemon- non-secret transport settings
- redacted secret metadata such as
configured,source,secret_ref, orenv
- file-backed connectors are readable but not mutable through the daemon control plane
- deletion is rejected when the connector is still referenced by dependent config such as schedules or reply-target relationships
Runtime upsert semantics
PUT /v1/runtime/connectors/{kind}/{name} behaves like a field-aware upsert.
- If the connector does not exist, the daemon creates it.
- If the connector already exists, only fields present in the JSON payload are updated.
- Fields omitted from the payload keep their stored value.
PUT, but it is not a naive full-record replacement.
Shared helper shapes
ConnectorSecretInput
Secret-bearing runtime fields such as bot_token, signing_secret, secret_token, and bearer_token use the same write-only helper shape:
value: store or rotate a daemon-managed generic secret slotsecret_ref: point at an existing daemon-managed generic secret slotenv: read the secret from an environment variable
envcannot be combined withvalueorsecret_ref
ReplyHandle
Connector reply-target fields use raw ReplyHandle objects:
- HTTP connector
default_reply_targets - Slack connector
additional_reply_targets - Telegram connector
additional_reply_targets - HTTP ingress
reply_targets
SessionReplyTargetRequest surface used by /v1/sessions/{session_id}/reply-targets.
Plugin-specific address payloads commonly encode:
http: a raw URL string or serializedHttpReplyRouteslack: serializedSlackReplyRoutetelegram: serializedTelegramReplyRouteexternal: serializedExternalReplyRoute
session_policy
Connectors can optionally bootstrap sessions for inbound traffic:
create_if_missingpersona_idcapability_scopecredential_scope
persona_id is validated against existing daemon persona records.
HTTP connectors
Runtime payload
PUT /v1/runtime/connectors/http/{name} accepts:
actor_idfixed_session_idbearer_tokenallow_unauthenticated_ingressdefault_reply_targetsdefault_binding_keyssession_policy
- if no bearer token source is configured,
allow_unauthenticated_ingressmust betrue
Ingress endpoint
POST /v1/connectors/http/{name}
session_idbinding_keysactor_idcontentinput_itemsattachmentsmetadatareply_targetsreply_pluginreply_addressidempotency_key
- connector
fixed_session_id - payload
session_id - session already bound to the provided
binding_keys - connector-derived fallback session id based on the first binding key
- uses the
Authorization: Bearer ...header when a connector bearer token is configured - otherwise requires
allow_unauthenticated_ingress=true
Slack connectors
Runtime payload
PUT /v1/runtime/connectors/slack/{name} accepts:
bot_tokensigning_secretallow_unauthenticated_ingressapi_base_urlfixed_session_idinclude_self_outputadditional_reply_targetsadditional_binding_keyssession_policy
- if no signing secret source is configured,
allow_unauthenticated_ingressmust betrue
Ingress endpoint
POST /v1/connectors/slack/{name}
- verifies the Slack signature when a signing secret is configured
- rejects stale or far-future signed requests
- answers
url_verificationwith the Slackchallenge - ignores bot-authored events
- derives one durable session from channel plus root thread timestamp when
fixed_session_idis not configured - automatically downloads inbound files when a
bot_tokenis configured
- prefers Slack
event_id
External connectors
Runtime payload
PUT /v1/runtime/connectors/external/{name} accepts:
platformmodebase_urlshared_tokenallow_unauthenticated_ingressfixed_session_idinclude_self_outputadditional_reply_targetsadditional_binding_keyssession_policyingress_events_per_secondchild_process
- non-secret transport config
child_process.env_keyschild_process.credential_env_keys- redacted secret metadata for the shared token
child_process.credential_slots is configured, the daemon does not inject those platform secrets into the child environment directly. It starts the sidecar with one short-lived opaque credential token and the list of allowed env keys, and the sidecar fetches each concrete secret from the daemon on demand.
Ingress endpoints
POST /v1/connectors/external/{name}/eventsPOST /v1/connectors/external/{name}/events/batch
- single-event ingress returns
accepted,duplicate, orrejected - single-event rate limiting is HTTP
429 - batch ingress is ordered, non-atomic, and returns per-item statuses
- session derivation can use
thread.path,routing_key, orfixed_session_id - sidecar runtime endpoints stay on protocol
1, while ingress semantics now default to protocol2
- when
include_self_output=true, replies default back into the same Slack channel/thread additional_reply_targetsare appended after that default self-target
Telegram connectors
Runtime payload
PUT /v1/runtime/connectors/telegram/{name} accepts:
bot_tokensecret_tokenallow_unauthenticated_ingressapi_base_urlingress_modepolling_timeout_secondsfixed_session_idinclude_self_outputadditional_reply_targetsadditional_binding_keyssession_policy
ingress_mode=pollingrequires a bot tokeningress_mode=webhookrequires either a secret token source orallow_unauthenticated_ingress=true
Ingress endpoint
POST /v1/connectors/telegram/{name}
- verifies
x-telegram-bot-api-secret-tokenwhen a secret token is configured - derives one durable session from chat id plus topic thread id when
fixed_session_idis not configured - downloads inbound photos and documents through the Telegram Bot API when a bot token is available
- keyed by Telegram
update_id
- when
include_self_output=true, replies default back to the same chat or topic additional_reply_targetsare appended after the self-target
Output expectations
Connector ingress and output routing are separate concerns:- ingress routes submit work into Kheish
- reply targets determine where finalized output is delivered
- output delivery can stay daemon-local or flow through output plugins
