Slack chat.scheduleMessage Quota & Lifecycle Prompt
Design scheduled-message orchestration with chat.scheduleMessage that respects the 30-scheduled-per-channel limit, the 120-day horizon, cancellation, and dedup.
- Target user
- Engineers building scheduled-notification systems on Slack
- Difficulty
- Intermediate
- Tools
- Claude, ChatGPT, Cursor
The prompt
You are a senior platform engineer who has built scheduled-notification systems on Slack and hit every undocumented edge of `chat.scheduleMessage` in production. I will provide: - The use case ([maintenance reminders / standup pings / deploy-freeze notices / digest delivery]) - Volume (how many scheduled messages, how many channels) - Lead time and recurrence needs - My persistence layer for tracking scheduled IDs Your job: 1. **Hard limits** — enforce the real constraints: a channel can hold at most ~30 scheduled messages from an app, scheduling more than ~120 days out is rejected, and you cannot schedule into the past. Show how to check current load with `chat.scheduledMessages.list` before scheduling. 2. **ID lifecycle** — `chat.scheduleMessage` returns a `scheduled_message_id`; persist it so you can `chat.deleteScheduledMessage` later. Design the store keyed to your domain entity (which maintenance window, which standup). 3. **Recurrence** — Slack has no native recurrence; show how to schedule the next occurrence at send time (or via your own scheduler) and avoid drift. 4. **Cancellation & updates** — there is no update; to change a scheduled message you delete and re-create. Handle the race where the message already fired. 5. **Dedup** — prevent scheduling the same logical message twice (idempotency key in your store), important after retries or redeploys. 6. **Quota pressure** — what to do when a channel nears the 30-message cap: batch, consolidate, or fall back to a scheduler that posts via `chat.postMessage` at send time. Output as: (a) limits enforcement checklist, (b) scheduled-ID store schema, (c) recurrence strategy, (d) delete-and-recreate flow with the fired-race handled, (e) idempotency design, (f) quota-pressure fallback. Safety: persist every scheduled_message_id so you can cancel; an orphaned scheduled message will fire even after you've torn down the feature.
Why this prompt works
chat.scheduleMessage looks like a fire-and-forget convenience until you operate it at any scale, at which point the undocumented-feeling limits start rejecting your calls: roughly 30 scheduled messages per channel per app, a horizon around 120 days, and no scheduling into the past. This prompt front-loads those constraints and requires a pre-flight check with chat.scheduledMessages.list, so the design accounts for quota pressure instead of discovering it as a wall of API errors during a busy maintenance week.
The ID-lifecycle requirement is the part teams skip and regret. Every scheduled message returns an ID that is your only handle for cancellation, and there is no update endpoint — changing a message means delete-and-recreate, which races against the actual send time. Forcing a persisted store keyed to the domain entity, plus explicit handling of the already-fired race, produces a design where an abandoned feature can’t keep paging a channel weeks later because nobody saved the IDs.
The dedup and quota-pressure sections push the AI past the happy path into the operational reality of redeploys and retries, where the same logical reminder can get scheduled twice, and channels can fill up. By demanding an idempotency key and a concrete fallback to send-time chat.postMessage, the prompt yields something a human can review against real volume — and the safety notes keep the orphaned-message failure mode front of mind, since that’s the one that embarrasses you in production.