Skip to main content
Channel delivery contracts. A Channel pushes an ask_user question to a human on a specific medium (Telegram, Slack, SMS, …) and bridges the human’s reply back into the interactions store by forwarding it to the delivery’s public callback_url. Channels are registered on the app handle (tai42_app.channels) by channel plugins and looked up by name when ask_user is called with channel=.... Delivery either returns None (success) or raises ChannelDeliveryError (any failure) — never a bool. A channel also sends fire-and-forget notifications: notify pushes one ChannelNotification to a human with no interaction, no ticket, and no reply path, under the same loud-failure rule; it returns the per-message ids the medium assigned the send (empty when the medium exposes none), never a bool.

Channel

tai42_contract.channels.Channel
Delivers one question to a human on a specific medium. A channel plugin registers an instance under a name (tai42_app.channels.register); ask_user resolves it by name and calls deliver after the interaction is persisted and its callback ticket is minted. A channel never reaches the interactions store directly: the human’s reply travels back through the delivery’s public callback_url. A channel MAY advertise richer notify support with two OPTIONAL, class-level capability flags — supports_media_notifications and supports_template_notifications — set as plain class attributes. They are a documented convention, NOT Protocol members: a channel that supports the richer notify form sets the matching attribute to True; a channel that omits it advertises no support (absent = False). Because they are not part of the Protocol, a text-only channel that declares neither is still a valid Channel (both structurally and under runtime isinstance). The central notify_user helper reads them defensively with getattr(channel, "<flag>", False) and refuses a media or template notification to a channel that does not advertise the matching flag, so a sibling channel that reads only notification.message can never silently drop the extra content.

Members

deliver

tai42_contract.channels.Channel.deliver
Push delivery to the medium, or raise ChannelDeliveryError. Send the question to the resolved recipient — delivery.recipient when set (after checking it against the plugin’s operator allowlist), else the plugin’s operator-configured default — and arrange for the reply to reach delivery.callback_url — either a tappable link carrying the URL, or an inbound-route correlation the plugin stores. Any delivery failure — an unreachable or rejecting medium, a recipient outside the operator allowlist, a required credential or recipient not configured, a bad send response — raises ChannelDeliveryError; a plain return is the only success signal. One send attempt only: retrying is the caller’s decision, never an implicit loop here (no medium API offers an idempotency key, so a blind retry risks a double-send). Parameters

notify

tai42_contract.channels.Channel.notify
Send a fire-and-forget message, or raise ChannelDeliveryError. No interaction, no ticket, no callback, no reply. Any delivery failure raises ChannelDeliveryError; a return means the medium ACCEPTED the message — not that a human saw it — and yields the per-message ids it assigned this send (several when the medium splits a long message, empty when it exposes no id), which later correlate an out-of-band delivery receipt back to this send. One send attempt only, no retry. A channel that cannot notify raises NotImplementedError. Parameters

ChannelDelivery

tai42_contract.channels.ChannelDelivery
One question handed to a channel for out-of-band delivery. callback_url is the public /api/interactions/callback/{ticket} answer sink; the channel arranges for the human’s reply to reach it. recipient is the OPTIONAL caller-requested address (chat id, phone number, …): the channel plugin validates it against its operator-set allowlist and refuses to send to an unlisted address; when omitted the plugin sends to its operator-configured default recipient. It is an address only, never a secret or credential. Attributes

ChannelDeliveryError

tai42_contract.channels.ChannelDeliveryError
Raised by a Channel when delivering a question fails. Every failure mode — an unreachable medium API, a rejected send, a missing credential, a misconfigured recipient — raises this single typed error. deliver NEVER returns a bool and NEVER silently drops a message: an undeliverable question is a loud failure, so the only success signal is a plain return.

ChannelNotification

tai42_contract.channels.ChannelNotification
One fire-and-forget message handed to a channel. A notification carries no interaction, no ticket, no callback_url and no deadline: the channel sends the message and nothing travels back. recipient is the OPTIONAL caller-requested address (chat id, phone number, …): the channel plugin validates it against its operator-set allowlist and refuses to send to an unlisted address; when omitted the plugin sends to its operator-configured default recipient. It is an address only, never a secret or credential. sender_identity is the OPTIONAL address to send FROM when the channel fronts several operator identities: an internal routing control set by the sending side, never caller-supplied, and an address only — never a secret. media and template are OPTIONAL richer-send forms reusing the same message as the human-readable equivalent. media is display media the channel sends alongside the message (reusing MediaItem); a present list is non-empty. template sends a pre-approved ChannelTemplate for out-of-window delivery. The two are MUTUALLY EXCLUSIVE — a template is the out-of-window send and a companion standalone media item would be rejected by the medium there, so setting both is refused rather than partially delivered. A channel that does not advertise the matching capability flag (supports_media_notifications / supports_template_notifications, the OPTIONAL class-attribute convention documented on Channel) never receives these fields. Attributes

ChannelTemplate

tai42_contract.channels.ChannelTemplate
A pre-approved, named template a channel sends outside its freeform window. Some media only accept arbitrary text inside a bounded conversation window (e.g. WhatsApp’s 24-hour customer-service window); outside it, the sole accepted send is an operator-authored template referenced by name in an approved language — both required and non-blank. parameters is the POSITIONAL list of body-text values substituted into the template’s body placeholders in order; it is empty when the template has no placeholders. Body text only — templates with header media, button parameters, or typed parameters (currency, date-time) are out of scope for this contract. Attributes