Notification Channels
Notification channels send approval requests to external platforms. Approvers can approve or reject directly from their email inbox, Slack workspace, Telegram chat, or WhatsApp conversation.How It Works
submitApprovalDecision() path. Audit logs, atomic counters, webhook delivery, and race-condition safety all apply regardless of channel.
Supported Channels
| Channel | Delivery | How Approvers Act |
|---|---|---|
| Rich HTML via Resend | Click Approve/Reject button links | |
| Slack | Block Kit message with buttons | Click interactive action buttons |
| Telegram | Message with inline keyboard | Tap inline keyboard buttons |
| Interactive button message | Tap reply buttons | |
| Webhook | JSON POST with tokens | POST tokens back to Conto API |
Setting Up Channels
Go to Settings > Channels in the Conto dashboard, or use the REST API.Choose a channel type
Click Add Channel and select the platform: Email, Slack, Telegram, WhatsApp, or Webhook.
Configure credentials
Each channel type requires different configuration:
Webhook targets must resolve to public addresses. Conto rejects loopback, private, link-local, and other internal-only destinations. In production, webhook channels must use HTTPS.
| Channel | Required Config |
|---|---|
| Comma-separated recipient email addresses | |
| Slack | Bot token (xoxb-...) and channel ID |
| Telegram | Bot token, chat ID, and optional webhook secret token |
| Phone number ID, access token, recipient numbers | |
| Webhook | HTTPS URL to receive JSON payloads |
Select event types
Choose which events trigger notifications:
| Event | When It Fires | Notes |
|---|---|---|
approval.requested | New payment needs approval | Available on all channel types |
approval.decided | Someone approved or rejected | Available on all channel types |
approval.escalated | Request escalated after timeout | Available on all channel types |
approval.expired | Request expired without resolution | Available on all channel types |
payment.executed | Payment is submitted onchain | WEBHOOK channels only |
Channel Configuration Details
Email
Uses Resend to deliver rich HTML emails. Each eligible approver receives their own email with unique Approve and Reject buttons.Config fields:
recipients- List of email addresses. Only addresses matching eligible approvers receive actionable emails.
Slack
Slack
Requires a Slack app with the
chat:write bot scope. Messages use Block Kit with payment details and interactive Approve/Reject buttons.Config fields:botToken- Your Slack app’s bot token (xoxb-...)channelId- The Slack channel ID to post messages to
- Create a Slack app at api.slack.com/apps
- Add the
chat:writebot scope - Install the app to your workspace
- Set the interactivity request URL to
https://conto.finance/api/webhooks/slack - Set
SLACK_SIGNING_SECRETin your Conto environment
Telegram
Telegram
Uses the Telegram Bot API to send messages with inline keyboard buttons.Config fields:
botToken- Your Telegram bot token from @BotFatherchatId- The chat or group ID to send messages tosecretToken- Optional webhook secret. When set, Conto requires Telegram to send the matchingx-telegram-bot-api-secret-tokenheader on every callback.
- Create a bot via @BotFather
- Set the webhook URL:
https://api.telegram.org/bot{token}/setWebhook?url=https://conto.finance/api/webhooks/telegram - If you configure
secretToken, include Telegram’ssecret_tokenoption when callingsetWebhook - Add the bot to your group chat
chatId. If
secretToken is set, Telegram callbacks must also include the matching
secret header before approval tokens are processed.When an approver taps a button, the message updates to show the result.WhatsApp
Uses the WhatsApp Cloud API to send interactive button messages.Config fields:
phoneNumberId- Your WhatsApp Business phone number IDaccessToken- Permanent access token from MetarecipientNumbers- Phone numbers in international format (e.g.,+1234567890)
- Register at developers.facebook.com
- Create a WhatsApp Business app
- Set the webhook URL to
https://conto.finance/api/webhooks/whatsapp - Set
WHATSAPP_APP_SECRETandWHATSAPP_VERIFY_TOKENin your Conto environment - Subscribe to the
messageswebhook field
Webhook
Webhook
Sends a signed JSON payload to any HTTPS endpoint. Use this for custom integrations that need to review and submit approval decisions.Config fields:To submit a decision, POST the token back to the
url- Your HTTPS endpoint URL
- The destination must resolve to a public IP address
- Private, loopback, link-local, and
.internal/.localhosts are rejected - In production, only
https://webhook targets are accepted
actionUrl:REST API
Manage channels programmatically:Action Token Security
Action tokens are the core security mechanism for external approvals.- 32 bytes of cryptographic randomness, base64url-encoded
- Protected at rest. Plaintext tokens are not retained after issuance
- One-time use. Tokens cannot be reused after a decision is recorded
- Time-limited. Tokens expire when the approval request expires (default 24 hours)
- Per-user, per-action. Each approver gets separate Approve and Reject tokens
- Full audit trail. Every decision records the channel, token ID, IP address, and user agent
Environment Variables
| Variable | Required For | Description |
|---|---|---|
RESEND_API_KEY | Resend API key for sending emails | |
SLACK_SIGNING_SECRET | Slack | Slack app signing secret for webhook verification |
WHATSAPP_APP_SECRET | Meta app secret for webhook signature verification | |
WHATSAPP_VERIFY_TOKEN | Token for Meta webhook verification challenge |
Webhook Payload Validation
Inbound webhook payloads from Slack, Telegram, and WhatsApp are validated against typed Zod schemas before processing. Malformed payloads are rejected with a400 response and never reach handler logic. Signature verification
(HMAC) still runs first for all three providers regardless of payload shape.
Delta Verification Channel (pilot)
For organizations enrolled in the Delta Mandate pilot (Organization.deltaPilotEnabled = true), a WEBHOOK channel can be marked
as a Delta channel via its config to receive a richer, machine-friendly
payload built for automated verification.
A Delta channel is a normal WEBHOOK channel with two extra fields in its
config JSON:
approval.requested fires for a Delta-flagged channel, the outbound
payload includes invoice context from the typed PaymentRequest invoice
record when present, with a fallback to legacy context.invoice data for
older requests. It also includes the counterparty record and a pair of
pre-minted single-use action tokens so the verifier can call back
immediately:
X-Conto-Signature: sha256(${X-Conto-Timestamp}.${rawBody})).
For SDK-created payments, Conto now opens the approval request as part of the
same flow when a matching workflow exists, so approval.requested webhooks are
emitted without requiring a separate dashboard step. The SDK response also
returns the approvalRequestId for traceability during testing.
The verifier calls back into the existing action endpoint to record its
decision and (optionally) attach a proof reference:
DeltaVerification row joined to the
PaymentRequest, and surfaces on the Delta tab in the dashboard.
For new SDK-created requests, Conto also stores the invoice fields from
context.invoice in a typed PaymentRequest invoice relation. That typed
record is what Delta webhook delivery now prefers when building approval and
payment.executed payloads.
Non-Delta WEBHOOK channels keep the standard payload shape — this
enrichment is fully opt-in via the delta: true config flag.
For production verifiers, prefer the signed internal callback route:
actionToken.callbackUrl in the webhook payload remains the backwards-
compatible demo path. Both routes persist the same verification {} block.
If the same Delta channel also subscribes to payment.executed, Conto sends
an execution webhook after the payment is submitted onchain so the verifier
can mark its invoice row PAID without polling:
Next Steps
Approval Workflows
Configure multi-approval workflows with escalation and sequential approvals
Securing Agents
Set up spending limits and approval thresholds for your agents