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

# Send loyalty messages from your systems

> Use the Loyalty Messages API to send OneMove push notifications and emails from your own software.

export const APIBadge = () => {
  return <div style={{
    display: 'inline-block',
    backgroundColor: 'rgb(var(--primary-light))',
    color: '#ffffff',
    padding: '1px 4px',
    borderRadius: '.375rem',
    borderColor: 'rgb(var(--gray-200))',
    borderWidth: '1px',
    position: 'relative',
    top: '-2px',
    marginLeft: '4px',
    fontSize: '11px',
    fontWeight: 700,
    letterSpacing: '0.05em',
    lineHeight: '11px'
  }}>
      API
    </div>;
};

Create a message with the <a href="/api/loyalty/loyalty-messages/create" target="_blank">Loyalty Messages API<APIBadge /></a> when you want to notify residents from a CRM, PMS, or other backend. Operators can also [send from the Dashboard](/loyalty/messages). Residents receive a OneMove push notification, a branded email, or both.

## Prerequisites

Before you send messages, ensure you have:

* Loyalty enabled on the account.
* A [secret key](/development/api-keys), or a **restricted key** with message permissions.

<Warning>
  Your secret key can perform any action on your account, so only ever use it from your own server. Never put it in a browser, a mobile app, or a vendor's client. If a secret key is ever exposed, [roll it](/development/api-keys) immediately.
</Warning>

## Permissions

* `loyalty.messages.write` — create and send messages
* `loyalty.messages.read` — retrieve and list messages

A vendor that only sends messages typically needs `loyalty.messages.write`. Listing or polling a send needs `loyalty.messages.read`.

## 1. Create a message

POST a message with a `recipient`, `title`, and `body`. `channels` defaults to `["push"]` when you omit it.

`channels` and `email_delivery` were added in API version `2026-09-15`. Pin that version, or send `Yorlet-Version: 2026-09-15`, to set channels and read email delivery. See [API versioning](/development/versioning).

```bash Send a building broadcast theme={"theme":"dracula"}
curl https://api.yorlet.com/v1/loyalty/messages \
  -H "Authorization: Bearer {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -H "Yorlet-Version: 2026-09-15" \
  -d '{
        "title": "Lift maintenance this week",
        "body": "The lift will be out of service on Thursday between 9am and 5pm.",
        "category": "announcement",
        "channels": ["push", "email"],
        "recipient": { "type": "building", "building": "bui_lrhplff1u4ZhIPSU" }
      }'
```

```json Response theme={"theme":"dracula"}
{
  "id": "lmsg_a1B2c3D4e5F6g7H8",
  "object": "loyalty.message",
  "title": "Lift maintenance this week",
  "body": "The lift will be out of service on Thursday between 9am and 5pm.",
  "category": "announcement",
  "channels": ["push", "email"],
  "recipient": { "type": "building", "building": "bui_lrhplff1u4ZhIPSU" },
  "status": "pending",
  "delivery": { "attempted": 0, "delivered": 0, "failed": 0, "read": 0, "skipped": 0 },
  "email_delivery": { "attempted": 0, "delivered": 0, "failed": 0, "skipped": 0 },
  "failure_code": null,
  "sent_at": null
}
```

Create returns `status: "pending"`. Delivery runs in the background.

To write to one customer instead:

```bash Send to a customer theme={"theme":"dracula"}
curl https://api.yorlet.com/v1/loyalty/messages \
  -H "Authorization: Bearer {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -H "Yorlet-Version: 2026-09-15" \
  -d '{
        "title": "Your points are ready",
        "body": "This month's reward is waiting in OneMove.",
        "channels": ["email"],
        "recipient": { "type": "customer", "customer": "cus_123" }
      }'
```

The customer must have an active tenancy on the account.

### Parameters

* `title` — shown to the recipient. Maximum 100 characters.
* `body` — shown to the recipient. Maximum 500 characters.
* `recipient` — `type: "building"` with `building`, or `type: "customer"` with `customer`.
* `channels` — `push`, `email`, or both. Defaults to `["push"]`.
* `category` — optional `announcement`, `event`, `offer`, or `alert`. Groups the message on the building noticeboard in OneMove.

## 2. Who is notified

Building broadcasts:

* **Push** goes to active residents who use OneMove and have confirmed they live at the building.
* **Email** goes to every active resident with an email address, including residents who do not use OneMove.

Customer messages:

* **Push** requires the customer to use OneMove.
* **Email** uses the customer email. They do not need to use OneMove.

Email-only sends do not appear in the OneMove inbox. Building broadcasts appear on the noticeboard once they are `sent`.

## 3. Poll until it is sent

Retrieve the message with the <a href="/api/loyalty/loyalty-messages/retrieve" target="_blank">Retrieve API<APIBadge /></a> until `status` is `sent` or `failed`.

```bash Retrieve a message theme={"theme":"dracula"}
curl https://api.yorlet.com/v1/loyalty/messages/lmsg_a1B2c3D4e5F6g7H8 \
  -H "Authorization: Bearer {{API_KEY}}" \
  -H "Yorlet-Version: 2026-09-15"
```

* `delivery` summarises OneMove push attempts.
* `email_delivery` summarises email attempts when `email` was requested, and is `null` otherwise.
* `failure_code` is set when the message failed: `no_onemove_user`, `no_device_tokens`, `no_email`, or `send_error`.
* `sent_at` is set when at least one push or email was delivered.

`status` is `sent` when either channel delivers, and `failed` only when neither does.

You can also [list messages](/api/loyalty/loyalty-messages/list).

## Notes for integrators

* Call from your backend after the occasion you are announcing, not from a vendor's frontend.
* Each successful push or email is billed at £0.02. Failed and skipped deliveries are not charged. See [list pricing](https://www.yorlet.com/pricing#loyalty).
* Operators who do not want to call this API can [send from the Dashboard](/loyalty/messages) instead.
