Skip to main content
Chert treats every Slack credential as server-side and every inbound event as untrusted until verified.

Before You Begin

  • Chert Notifications and Chert Agent are installed via the OAuth flow described in Install.

OAuth token storage

PropertyValue
Where storedPer-project row in the slack_installations table
Token typeSlack bot token (xoxb-…)
Exposure to browsersNever returned by the API
Status endpointReturns workspace metadata only — team id, team name, bot user id, install timestamp
DisconnectPOST /api/slack/disconnect deletes the row; the bot can no longer post for that project
Chert does not call Slack’s auth.revoke on disconnect. To fully revoke the install on Slack’s side, remove the app from Slack’s Manage apps page.

Signing-secret verification

Every inbound webhook from Slack is verified before any logic runs.
AppSigning secret env var
Chert Agent events webhookSLACK_SIGNING_SECRET
Chert Notifications events webhookSLACK_HELPER_SIGNING_SECRET
The verification follows Slack’s documented v0 scheme:
FieldValue
Basestringv0:<x-slack-request-timestamp>:<raw body>
Headerx-slack-signature: v0=<hex hmac-sha256>
Freshness window5 minutes (timestamp must be within ±300 s of server time)
ComparisonTiming-safe
A failed verification returns 401 with a structured error body — { ok: false, code: 2004, message, retryable: false, trace_id } — and the event is dropped. If the Slack signing secret is missing server-side the route returns 500 with code 3003 (PROVIDER_NOT_CONFIGURED, retryable: false) instead. Slack’s one-time url_verification handshake is the only event accepted without signing — it still returns a bare 200 with the raw challenge string, unchanged. Genuine error paths across /api/slack/* now carry the structured body (numeric code, generic message, retryable, trace_id); the OAuth callback redirects to /settings with a stable short reason slug (no_code, slack_denied, oauth_exchange_failed, not_configured, …) rather than raw error text in the URL. See the Messaging API error scheme for the code registry.

Event deduplication

Slack retries webhooks on slow responses or network errors. Chert deduplicates by Slack’s event_id for 10 minutes, so retried events do not double-fire actions like proposals or sends.

Channel-binding scope

ScopeWhat it means
One channel per projectNotifications and agent posts target the bound channel only.
Cross-project isolationEach project’s bot token, settings, and canvases are independent of every other project, even on the same workspace.
Private channelsThe bot must be invited (/invite @Chert Notifications) before it can post or write canvases.

What the bot reads vs writes

OperationTypeSurface
List channels for the pickerReadconversations.list
Post reply notification cardsWritechat.postMessage
Post agent responsesWritechat.postMessage
Update agent progress messagesWritechat.update
Read thread history for contextReadconversations.replies (only on threads under bot-posted messages)
Read reactions on proposal cardsReadreactions.get (only on bot-posted messages)
Create and edit canvasesWriteconversations.canvases.create, canvases.edit, canvases.delete
Upload xlsx previewsWritefiles.getUploadURLExternal, files.completeUploadExternal
The bot does not browse channel history beyond threads attached to its own messages, does not read direct messages, and does not read user profiles.

Webhook signing for outbound notifications

When notificationMethod is webhook or both and a webhookSecret is configured, every outbound POST from Chert carries an x-chert-signature: v1,<ts>,<hex> header. The signature is HMAC-SHA256 over <ts>.<raw-body> using your webhookSecret. Verify on receipt to authenticate the payload.

Sub-processors

VendorPurposeData
SlackNotification surface, agent surfaceLead names, message bodies, conversation excerpts, project metadata
AnthropicLLM drafting and classificationSame — used to draft replies and answer thread questions
ResendEmail notificationsOperator email addresses, reply previews
All three are bound by their respective DPAs. Customers can disable the LLM surface by turning off AI-drafted replies in Settings.

See Also

Configuration

Routing, flags, and the Nth-reply filter.

Notifications

Card anatomy and thread-reply triage.