# Slack and Discord Webhooks *Stream Aura events to the channels your team already lives in.* ## Overview Aura emits structured events for everything that happens inside the semantic engine: intent logs, impact alerts, zone claims, conflicts, sync pushes, Prove failures, and team messages. The webhook integration delivers these events to Slack and Discord (and any HTTP endpoint speaking the same JSON) in near-real time. Use webhooks to answer questions like: - Who is editing what, right now? - Which intents were logged in the last hour? - Did anyone's change break a Prove goal on main? - What functions did my teammates push to the mothership while I was at lunch? The webhook system is a pure publisher — Aura posts JSON to a URL you control. Authentication, retry logic, and rate limiting are handled by the emitter. ## Setup ### Slack 1. Create a Slack app at `api.slack.com/apps`. Enable **Incoming Webhooks**. 2. Add a webhook to the target channel (e.g. `#aura-intents`). Copy the URL. 3. Register the webhook with Aura: ```bash aura webhook add \ --name slack-intents \ --url 'https://hooks.slack.com/services/T.../B.../...' \ --events intent.logged,prove.failed,zone.blocked \ --format slack ``` Verify: ```bash aura webhook test slack-intents ``` A test message lands in the channel. ### Discord 1. In Discord, **Server Settings → Integrations → Webhooks → New Webhook**. Pick a channel. Copy the URL. 2. Register with the `discord` format: ```bash aura webhook add \ --name discord-mothership \ --url 'https://discord.com/api/webhooks/.../...' \ --events live.push,live.pull,conflict \ --format discord ``` The `slack` and `discord` formats produce payloads matching each platform's native structure. For generic HTTP, use `--format raw`. ### Hosting Webhook emission runs in the `aura daemon` process on each developer's machine *and* in the mothership server (if you run [Mothership](/mothership-overview)). For team-wide events, register the webhook on the mothership: ```bash aura --remote team-main webhook add ... ``` Individual events will then fire once per team, not once per developer. ## Configuration ### `.aura/webhooks.toml` ```toml [[webhook]] name = "slack-intents" url_env = "SLACK_INTENT_WEBHOOK" # Read from env var; never hard-code tokens. events = ["intent.logged", "prove.failed"] format = "slack" min_severity = "info" [[webhook]] name = "discord-alerts" url_env = "DISCORD_ALERT_WEBHOOK" events = ["conflict", "zone.blocked", "impact.new"] format = "discord" min_severity = "warning" [webhook_defaults] timeout_ms = 3000 retry = { max_attempts = 3, backoff = "exponential" } rate_limit = { per_minute = 60 } ``` Always use `url_env`, not `url`, in committed config. Webhook URLs are credentials. ### Event filters Filter events with expressions: ```toml [[webhook]] name = "slack-security" url_env = "SLACK_SEC_WEBHOOK" events = ["pr_review.finding"] filter = "severity == 'error' && category == 'security'" format = "slack" ``` ## Event Types Aura emits events under these topics. All events share a common envelope: ```json { "event": "intent.logged", "event_id": "evt_01HW3K9Z4X7P2Q8R", "timestamp": "2026-04-21T14:23:09Z", "actor": { "id": "ashiq", "kind": "human" }, "repo": "naridon-inc/aura", "branch": "feat/login-refresh", "data": { ... } } ``` | Topic | When it fires | |-------|---------------| | `intent.logged` | `aura_log_intent` succeeds. | | `intent.mismatch` | Hook detects intent diverging from AST diff. | | `prove.passed` | `aura prove` succeeds for a goal. | | `prove.failed` | `aura prove` fails. | | `pr_review.finding` | PR review surfaces a violation. | | `pr_review.done` | PR review completes (any outcome). | | `live.push` | Functions pushed to mothership. | | `live.pull` | Teammate changes pulled and merged. | | `impact.new` | Cross-branch impact detected. | | `impact.resolved` | Impact alert resolved. | | `zone.claimed` | A developer claims a file/function zone. | | `zone.blocked` | An edit was blocked by a zone. | | `conflict` | AST merge conflict requires human resolution. | | `msg.team` | Team message posted. | | `sentinel.msg` | AI-agent-to-agent message posted. | ## Example Payloads ### `intent.logged` (Slack format) ```json { "blocks": [ { "type": "header", "text": { "type": "plain_text", "text": "Intent logged — feat/login-refresh" } }, { "type": "section", "fields": [ { "type": "mrkdwn", "text": "*Author*\nashiq" }, { "type": "mrkdwn", "text": "*Nodes*\n3 modified, 1 added" } ] }, { "type": "section", "text": { "type": "mrkdwn", "text": "> Switch linear backoff to exponential for rate-limit compliance" } }, { "type": "context", "elements": [ { "type": "mrkdwn", "text": "commit `abc1234` · " } ] } ] } ``` ### `prove.failed` (raw format) ```json { "event": "prove.failed", "event_id": "evt_01HW3MX8ZQ", "timestamp": "2026-04-21T14:24:18Z", "actor": { "id": "ci", "kind": "automation" }, "repo": "naridon-inc/aura", "branch": "feat/login-refresh", "data": { "goal": "Refresh token rotation invalidates old token", "result": "fail", "gaps": [ { "node": "auth::rotate_refresh", "reason": "No edge to token_blocklist::add" } ], "commit": "abc1234" } } ``` ### `zone.blocked` (Discord format) ```json { "username": "Aura", "embeds": [{ "title": "Edit blocked — team zone", "color": 15158332, "fields": [ { "name": "File", "value": "src/billing/invoice.rs", "inline": true }, { "name": "Owner", "value": "jamie", "inline": true }, { "name": "Claimed for", "value": "Refactor invoice generation", "inline": false } ], "footer": { "text": "Message jamie or wait for the zone to release." } }] } ``` ### `impact.new` ```json { "event": "impact.new", "data": { "alert_id": "imp_01HW3P4K", "your_branch": "feat/login-refresh", "other_branch": "feat/session-model", "function": "auth::Session::refresh", "change": "signature_changed", "before": "fn refresh(&self) -> Result", "after": "fn refresh(&self, ctx: &Context) -> Result" } } ``` ## Troubleshooting **No events arriving in Slack.** Check the webhook URL isn't expired — Slack rotates them on app re-install. `aura webhook test ` surfaces the HTTP status. **Discord returns 429.** Discord rate-limits aggressively (5 req/2s per webhook). Lower `rate_limit.per_minute` or deduplicate via a dedicated webhook per topic. **Events emitted twice.** Two daemons are running (e.g. your laptop + mothership) and both have the same webhook. Register team-wide webhooks only on the mothership; register personal webhooks only on your laptop. **Payloads too large for Slack.** Slack caps at 40 KB. For `pr_review.done` on large PRs, use `--compact` on the webhook registration to truncate findings to top 10. **Sensitive data in payloads.** Use `redact_keys` in `.aura/config.toml` to strip fields before emission: ```toml [webhooks.redact] keys = ["email", "ssh_key", "api_token"] ``` ## Thread-Based Routing For Slack, route related events to the same thread. All events for a given branch can be grouped: ```toml [[webhook]] name = "slack-branch-threads" url_env = "SLACK_BRANCH_WEBHOOK" events = ["intent.logged", "prove.failed", "pr_review.done"] thread_key = "branch" # new thread per branch; events update the existing one ``` The first event in a branch opens a thread; subsequent events for the same branch reply to that thread. When the PR merges, Aura posts a final message and archives the thread with a reaction. Discord has no first-class threads, but Aura simulates thread-like grouping by prefixing messages with the branch name. ## Quiet Hours and Digest Mode Teams get webhook fatigue when every intent fires a notification. Two mitigations: **Quiet hours.** Suppress non-critical events during configured windows: ```toml [[webhook]] name = "slack-intents" url_env = "SLACK_INTENT_WEBHOOK" events = ["intent.logged"] quiet_hours = { tz = "America/New_York", from = "19:00", to = "08:00" } ``` Events during quiet hours are buffered and delivered at the next active window, collapsed into a single digest. **Digest mode.** Batch low-priority events into a single hourly message instead of one per event: ```toml [[webhook]] name = "slack-digest" url_env = "SLACK_DIGEST_WEBHOOK" events = ["intent.logged", "live.push"] digest = { every = "1h", max_entries = 50 } ``` The resulting message is a single Slack block summarizing all events in the interval, grouped by author. ## Retry and Delivery Semantics Aura's webhook emitter is designed to be well-behaved: - **At-least-once delivery.** If a post fails (network, 5xx), it is retried with exponential backoff up to `retry.max_attempts`. Idempotency is up to the receiver; each event has a unique `event_id` you can dedupe on. - **Ordering is per-topic.** Events within the same topic (e.g. all `intent.logged`) are delivered in causal order. Events across topics may interleave. - **Rate limit aware.** On a 429 response, Aura obeys `Retry-After` and slows subsequent posts to the same endpoint. - **Circuit breaker.** After 10 consecutive failures to the same webhook, Aura opens a circuit and pauses emission for 5 minutes. Repeated opens over a day auto-disable the webhook and alert via `aura doctor`. For strict ordering and exactly-once semantics, use the [HTTP events stream](/aura-api-reference) instead — webhooks optimize for simplicity, not durability. ## Security: Verifying Webhook Senders Slack and Discord verify Aura, but you may want to verify Aura verified them. Enable HMAC signing: ```toml [[webhook]] name = "slack-intents" url_env = "SLACK_INTENT_WEBHOOK" hmac_secret_env = "SLACK_INTENT_HMAC" ``` Each payload gets an `X-Aura-Signature: sha256=` header. The receiver recomputes the HMAC over the raw body and compares. This is most useful when the webhook destination is your own service, not Slack or Discord directly. ## Multi-Channel Routing Route events to different channels by severity or topic: ```toml [[webhook]] name = "slack-low-priority" url_env = "SLACK_LOW_WEBHOOK" events = ["intent.logged", "live.push"] [[webhook]] name = "slack-alerts" url_env = "SLACK_ALERT_WEBHOOK" events = ["prove.failed", "conflict"] [[webhook]] name = "slack-security" url_env = "SLACK_SEC_WEBHOOK" events = ["pr_review.finding"] filter = "category == 'security'" ``` Channel fan-out is done server-side; one event produces up to N HTTP posts. ## See Also - [Linear / Jira integration](/linear-jira-integration) — same event stream, different destination. - [Aura HTTP API](/aura-api-reference) — pull events instead of pushing. - [Mothership overview](/mothership-overview) — the server that hosts team-wide webhooks. - [Custom plugins](/custom-plugins) — write a plugin to consume events programmatically.