> ## Documentation Index
> Fetch the complete documentation index at: https://developer.zapsterapi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# How to schedule your first message

> Step-by-step tutorial to send a scheduled WhatsApp message through the Zapster API using the send_at field, then check its status and cancel it if needed.

In this guide you will schedule a WhatsApp message to be sent at a future date. It takes less than 5 minutes.

## What you will need

* A Zapster API account (if you do not have one, [create it here](https://app.zapsterapi.com/signup))
* A connected instance (WABA or unofficial)
* Your authentication token

## 1. Schedule a message

The `send_at` field is what turns a regular send into a schedule. Pass the date and time in the ISO 8601 format with a time zone.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.zapsterapi.com/v1/wa/messages \
    -H "Authorization: Bearer YOUR_TOKEN" \
    -H "X-Instance-ID: YOUR_INSTANCE" \
    -H "Content-Type: application/json" \
    -d '{
      "recipient": "5511999999999",
      "text": "Hi! Your invoice is due tomorrow.",
      "send_at": "2026-03-30T09:00:00-03:00"
    }'
  ```

  ```javascript JavaScript (fetch) theme={null}
  const response = await fetch('https://api.zapsterapi.com/v1/wa/messages', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_TOKEN',
      'X-Instance-ID': 'YOUR_INSTANCE',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      recipient: '5511999999999',
      text: 'Hi! Your invoice is due tomorrow.',
      send_at: '2026-03-30T09:00:00-03:00',
    }),
  });

  const data = await response.json();
  console.log(data);
  // { message_id: "msg_k7m2nxp9q4", status: "scheduled", send_at: "2026-03-30T12:00:00.000Z" }
  ```

  ```python Python (requests) theme={null}
  import requests

  response = requests.post(
      'https://api.zapsterapi.com/v1/wa/messages',
      headers={
          'Authorization': 'Bearer YOUR_TOKEN',
          'X-Instance-ID': 'YOUR_INSTANCE',
          'Content-Type': 'application/json',
      },
      json={
          'recipient': '5511999999999',
          'text': 'Hi! Your invoice is due tomorrow.',
          'send_at': '2026-03-30T09:00:00-03:00',
      },
  )

  print(response.json())
  # { "message_id": "msg_k7m2nxp9q4", "status": "scheduled", "send_at": "2026-03-30T12:00:00.000Z" }
  ```
</CodeGroup>

If the response comes back with status **201**, the message was scheduled. Keep the `message_id`, because it is what you use to cancel or check the status later. Notice that the `send_at` in the response comes in UTC, so `-03:00` becomes `+3 hours` in the returned time.

## 2. Check the status

To confirm the message is still in the queue, use the list endpoint with date and status filters.

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api.zapsterapi.com/v1/wa/messages?from=2026-03-30T00:00:00Z&to=2026-03-30T23:59:59Z&status=scheduled" \
    -H "Authorization: Bearer YOUR_TOKEN"
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    'https://api.zapsterapi.com/v1/wa/messages?from=2026-03-30T00:00:00Z&to=2026-03-30T23:59:59Z&status=scheduled',
    { headers: { 'Authorization': 'Bearer YOUR_TOKEN' } }
  );

  const { data } = await response.json();
  console.log(data);
  ```
</CodeGroup>

You will see the message with the `scheduled` status. Once the scheduled time passes, the status changes to `sent` (or `failed` if something went wrong during the send).

## 3. Cancel if you need to

Changed your mind? Cancel with a DELETE, passing the `message_id`.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X DELETE https://api.zapsterapi.com/v1/wa/messages/msg_k7m2nxp9q4 \
    -H "Authorization: Bearer YOUR_TOKEN"
  ```

  ```javascript JavaScript theme={null}
  await fetch('https://api.zapsterapi.com/v1/wa/messages/msg_k7m2nxp9q4', {
    method: 'DELETE',
    headers: { 'Authorization': 'Bearer YOUR_TOKEN' },
  });
  // { id: "msg_k7m2nxp9q4", status: "canceled" }
  ```
</CodeGroup>

You can only cancel messages that have not been sent yet. If the message already went out, the DELETE returns error **422**.

## Frequently asked questions

<AccordionGroup>
  <Accordion title="Can I schedule for 1 minute from now?">
    Yes. The minimum is 1 minute in the future. If you pass a date that is already in the past or less than 1 minute away, the API returns a validation error.
  </Accordion>

  <Accordion title="What happens if the instance is offline at send time?">
    The system tries to send 3 times with a growing interval between attempts. If all of them fail, the message goes to the `failed` status with the error reason in the `errors` field.

    When the instance comes back, it does not automatically resend messages that already failed. You would need to schedule them again.
  </Accordion>

  <Accordion title="Can I edit a message that is already scheduled?">
    No. If you need to change the text, the recipient, or the time, cancel the existing message with `DELETE /v1/wa/messages/:id` and create a new one.
  </Accordion>

  <Accordion title="Why did my scheduled message fail?">
    Check the history with `GET /v1/wa/messages` and filter by `status=failed`. The `errors` field in the response carries the code and description of the problem.

    Common reasons:

    * The instance was disconnected at send time
    * The WABA 24-hour window expired (error 131047)
    * The template was not found (error 132000)
    * The recipient's number does not exist or is blocked
  </Accordion>

  <Accordion title="What is the scheduled message limit?">
    It depends on your plan:

    | Plan       | Concurrent scheduled | Maximum lead time |
    | ---------- | -------------------- | ----------------- |
    | Essential  | 10                   | 7 days            |
    | Pro        | 500                  | 1 year            |
    | Enterprise | 10,000               | No limit          |

    If you reach the limit, the API returns the `max_scheduled_messages_reached` error. You can cancel pending messages to free up space.
  </Accordion>

  <Accordion title="Why are delivered_at or read_at empty?">
    These fields depend on the recipient's privacy settings on WhatsApp. If the person turned off read receipts, `read_at` will never be filled in. If there was a network issue, `delivered_at` may take a while to appear.

    The message status stays `sent` regardless of these fields.
  </Accordion>

  <Accordion title="Does it work with WABA and unofficial instances?">
    Yes, both types. You do not need to change anything in the configuration. The API automatically detects the instance type and sends the right way.
  </Accordion>
</AccordionGroup>

## Next steps

* [See all the filters available in the list endpoint](/pt-BR/v1/api-reference/messages/list-messages)
* [Understand the message lifecycle](/en/v1/concepts/message-lifecycle)
* [Limits and plans](/en/v1/concepts/scheduled-messages#limits-per-plan)
