slick message¶
Send, edit, and delete Slack messages. Markdown input is converted to Block
Kit by default; --blocks accepts raw Block Kit JSON. Slack-side message
operations require chat:write (and matching channel scopes); user-token
profiles can edit and delete only their own messages.
slick message send Send a Slack message
slick message edit Edit an owned Slack message
slick message delete Delete an owned Slack message
slick message scheduled list List scheduled Slack messages
slick message scheduled delete Delete a scheduled Slack message
For thread replies, see reply. For reactions, see
react.
message send¶
slick message send --channel C1234567890 --message "Deploy complete"
echo "Deploy complete" | slick message send --channel C1234567890 --file -
# DMs (single user, group DM, or by Slack-profile email)
slick message send --user U1234567890 --message "Build artifact ready"
slick message send --user dev@example.com,ops@example.com --message "Review please"
# Raw Block Kit input
slick message send --channel C1234567890 --blocks --file blocks.json
# Preview without sending
slick message send --channel C1234567890 --message "Deploy complete" --dry-run
# Schedule a future send. Natural-language times are not accepted here. (_yet_)
slick message send --channel C1234567890 --message "Deploy tomorrow" --schedule 2026-06-01T15:00:00-04:00
slick message send --channel C1234567890 --message "Deploy in 90 minutes" --schedule 90m
slick message send --user dev@example.com --message "DM follow-up" --schedule 90m
Flags¶
-c, --channel <CHANNEL> Channel ID, name, or alias
-u, --user <USER>… User ID or alias; repeat or comma-separate for group DM
-m, --message <TEXT> Message body
-f, --file <FILE> Read message body from file or - for stdin
-N, --filename <NAME> Filename metadata for stdin sources
-b, --blocks Treat message source as raw Block Kit JSON
-s, --schedule <WHEN> Schedule for future send: RFC3339, Go duration, or Unix seconds
-n, --dry-run Preview without sending
--attribution Force attribution on for this command
--no-attribution Disable attribution for this command
--attribution-label <LABEL> Override attribution label
--attribution-emoji <EMOJI> Override attribution emoji
--attribution-message <TEXT> Override attribution message
If default_channel is set in config, both --channel and --user can be
omitted for immediate sends. Scheduled sends require an explicit --channel or
--user target.
--user accepts Slack user IDs, configured aliases, and Slack-profile email
addresses resolved via users.lookupByEmail. Repeat the flag or comma-separate
values to open a group DM. If Slack returns users_not_found for an email,
that address did not match a Slack user in the active workspace.
--schedule accepts only RFC3339 timestamps, Go durations such as 90m or
2h30m, and Unix seconds. It rejects every other format, including
natural-language strings such as tomorrow at 9am. Scheduled sends accept
either --channel or --user; scheduled DMs resolve/open the DM before calling
Slack's scheduled-message API. Real scheduled --user sends return the raw DM
or MPIM conversation ID in data.channel. Scheduled sends return
scheduled_message_id, not ts, because Slack does not assign a message
timestamp until the message fires.
Markdown handling¶
Markdown converts to semantic Block Kit blocks where possible (paragraphs, headings, tables). Unsupported block-level constructs — lists, blockquotes, fenced code, raw HTML — are preserved as readable section blocks rather than dropped.
Output¶
Human (real send):
When Slack metadata is available in human mode, channel may render as a
friendly clickable label such as #deployments or @johndoe. Use JSON output
for the raw conversation ID plus channel_name, channel_hr, and
channel_url metadata.
Human (--dry-run). ts is the literal "dry-run", no permalink is
emitted, and dry_run=true joins the event:
JSON envelope (real send):
{
"meta": {"command": "message.send", "workspace": "default", "timestamp": "…", "request_id": "…"},
"data": {
"message": {
"type": "message",
"text": "Deploy complete",
"ts": "1746284582.123456",
"channel": "C7N2Q8L4P",
"channel_name": "deployments",
"channel_hr": "#deployments",
"channel_url": "https://app.slack.com/client/T8KQ42P9D/C7N2Q8L4P"
},
"permalink": "https://example.slack.com/archives/C7N2Q8L4P/p1746284582123456",
"attribution": true
},
"errors": []
}
JSON envelope (--dry-run). No Slack call is made, so ts is the literal
string "dry-run", permalink is absent, and dry_run: true joins the
data record:
{
"data": {
"message": {
"type": "message",
"text": "Deploy complete",
"ts": "dry-run",
"channel": "C7N2Q8L4P",
"channel_url": "https://app.slack.com/client/T8KQ42P9D/C7N2Q8L4P"
},
"dry_run": true,
"attribution": true
}
}
permalink is Slack's chat.getPermalink HTTP URL in JSON. Human mode renders
the short form (p1746284582123456) as an OSC 8 terminal hyperlink on
supporting terminals.
JSON envelope (--schedule). Scheduled messages have no ts until Slack
posts them, so scheduled_message_id is the reference for list/delete:
{
"data": {
"channel": "C1234567890",
"channel_name": "deployments",
"channel_hr": "#deployments",
"channel_url": "https://app.slack.com/client/T8KQ42P9D/C1234567890",
"scheduled_message_id": "Q123",
"post_at": 1780335600,
"post_at_iso": "2026-06-01T19:00:00Z",
"text": "Deploy tomorrow",
"attribution": {"enabled": true, "label": "slick"}
}
}
message edit¶
Edit your own message. Slack permits edits only to messages your token identity sent.
slick message edit --channel C1234567890 --timestamp 1746284582.123456 --message "Corrected text"
slick message edit --channel C1234567890 --timestamp 1746284582.123456 --message "Updated" --dry-run
Flags¶
-c, --channel <CHANNEL> Channel ID, name, or alias
-t, --timestamp <TS> Message timestamp
-m, --message <TEXT> Message body
-f, --file <FILE> Read message body from file or - for stdin
-b, --blocks Treat message source as raw Block Kit JSON
-n, --dry-run Preview without mutating
--attribution Force attribution on for this command
--no-attribution Disable attribution for this command
--attribution-label <LABEL> Override attribution label
--attribution-emoji <EMOJI> Override attribution emoji
--attribution-message <TEXT> Override attribution message
Human (--dry-run):
JSON envelope (real edit). Slack's chat.update does not return a
permalink, and slick suppresses data.message.blocks even when the edit
re-renders Block Kit — verify rendered output via
history list or the Slack UI when needed.
The attribution field is still present:
{
"data": {
"message": {
"type": "message",
"text": "Corrected text",
"ts": "1746284582.123456",
"channel": "C1234567890"
},
"attribution": true
}
}
message delete¶
Delete your own message. Requires --force unless you also pass --dry-run.
slick message delete --channel C1234567890 --timestamp 1746284582.123456 --dry-run
slick message delete --channel C1234567890 --timestamp 1746284582.123456 --force
Flags¶
-c, --channel <CHANNEL> Channel ID, name, or alias
-t, --timestamp <TS> Message timestamp
-n, --dry-run Preview without mutating
-F, --force Confirm deletion
Output¶
Human:
JSON envelope keeps ts (formerly timestamp before v0.4.0):
The deleted field was removed in v0.4.0; the action label conveys success
and errors[] conveys failure.
message scheduled list¶
List pending scheduled messages. Pass --channel to narrow the result to one
conversation, or omit it to ask Slack for the workspace-visible scheduled
messages for the active token.
slick message scheduled list --channel C1234567890 --limit 20 --output=json
slick message scheduled list --cursor <next-cursor> --output=json
Flags¶
-c, --channel <CHANNEL> Channel ID, name, or alias
-C, --cursor <CURSOR> Pagination cursor
-L, --limit <N> Maximum scheduled messages to return
Human output in a TTY or with --output=human renders a table. CHANNEL
uses a readable #channel or @user name when Slack metadata is readable, and
DM marks direct-message conversations:
ID CHANNEL DM POST_AT TEXT
Q123 #deployments false 2026-06-01T19:00:00Z Deploy tomorrow
Q124 @johndoe true 2026-06-01T20:00:00Z Hello from slick — sched…
When there are no pending messages, human output keeps the same action label
and adds count=0:
JSON output uses data.scheduled_messages[] and cursor metadata:
{
"meta": {
"command": "message.scheduled.list",
"pagination": {"next_cursor": "cur-2", "has_more": true, "max_items": 20, "items_returned": 1}
},
"data": {
"scheduled_messages": [
{
"id": "Q123",
"channel": "C1234567890",
"channel_name": "deployments",
"channel_hr": "#deployments",
"channel_url": "https://app.slack.com/client/T8KQ42P9D/C1234567890",
"channel_type": "channel",
"is_dm": false,
"post_at": 1780335600,
"post_at_iso": "2026-06-01T19:00:00Z",
"text_preview": "Deploy tomorrow"
}
]
}
}
For automation, use JSON. The human CHANNEL value is display-only; pass the
raw data.scheduled_messages[].channel value back to
message scheduled delete or to future scheduled sends. JSON also adds
optional channel_name, channel_hr, channel_url, channel_type,
channel_user, and is_dm fields when metadata can be resolved.
text_preview is capped at 200 Unicode characters and ends with … when
truncated.
message scheduled delete¶
Delete a pending scheduled message by exact channel and scheduled-message ID.
No --force flag is required.
slick message scheduled delete --channel C1234567890 --scheduled-id Q123 --dry-run
slick message scheduled delete --channel C1234567890 --scheduled-id Q123 --output=json
Flags¶
-c, --channel <CHANNEL> Channel ID, name, or alias
--scheduled-id <QID> Scheduled message ID returned by scheduled send/list
-n, --dry-run Preview without mutating
JSON output:
Common errors¶
| Error | Cause | Fix |
|---|---|---|
validation_error: --channel or --user is required |
No destination given and default_channel isn't set in config. |
Provide --channel/--user, or set default_channel. |
not_in_channel (not_found, exit 2) |
App or user is not a member of the channel. | Add the bot to the channel, or use a user token that is a member. |
channel_not_found |
Bad channel ID or alias not configured. | Verify with lookup channel. |
user_not_found |
Email or alias did not resolve. | Use a known Slack user ID or verify via lookup user. |
cant_update_message / cant_delete_message (validation_error, exit 4) |
Slack disallows the edit/delete for this token identity. | Use the user token that originally sent the message. |
Schedule validation_error |
--schedule was in the past, beyond 120 days, or not RFC3339/duration/Unix seconds. |
Use a future RFC3339 timestamp, Go duration, or Unix seconds. |
Raw Block Kit validation_error with no stdout data |
Malformed JSON, unsupported block types, missing required fields, or Slack limits. | Inspect the --blocks JSON; the error envelope describes the rule that failed. |
See also¶
reply— thread replies (separate top-level command).react— reactions on messages.history— verify rendered output after edits.status,auth,config.- Slack API methods:
chat.postMessage,chat.update,chat.delete,chat.scheduleMessage,chat.scheduledMessages.list,chat.deleteScheduledMessage.