> ## Documentation Index
> Fetch the complete documentation index at: https://gowa-docs.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Scheduled Sends

> Send any message later, once or on a daily, weekly or monthly repeat, and manage schedules through /send/schedules.

![GOWA Message Scheduler Overview](https://mintcdn.com/gowa-docs/G0gUpNl3gUGyehVX/images/GOWA-Message-Scheduler-Overview.png?fit=max\&auto=format\&n=G0gUpNl3gUGyehVX\&q=85\&s=663bfdbb9e1c0bb451c0fa782380b139)

Any send request can be scheduled instead of sent immediately. Add `scheduled_at` and `timezone`; GOWA stores the request and a background worker sends it at that time. Schedules survive restarts and wait for an offline device.

Schedulable message types: text, image, file, video, audio, sticker, contact, link, location, poll and forward (`/message/{id}/forward`). Presence and typing indicators are not schedulable.

## Schedule fields

| Field | Required | Description |
| - | - | - |
| `scheduled_at` | yes | RFC 3339 time of the first send, in the future, e.g. `2026-11-01T09:00:00+07:00`. The first send always happens at this time. |
| `timezone` | yes | IANA zone (e.g. `Asia/Jakarta`) used to compute repeats, so "09:00 every day" stays 09:00 across DST changes. |
| `recurrence` | no | `once` (default), `daily`, `weekly` or `monthly`. |
| `weekdays` | weekly | Days to send, `0` = Sunday … `6` = Saturday, e.g. `[1, 3, 5]`. |
| `day_of_month` | monthly | `1`–`31`; clamped to the last day of shorter months. |
| `end_at` | no | Recurring only. No sends after this time. |
| `occurrence_limit` | no | Recurring only. Stop after this many sends. |

Without `scheduled_at`, the request is sent immediately as usual.

## Examples

```bash theme={null}
# once, tomorrow morning
curl -u admin:changeme -X POST http://localhost:3000/send/message \
  -H "Content-Type: application/json" -H "X-Device-Id: my-device" \
  -d '{
    "phone": "628123456789",
    "message": "Reminder: meeting at 10:00",
    "scheduled_at": "2026-11-01T09:00:00+07:00",
    "timezone": "Asia/Jakarta"
  }'
```

```json theme={null}
{
  "code": "SUCCESS",
  "message": "Success",
  "results": {
    "schedule_id": "0b5c3a8e-7f5d-4d6f-9d1e-2f8c1a7b9e10",
    "scheduled_at": "2026-11-01T02:00:00Z",
    "next_run_at": "2026-11-01T02:00:00Z"
  }
}
```

Scheduled responses have `schedule_id` instead of `message_id`.

```bash theme={null}
# every Monday, Wednesday and Friday at 08:30, 12 times
curl -u admin:changeme -X POST http://localhost:3000/send/message \
  -H "Content-Type: application/json" -H "X-Device-Id: my-device" \
  -d '{
    "phone": "120363025246125486@g.us",
    "message": "Standup in 30 minutes",
    "scheduled_at": "2026-11-02T08:30:00+07:00",
    "timezone": "Asia/Jakarta",
    "recurrence": "weekly",
    "weekdays": [1, 3, 5],
    "occurrence_limit": 12
  }'
```

```bash theme={null}
# monthly invoice on the 1st, with an uploaded file (multipart: repeat weekdays as separate fields if used)
curl -u admin:changeme -X POST http://localhost:3000/send/file \
  -H "X-Device-Id: my-device" \
  -F phone=628123456789 \
  -F file=@invoice.pdf \
  -F scheduled_at=2026-12-01T09:00:00+07:00 \
  -F timezone=Asia/Jakarta \
  -F recurrence=monthly \
  -F day_of_month=1 \
  -F end_at=2027-12-31T23:59:59+07:00
```

Uploaded files are kept on the server until the schedule finishes. In multipart bodies send `weekdays` as repeated fields (`-F weekdays=1 -F weekdays=3`).

## Manage schedules

Schedules belong to a device; send the same `X-Device-Id`.

| Action | Request |
| - | - |
| List | `GET /send/schedules` with optional `status`, `search` (recipient or text), `message_type`, `limit`, `offset` |
| Get one | `GET /send/schedules/{schedule_id}` |
| Pause | `POST /send/schedules/{schedule_id}/pause` |
| Resume | `POST /send/schedules/{schedule_id}/resume` |
| Cancel | `POST /send/schedules/{schedule_id}/cancel` |

```bash theme={null}
curl -u admin:changeme -H "X-Device-Id: my-device" \
  "http://localhost:3000/send/schedules?status=active&limit=25"
```

The list returns `results.data` and `results.pagination` (`limit`, default 25; `offset`; `total`).

### Statuses

| Status | Meaning |
| - | - |
| `active` | Waiting for its next run. |
| `running` | Claimed by the worker and sending. |
| `paused` | Held until resumed. |
| `completed` | No runs left. |
| `failed` | Gave up after retries, or the stored request was invalid. |
| `cancelled` | Cancelled by you. |

## Delivery behavior

* The worker checks for due schedules every 5 seconds.
* **Device offline or not logged in:** the run waits and retries every 30 seconds. Waiting does not count as a failed attempt, so a schedule survives long outages.
* **Send errors:** retried with backoff starting at 15 seconds and capped at 5 minutes, up to 10 attempts per occurrence. A recurring schedule then skips to its next occurrence; a one-off schedule becomes `failed`.
* **Invalid request** (for example a bad recipient): fails immediately without retrying.
* **Late runs:** an occurrence delayed past `end_at` is not sent.
* **Crashes and restarts:** shutdown does not abort a send already in flight. If the process dies mid-send, GOWA cannot know whether the message went out, so once the 2-minute claim expires that occurrence is skipped as interrupted and **not resent**. Recurring schedules continue with their next occurrence.

## Over MCP

The MCP tool `whatsapp_send` accepts the same schedule fields, and `whatsapp_schedule` lists, gets, pauses, resumes and cancels schedules. See [MCP server](https://gowa-docs.mintlify.site/integrations/mcp.md).

> [!NOTE]
>
> Check `GET /app/info`: `results.scheduled_sends` is `true` when the server accepts schedule fields and exposes `/send/schedules`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.