Skip to main content

Overview

The Scheduled Messages API allows you to schedule messages to be sent at specific times or on a recurring schedule. Messages are processed by a background job queue.

List Scheduled Messages

GET /scheduled Retrieve all scheduled messages for the authenticated user with pagination.

Query Parameters

status
string
Filter by status: pending, paused, completed, or all
type
string
Filter by schedule type: once, recurring, or all
page
integer
default:"1"
Page number

Response

messages
object
Paginated scheduled messages
data
array
Array of scheduled message objects
id
integer
Scheduled message ID
webhook_id
integer
Associated webhook ID
template_id
integer
Associated template ID (nullable)
message_content
object
Message payload (same as send message)
schedule_type
string
Either once or recurring
scheduled_at
string
ISO timestamp for one-time messages
recurrence_pattern
object
Recurrence configuration for recurring messages
timezone
string
User’s timezone (IANA format)
next_send_at
string
ISO timestamp of next scheduled send
status
string
Status: pending, paused, completed
send_count
integer
Number of times message has been sent
max_sends
integer
Maximum sends before completing (nullable)
current_page
integer
Current page number
per_page
integer
Items per page (20)
total
integer
Total scheduled messages
curl -X GET "https://your-domain.com/scheduled?status=pending&type=recurring" \
  -H "Accept: application/json" \
  -H "X-CSRF-TOKEN: your-csrf-token" \
  --cookie "session-cookie"
Response
{
  "messages": {
    "data": [
      {
        "id": 1,
        "user_id": 1,
        "webhook_id": 5,
        "template_id": 2,
        "message_content": {
          "content": "Daily report for {date}",
          "embeds": [...]
        },
        "schedule_type": "recurring",
        "scheduled_at": null,
        "recurrence_pattern": {
          "frequency": "daily",
          "time": "09:00",
          "days": null
        },
        "timezone": "America/New_York",
        "next_send_at": "2024-01-16T14:00:00.000000Z",
        "status": "pending",
        "send_count": 15,
        "max_sends": null,
        "webhook": {
          "id": 5,
          "name": "Daily Reports"
        },
        "template": {
          "id": 2,
          "name": "Daily Report Template"
        },
        "created_at": "2024-01-01T10:00:00.000000Z",
        "updated_at": "2024-01-15T14:00:00.000000Z"
      }
    ],
    "current_page": 1,
    "per_page": 20,
    "total": 45
  },
  "filters": {
    "status": "pending",
    "type": "recurring"
  }
}

Create Scheduled Message

POST /scheduled Schedule a new message for one-time or recurring delivery.

Request Body

webhook_id
integer
required
Webhook ID to send message through
template_id
integer
Optional template ID to use
message_content
object
required
Message payload (same structure as send message)
message_content.content
string
Plain text content (max 2000 characters)
message_content.embeds
array
Array of embed objects (max 10)
schedule_type
string
required
Either once or recurring
scheduled_at
string
ISO 8601 timestamp (required if schedule_type is once)
recurrence_pattern
object
Recurrence configuration (required if schedule_type is recurring)
recurrence_pattern.frequency
string
required
Frequency: daily, weekly, monthly
recurrence_pattern.time
string
required
Time in HH:MM format (24-hour)
recurrence_pattern.days
array
Array of day numbers (0-6, Sunday=0) for weekly frequency
recurrence_pattern.day_of_month
integer
Day of month (1-31) for monthly frequency
timezone
string
required
User’s timezone in IANA format (e.g., America/New_York, Europe/London)
max_sends
integer
Maximum number of times to send (for recurring). Null = unlimited
files
array
File attachments (multipart/form-data). Max 10 files, 10MB each
{
  "webhook_id": 5,
  "template_id": 2,
  "message_content": {
    "content": "This message will be sent once at the scheduled time",
    "embeds": [
      {
        "title": "Scheduled Announcement",
        "description": "Important update coming at {datetime}",
        "color": 3447003
      }
    ]
  },
  "schedule_type": "once",
  "scheduled_at": "2024-01-20T15:00:00",
  "timezone": "America/New_York"
}

Response

Returns 302 redirect to /scheduled with success message.

Update Scheduled Message

PUT/PATCH /scheduled/{id} Update a scheduled message. Only allowed if the message hasn’t been sent yet (send_count is 0).

Path Parameters

id
integer
required
Scheduled message ID

Request Body

Same structure as Create Scheduled Message. Additional fields:
remove_files
array
Array of file IDs to remove
{
  "webhook_id": 5,
  "message_content": {
    "content": "Updated message content"
  },
  "schedule_type": "recurring",
  "recurrence_pattern": {
    "frequency": "daily",
    "time": "10:00"
  },
  "timezone": "America/New_York",
  "remove_files": [1, 2]
}

Response

Returns 302 redirect to /scheduled with success message.
Cannot edit messages that have already been sent (send_count > 0)

Delete Scheduled Message

DELETE /scheduled/{id} Permanently delete a scheduled message and all associated files.

Path Parameters

