Slack Webhook
Post messages to Slack through an incoming webhook URL.
Purpose
This skill is for sending Slack notifications with Slack Incoming Webhooks by calling the webhook URL directly.
Incoming webhooks post to the Messages surface. They do not create modals or Home tabs.
Inputs:
- message text, or a complete Slack webhook JSON payload
- Slack incoming webhook URL from
SLACK_WEBHOOK_URL,--webhook-url-stdin, or the locally saved URL - optional Block Kit
blocks - optional
thread_ts,unfurl_links, andunfurl_media
Output:
- dry-run JSON payload by default
- Slack HTTP status and response body when
--sendis explicitly provided
Sending Slack messages is externally visible and cannot be undone by this webhook. Always confirm the channel/workspace implied by the webhook URL and the final message content before running with --send.
What This Skill Needs
- user-provided message text or payload JSON
- a Slack incoming webhook URL, usually
SLACK_WEBHOOK_URL curlbashpython3
Webhook URL resolution order:
SLACK_WEBHOOK_URL--webhook-url-stdinfor the current run- locally saved URL at
~/.config/slack-webhook/webhook_url
If no webhook URL is available, dry-run payload preview still works. Sending requires a webhook URL. When the user has not configured one yet, direct them to https://frevana.gitbook.io/frevana-docs/connect-chat-apps/slack-webhook-integration for setup instructions.
Users can save a webhook URL with:
SLACK_WEBHOOK_URL="https://hooks.slack.com/services/..." \
bash <skill-path>/scripts/send_slack_webhook.sh \
--save-webhook-url
For a one-time non-interactive send without exporting an environment variable first:
printf '%s\n' 'https://hooks.slack.com/services/...' | \
bash <skill-path>/scripts/send_slack_webhook.sh \
--webhook-url-stdin \
--text "Deploy completed." \
--send
or clear it with:
bash <skill-path>/scripts/send_slack_webhook.sh --clear-webhook-url
Never print the webhook URL back to the user. Treat it as a secret.
Execution Order
- Extract the final Slack message text or payload JSON.
- Prefer
scripts/send_slack_webhook.shover ad hoccurl. - Run a dry run first unless the user already asked for an immediate Slack post and the final message is unambiguous.
- Review the dry-run payload for channel intent, text fallback, Block Kit structure, thread target, and unfurl behavior.
- If
--sendis used in an interactive terminal and no webhook is configured yet, the script prompts for one-time secure input and can save it for future runs. - Run again with
--sendonly after explicit user approval. - Treat Slack response body
okwith HTTP200as posted. Report non-okSlack error strings such asinvalid_payload,no_text,no_service,channel_not_found,channel_is_archived, oraction_prohibited.
Commands
Dry-run a simple message
bash <skill-path>/scripts/send_slack_webhook.sh \
--text "Deploy completed."
Actually post a message
bash <skill-path>/scripts/send_slack_webhook.sh \
--text "Deploy completed. :rocket:" \
--send
Use Slack mrkdwn
bash <skill-path>/scripts/send_slack_webhook.sh \
--text "*Production deploy* finished successfully."
Slack mrkdwn examples:
| Syntax | Result |
|---|---|
*bold* |
bold text |
_italic_ |
italic text |
~strike~ |
strikethrough text |
`code` |
inline code |
| `<https://example.com | label>` |
:white_check_mark: |
emoji |
Send a Block Kit payload
bash <skill-path>/scripts/send_slack_webhook.sh \
--text "Deployment status" \
--blocks-json '[{"type":"section","text":{"type":"mrkdwn","text":"*Production deploy* succeeded."}}]'
text is still recommended as a fallback for notifications and clients that do not render blocks.
Supported Block Kit block types for webhook messages are based on Slack's Messages-surface block reference:
actionscardcarouselcontainercontextcontext_actionsdata_tabledata_visualizationdividerfileheaderimagemarkdownplanrich_textsectiontabletask_cardvideo
Blocks that are not available on the Messages surface, such as modal-only blocks like input or alert, should not be sent through this skill.
More Block Kit examples
Section + Context + Actions
bash <skill-path>/scripts/send_slack_webhook.sh \
--text "Build status update" \
--blocks-json '[
{"type":"section","text":{"type":"mrkdwn","text":"*Build succeeded* for `main`."}},
{"type":"context","elements":[{"type":"mrkdwn","text":"Commit: `a1b2c3d` • Duration: 4m 12s"}]},
{"type":"actions","elements":[
{"type":"button","text":{"type":"plain_text","text":"View build"},"url":"https://example.com/builds/123"},
{"type":"button","text":{"type":"plain_text","text":"Open logs"},"url":"https://example.com/builds/123/logs"}
]}
]'
Header + Table
bash <skill-path>/scripts/send_slack_webhook.sh \
--text "Daily KPI summary" \
--blocks-json '[
{"type":"header","text":{"type":"plain_text","text":"Daily KPI Summary"}},
{"type":"table","rows":[
[{"type":"raw_text","text":"Metric"},{"type":"raw_text","text":"Value"}],
[{"type":"raw_text","text":"Revenue"},{"type":"raw_text","text":"$12,430"}],
[{"type":"raw_text","text":"Orders"},{"type":"raw_text","text":"184"}],
[{"type":"raw_text","text":"Refund rate"},{"type":"raw_text","text":"1.2%"}]
]}
]'
Video
bash <skill-path>/scripts/send_slack_webhook.sh \
--text "Release demo video" \
--blocks-json '[
{
"type":"video",
"title":{"type":"plain_text","text":"Release Demo"},
"title_url":"https://example.com/demo",
"description":{"type":"plain_text","text":"Walkthrough of the latest release."},
"thumbnail_url":"https://example.com/demo-thumb.jpg",
"video_url":"https://example.com/demo.mp4",
"alt_text":"Release demo preview"
}
]'
Carousel
bash <skill-path>/scripts/send_slack_webhook.sh \
--text "Top items this week" \
--blocks-json '[
{
"type":"carousel",
"elements":[
{
"type":"card",
"title":{"type":"plain_text","text":"Item A"},
"description":{"type":"plain_text","text":"Highest conversion rate this week."}
},
{
"type":"card",
"title":{"type":"plain_text","text":"Item B"},
"description":{"type":"plain_text","text":"Most saved by users."}
}
]
}
]'
When using newer block types such as table, video, carousel, card, plan, or data_visualization, prefer --payload-file if the JSON becomes large. Dry-run first to verify Slack accepts the payload shape for your workspace and webhook app configuration.
Send a complete payload file
bash <skill-path>/scripts/send_slack_webhook.sh \
--payload-file ./slack_payload.json \
--send
Reply in a thread
bash <skill-path>/scripts/send_slack_webhook.sh \
--text "Follow-up status update" \
--thread-ts "1712345678.123456" \
--send
Save the dry-run payload
bash <skill-path>/scripts/send_slack_webhook.sh \
--text "Queued notification" \
--output ./out/slack-payload.json
Fixed Request Shape
The script sends a POST request to the configured incoming webhook URL with:
Content-Type: application/json- JSON payload containing either a user-provided complete payload, or a constructed payload from the supported options
Constructed payload fields:
text; required unless--payload-jsonor--payload-fileis usedblocks; optional JSON array from--blocks-jsonattachments; optional JSON array from--attachments-jsonthread_ts; optional Slack message timestampunfurl_links; optional booleanunfurl_media; optional boolean
The script locally validates blocks against Slack's supported Messages-surface block types and enforces the 50-block message limit. Do not invent unsupported Slack fields. Use --payload-json or --payload-file when the user needs a full custom Incoming Webhook payload.
Script Options
--text TEXT; message text and fallback--text-file PATH; read message text from a UTF-8 file--payload-json JSON; complete Slack webhook payload object--payload-file PATH; complete Slack webhook payload file--blocks-json JSON; Block Kit blocks array--attachments-json JSON; Slack attachments array--thread-ts TS; post as a thread reply--unfurl-links true|false; control link unfurling--unfurl-media true|false; control media unfurling--webhook-url-stdin; read a one-time webhook URL from stdin--save-webhook-url; save the resolved webhook URL for future runs--configure-webhook-url; prompt for and save a webhook URL--clear-webhook-url; remove the locally saved webhook URL--output PATH; save dry-run payload or send metadata JSON; save failures do not change the send result--send; actually call the Slack webhook
Notes
- Incoming webhooks are bound to one configured Slack destination. The script cannot choose arbitrary channels unless the webhook app configuration supports it.
- Incoming webhooks are send-only. They cannot read messages, delete messages, list channels, or manage users.
- Webhook URLs are secrets. Never echo a webhook URL, save it in logs, or include it in user-facing output.
- Messages containing
!can break ad hoc interactive shell commands because of history expansion. Prefer the bundled script with quoted arguments or--text-file. - Slack commonly rate-limits incoming webhooks around one message per second. Avoid bulk posting unless the user explicitly asks for it.
- If a user shares a webhook URL in chat, advise them to rotate it if it may have been exposed.
Example Prompts
Chinese
- "发一条 Slack 通知说部署完成"
- "用 Slack webhook 发这个 Block Kit payload"
- "把这段内容 dry-run 成 Slack webhook JSON"
- "保存一下 Slack webhook URL,以后发通知用"
English
- "Post this to Slack via webhook"
- "Send a Slack notification that the deploy completed"
- "Dry-run the Slack webhook payload"
- "Reply in this Slack thread using thread_ts"