approval="presentation" on your Agent and run a Telegram bot β Allow/Deny buttons render on chat, resolve against a SQLite store, and survive a restart.
Quick Start
1
Auto-wire from the agent
Set The bot detects the backend on the agent and connects it to the chat automatically. Unknown decision values (anything outside
approval="presentation" on the Agent β the bot wires the durable backend to the chat on start(). No register_approval_backend() call needed.allow, deny, always) are rejected before reaching the backend β fail closed.2
Manual wire (advanced)
Wire a backend you constructed separately β for tests or custom bootstrap where the backend is built by hand.
register_approval_backend gives the backend the channel renderer and target, then enables the /approve callback route.How It Works
An agent tool call persists to the store, renders Allow/Deny buttons, and unblocks only when an authorised tap resolves the approval ID. Each decision binds to an unguessableapproval_id, not a message id β so a late tap after a redeploy still resolves against the SQLite store instead of falling through as an unhandled callback.
What the user sees
The whole point of the feature β a non-developer only ever sees two buttons and a result. The original message updates in place: β Approved, β Denied, or β οΈ Approval could not be processed.If the originating turn was
/stopped or superseded while the Allow / Deny card was still open, the tap is dropped fail-closed by the core registry. The card updates to a βno longer applicableβ state and the decision recorded is ApprovalDecision(approved=False, reason="Turn no longer live (stopped or superseded)") β no session/always grant is persisted for the abandoned turn.Superseded turns
A reviewer can tap Allow minutes or hours after the card first appears. If the user cancelled the run (or a new turn took over) in the meantime, that late tap must not fire the tool. The core SDK enforces exactly this since PraisonAI #4950, so it applies to every backend on this page β not just the durable Telegram one:- The tool does not run.
- No
session/alwaysgrant is persisted for the abandoned turn. - The returned decision carries
reason="Turn no longer live (stopped or superseded)".
Configuration Options
Backend construction lives on the secure-backend page β this page only wires it to Telegram.Secure Approval Backend
PresentationApprovalBackend constructor args (store, allowed_actors, timeout)Durable Approvals
The SQLite
ApprovalStore schema behind the durable pathCommon Patterns
Auto-wire from agent (default). Setapproval="presentation" on the Agent and run the bot β nothing else. This is the Quick Start path.
rehydrate_approvals() runs automatically on start() and returns the restored count.
Best Practices
Set PRAISONAI_APPROVAL_ACTORS
Set PRAISONAI_APPROVAL_ACTORS
The backend is fail-closed β set the actor allowlist so only your approvers can resolve a tap. Without it, no actor is authorised.
One transport per backend
One transport per backend
Do not pass an already-wired backend from Slack to Telegram. When a backend already owns a sender, the coupling guard leaves it untouched so it never renders on one transport while addressing another.
Prefer the auto-wire path
Prefer the auto-wire path
Reserve
register_approval_backend() for tests or custom bootstrap. In normal deployments, approval="presentation" on the Agent is enough.Log the rehydrated count on startup
Log the rehydrated count on startup
start() logs how many approvals were restored. Watch that number to catch a broken deploy where pending approvals never come back.Related
Durable Approvals
The SQLite store behind the durable path
Secure Approval Backend
The
PresentationApprovalBackend this page wiresGateway Approval Durability
The gateway sibling of this transport wiring
Messaging Bots
Deploy agents to chat channels
Live-Authority Binding
Why a late tap on a stopped turn is dropped fail-closed