id
integer
required
Scheduled message ID
curl -X DELETE https://your-domain.com/scheduled/1 \
  -H "X-CSRF-TOKEN: your-csrf-token" \
  --cookie "session-cookie"

Response

Returns 302 redirect to /scheduled with success message.

Pause Scheduled Message

POST /scheduled/{id}/pause Pause a scheduled message to prevent it from being sent.

Path Parameters

id
integer
required
Scheduled message ID
curl -X POST https://your-domain.com/scheduled/1/pause \
  -H "X-CSRF-TOKEN: your-csrf-token" \
  --cookie "session-cookie"

Response

Returns 302 redirect back with success message. Status changes from pending to paused.

Resume Scheduled Message

POST /scheduled/{id}/resume Resume a paused scheduled message.

Path Parameters

id
integer
required
Scheduled message ID
curl -X POST https://your-domain.com/scheduled/1/resume \
  -H "X-CSRF-TOKEN: your-csrf-token" \
  --cookie "session-cookie"

Response

Returns 302 redirect back with success message. Status changes from paused to pending and next_send_at is recalculated.

Recurrence Patterns

Daily Recurrence

Sends every day at the specified time.
{
  "frequency": "daily",
  "time": "09:00"
}

Weekly Recurrence

Sends on specific days of the week.
{
  "frequency": "weekly",
  "time": "14:00",
  "days": [1, 3, 5]
}
Above example sends every Monday, Wednesday, and Friday at 2 PM.

Monthly Recurrence

Sends on a specific day of each month.
{
  "frequency": "monthly",
  "time": "10:00",
  "day_of_month": 15
}
Above example sends on the 15th of each month at 10 AM.

Timezone Support

All scheduled messages respect the user’s timezone. Times are stored in UTC but calculated based on the provided timezone.

Common Timezones

  • America/New_York - Eastern Time (US)
  • America/Chicago - Central Time (US)
  • America/Denver - Mountain Time (US)
  • America/Los_Angeles - Pacific Time (US)
  • Europe/London - GMT/BST
  • Europe/Paris - CET/CEST
  • Asia/Tokyo - Japan Standard Time
  • Australia/Sydney - AEST/AEDT
  • UTC - Coordinated Universal Time
Use IANA timezone database names. See full list.

Status Lifecycle

1

Pending

Message is scheduled and will be sent at the next scheduled time.
2

Paused

Message is temporarily disabled and won’t be sent until resumed.
3

Completed

For one-time messages: Already sent. For recurring: Reached max_sends limit.

File Attachments

Scheduled messages can include file attachments:
  • Files are stored locally in storage/app/scheduled_messages/{id}/
  • Maximum 10 files per message
  • Maximum 10MB per file
  • Supported formats: jpg, jpeg, png, gif, webp, mp4, mov, avi
  • Files are automatically deleted when message is deleted

File Upload

const formData = new FormData();
formData.append('webhook_id', '5');
formData.append('schedule_type', 'once');
formData.append('scheduled_at', '2024-01-20T15:00:00');
formData.append('timezone', 'America/New_York');
formData.append('message_content[content]', 'Message with file');
formData.append('files[]', fileInput.files[0]);

fetch('/scheduled', {
  method: 'POST',
  headers: {
    'X-CSRF-TOKEN': csrfToken
  },
  credentials: 'include',
  body: formData
})

Background Processing

Scheduled messages are processed by Laravel’s queue system:
  1. A scheduled job runs every minute
  2. Checks for messages where next_send_at <= now() and status = 'pending'
  3. Sends the message via Discord webhook
  4. Updates send_count and calculates next_send_at for recurring messages
  5. Changes status to completed if:
    • One-time message already sent
    • Recurring message reached max_sends
Ensure your Laravel queue worker is running for scheduled messages to be processed:
php artisan queue:work

Authorization

OperationPermission Required
ListOwner (user_id matches)
CreateAuthenticated + webhook access
ViewOwner
UpdateOwner
DeleteOwner
Pause/ResumeOwner
Users can only manage their own scheduled messages. There’s no sharing functionality for scheduled messages.

Best Practices

Always specify the user’s local timezone, not UTC. The system handles timezone conversion automatically.
For recurring messages, use templates to easily update message content without recreating the schedule.
For time-limited campaigns, set max_sends to prevent infinite recurring messages.
Create a one-time scheduled message in the near future to test before setting up recurring schedules.
Check send_count regularly to ensure messages are being delivered as expected.
Temporarily disable messages with pause instead of deleting, so you can resume later without recreating.

Error Handling

Common Errors

  • Invalid webhook: Webhook doesn’t exist or user doesn’t have access
  • Invalid timezone: Timezone string not recognized
  • Past timestamp: Scheduled time is in the past (one-time messages)
  • Invalid recurrence: Missing required fields for recurring messages
  • Cannot edit sent message: Trying to edit a message that already sent

Failed Sends

If a scheduled message fails to send:
  1. Error is logged in application logs
  2. next_send_at is still updated for recurring messages
  3. Message remains in pending status
  4. Check webhook validity and Discord API status

Build docs developers (and LLMs) love