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

# Messages

> Every message type, and when you are allowed to send it.

One endpoint sends everything: [`POST /v1/messages`](https://docs.lazyapp.in/api-reference/messages/send-a-message). The `type` field decides the shape of the rest of the body. It defaults to `text`.

## The 24-hour window

This governs everything else on this page.

> **Window open**
>
> The contact messaged you within the last 24 hours. Send anything — free-form text,
> media, interactive.

> **Window closed**
>
> Only an approved [template](https://docs.lazyapp.in/sending/templates.md) will go out. Free-form sends are
> refused.

LazyApp enforces this server-side. A free-form send outside the window fails with a clear error instead of quietly disappearing.

Replies inside the window are service messages. They are not unlimited and free: from 1 October 2026 Meta gives each number 1,000 free service messages a month and charges for the rest, billed to your own Meta payment method. See [Service messages from 1 October 2026](https://docs.lazyapp.in/billing/meta-message-billing.md#service-messages-from-1-october-2026).

## Addressing a recipient

By phone number, in E.164:

```json theme={null}
{ "to": "+919812345670" }
```

Or by business-scoped user id, when Meta gave you one instead of a number:

```json theme={null}
{ "recipient": { "type": "bsuid", "value": "US.13491208655302741918" } }
```

Or to a WhatsApp group you run, with `"type": "group_id"` (see [Group chats](https://docs.lazyapp.in/guides/groups.md)). Contacts who adopt a WhatsApp username may only have a BSUID; see [Identity, privacy and data](https://docs.lazyapp.in/guides/identity-and-privacy.md).

## Types

**Text**

```json theme={null}
{
  "type": "text",
  "to": "+919812345670",
  "body": "Your order is packed and leaves tonight."
}
```

Up to 4096 characters.

**Template**

```json theme={null}
{
  "type": "template",
  "to": "+919812345670",
  "template": {
    "name": "order_shipped",
    "language": "en",
    "category": "utility",
    "components": [
      {
        "type": "body",
        "parameters": [
          { "type": "text", "text": "1042" },
          { "type": "text", "text": "Thursday" }
        ]
      }
    ]
  }
}
```

The only type that works outside the 24-hour window. `category` affects pricing, so
set it accurately.

**Media**

```json theme={null}
{
  "type": "image",
  "to": "+919812345670",
  "media_id": "1234567890",
  "caption": "Your receipt"
}
```

`type` is `image`, `document`, `audio`, `video` or `sticker`. Reference bytes three ways:

| Field | Use when |
| - | - |
| `media_id` | You already [uploaded it](https://docs.lazyapp.in/sending/media.md) |
| `media_url` | LazyApp should fetch a public URL and upload it for you |

`filename` is worth setting for documents — it is what the recipient sees.

For a **voice note**, send `type: audio` with `"voice": true` (and no caption — Meta rejects captions on audio).

**Location**

```json theme={null}
{
  "type": "location",
  "to": "+919812345670",
  "latitude": 19.076,
  "longitude": 72.8777,
  "name": "Acme Retail, Bandra",
  "address": "Linking Road, Mumbai 400050"
}
```

**Contacts**

```json theme={null}
{
  "type": "contacts",
  "to": "+919812345670",
  "contacts": [
    {
      "name": { "formatted_name": "Asha Rao", "first_name": "Asha" },
      "phones": [{ "phone": "+919812345671", "type": "WORK" }]
    }
  ]
}
```

Between 1 and 5 contact cards.

**Interactive**

```json theme={null}
{
  "type": "interactive",
  "to": "+919812345670",
  "interactive": {
    "type": "button",
    "body": { "text": "Reschedule your delivery?" },
    "action": {
      "buttons": [
        { "type": "reply", "reply": { "id": "tomorrow", "title": "Tomorrow" } },
        { "type": "reply", "reply": { "id": "weekend", "title": "This weekend" } }
      ]
    }
  }
}
```

Buttons, lists, link buttons, location requests, media carousels, India address forms,
flows, `contact_information_request`, and [catalog / product](https://docs.lazyapp.in/sending/catalog.md) messages all live under
`interactive`. See [Interactive messages](https://docs.lazyapp.in/sending/interactive-messages.md) for every type and its limits. The contact's choice arrives as a `message.received` [webhook](https://docs.lazyapp.in/webhooks/events.md).
Cart checkouts arrive as `type: order` with `content.order`.

**Reaction**

```json theme={null}
{
  "type": "reaction",
  "to": "+919812345670",
  "reaction": {
    "message_id": "wamid.HBgMOTE5ODEyMzQ1Njcw",
    "emoji": "👍"
  }
}
```

Send an empty `emoji` to remove a reaction.

## Replying to a specific message

Quote the message you are answering, so it threads in the recipient's client:

```json theme={null}
{
  "type": "text",
  "to": "+919812345670",
  "body": "Yes, that size is in stock.",
  "context": { "message_id": "wamid.HBgMOTE5ODEyMzQ1Njcw" }
}
```

## Service window

Free-form messages only land within 24 hours of the contact's last inbound message. Outside that window, send a [template](https://docs.lazyapp.in/sending/templates.md).

`GET /v1/contacts/{contact}` includes `service_window` so you can choose the right message type before sending:

```json theme={null}
{
  "uuid": "1a2b3c4d-...",
  "phone_number": "+919812345670",
  "service_window": {
    "open": true,
    "state": "expiring",
    "expires_at": "2026-08-20T14:30:00+05:30",
    "last_inbound_at": "2026-08-19T14:30:00+05:30"
  }
}
```

`state` is `open`, `expiring` (less than two hours left) or `closed`. If you send anyway and the window is closed, the refusal carries `code: customer_service_window_closed`. See [Errors](https://docs.lazyapp.in/concepts/errors.md).

## Scheduling

Add `send_at` and the response becomes `202` with a scheduled id instead of a sent message:

```json theme={null}
{
  "type": "template",
  "to": "+919812345670",
  "template": { "name": "appointment_reminder", "language": "en" },
  "send_at": "2026-08-20T09:00:00+05:30",
  "ttl_seconds": 3600
}
```

`ttl_seconds` drops the message if it could not be sent within that window — better than a reminder arriving six hours late. List with [`GET /v1/messages/scheduled`](https://docs.lazyapp.in/api-reference/messages/list-scheduled-messages) and cancel with `DELETE`.

### How `send_at` is interpreted

There is no separate `timezone` field. The instant is taken from the string you send:

| You send | Interpreted as |
| - | - |
| `"2026-08-20T03:30:00Z"` | Absolute UTC |
| `"2026-08-20T09:00:00+05:30"` | Absolute instant with that offset |
| `"2026-08-20T09:00:00"` (no zone) | Wall clock in the API's timezone, which is **UTC** |

These three schedule the same instant (09:00 India Standard Time):

```json theme={null}
{ "send_at": "2026-08-20T03:30:00Z" }
{ "send_at": "2026-08-20T09:00:00+05:30" }
{ "send_at": "2026-08-20T03:30:00" }
```

The classic bug is a naive local time with no offset — it is treated as UTC, so the message goes out hours early or late for Indian and other non-UTC businesses. Prefer an explicit offset or `Z`.

Responses return `send_at` as ISO 8601. Convert to the viewer's timezone at display time; do not assume it comes back in the offset you sent.

LazyApp does not offer profile "queues" that drip-feed posts into recurring slots. For a batch of reminders, compute each `send_at` yourself (or stagger in your worker) and create one scheduled message per send.

Statuses and webhooks for scheduled vs sent messages are covered in [Message lifecycle](https://docs.lazyapp.in/concepts/message-lifecycle.md).

## Confirming a send

Fetch any message by uuid with [`GET /v1/messages/{uuid}`](https://docs.lazyapp.in/api-reference/messages/retrieve-a-message). Prefer [webhooks](https://docs.lazyapp.in/webhooks/overview.md) in production; the GET is for confirmation and debugging.

## Metadata

Attach up to 20 string values. They come back on webhooks, which makes reconciliation easy without a lookup table:

```json theme={null}
{ "metadata": { "order_id": "1042", "warehouse": "BOM-1" } }
```

## Direct send

Numbers enrolled in Meta's direct-send programme can send `utility` and `authentication` content without a pre-approved template, via [`POST /v1/messages/direct-send`](https://docs.lazyapp.in/api-reference/messages/send-without-an-approved-template). Marketing is not eligible. Check [capabilities](https://docs.lazyapp.in/concepts/capabilities.md) before relying on it. See [Direct Send](https://docs.lazyapp.in/sending/direct-send.md) for buttons, media headers and business-named templates.

## Ordering

Messages to the same recipient are sent in the order you call the API. Across different recipients there is no ordering guarantee, and none is needed.


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