> ## 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.

# Grant loyalty rewards from your systems

> Use the Loyalty API to enrol residents and award points 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 programmes in the Dashboard (optionally with `automatic_grant: { enabled: true }`) or via the API. For **Custom**, **One time**, and **Referral**, operators can [grant with a workflow](/loyalty/grant-with-a-workflow), including an **API request** trigger. For custom occasions in a CRM, access-control system, or PMS where you already call Yorlet from a server, POST program events instead. Residents redeem in OneMove. See [Loyalty](/loyalty) for the product overview.

## Prerequisites

Before you grant rewards, ensure you have:

* Loyalty enabled on the account.
* A [secret key](/development/api-keys), or a **restricted key** for a third party (recommended).

<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

| Permission                                                     | Grants                                                                                |
| -------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| `loyalty.programs.read` / `loyalty.programs.write`             | List or create programmes                                                             |
| `loyalty.program_events.read` / `loyalty.program_events.write` | List or grant points                                                                  |
| `loyalty.program_event_adjustments.write`                      | Cancel a grant                                                                        |
| `loyalty.residents.read` / `loyalty.residents.write`           | Enrol or update residents                                                             |
| `loyalty.tiers.read` / `loyalty.tiers.write`                   | Create or assign tiers                                                                |
| `loyalty.messages.read` / `loyalty.messages.write`             | [Send push notifications and emails](/development/integrations/loyalty/send-messages) |

A vendor that only awards points typically needs `loyalty.programs.read` and `loyalty.program_events.write`.

## 1. Look up or create a programme

Create a programme with the <a href="/api/loyalty/loyalty-programs/create" target="_blank">Loyalty Programs API<APIBadge /></a>, or look one up from the Dashboard.

Include `automatic_grant` for built-in types. Set `{ "enabled": true }` — and `after_months` when `type` is `tenure_milestone`. Prefer `type: "custom"` when the trigger lives outside Yorlet. `earn_rate` is allowed when `type` is `rent_on_time` or `custom`.

If you are issuing OneMove points from your own product (not a Yorlet tenancy), follow [Issue OneMove points](/development/integrations/onemove) instead — grant to an email, and skip residents.

```bash Create a custom programme theme={"theme":"dracula"}
curl https://api.yorlet.com/v1/loyalty/programs \
  -H "Authorization: Bearer {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
        "name": "Access control visit",
        "description": "Points when a resident checks in at the gym.",
        "statement_descriptor": "Gym visit",
        "type": "custom",
        "reward": { "type": "points", "points": 50 }
      }'
```

```json Response theme={"theme":"dracula"}
{
  "id": "lp_a1B2c3D4e5F6g7H8",
  "object": "loyalty.program",
  "automatic_grant": null,
  "name": "Access control visit",
  "type": "custom",
  "reward": { "type": "points", "points": 50 },
  "status": "active",
  "archived": false
}
```

To turn on a built-in grant, send `automatic_grant` instead of posting program events for that occasion:

```bash Enable automatic grants theme={"theme":"dracula"}
curl https://api.yorlet.com/v1/loyalty/programs \
  -H "Authorization: Bearer {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
        "name": "Rent on time",
        "description": "Points when rent is paid on time.",
        "statement_descriptor": "On time rent",
        "type": "rent_on_time",
        "automatic_grant": { "enabled": true },
        "reward": { "type": "points", "points": 100 }
      }'
```

For `tenure_milestone`, include `after_months`:

```json theme={"theme":"dracula"}
{ "enabled": true, "after_months": 12 }
```

## 2. Provision access

Create a resident with the <a href="/api/loyalty/loyalty-residents/create" target="_blank">Residents API<APIBadge /></a> when you need to enrol someone who is not already synced from an active tenancy.

```bash Create a resident theme={"theme":"dracula"}
curl https://api.yorlet.com/v1/loyalty/residents \
  -H "Authorization: Bearer {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
        "email": "jane@example.com",
        "name": "Jane Doe",
        "building": "bui_lrhplff1u4ZhIPSU"
      }'
```

Manual residents stay `pending` until the OneMove user confirms they live at the building.

## 3. Grant rewards

Create a program event with the <a href="/api/loyalty/loyalty-program-events/create" target="_blank">Loyalty Program Events API<APIBadge /></a>. The recipient is `customer`, `email`, or `resident`.

```bash Grant points theme={"theme":"dracula"}
curl https://api.yorlet.com/v1/loyalty/program_events \
  -H "Authorization: Bearer {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: gym-visit-cus_123-2026-03-15" \
  -d '{
        "loyalty_program": "lp_a1B2c3D4e5F6g7H8",
        "recipient": { "type": "customer", "customer": "cus_123" }
      }'
```

Optional fields:

* `loyalty_program_override.points` — override the programme's default points.
* `earn_rate.spend_amount` — required for earn-rate programmes, in pence.
* `identifier` — your order id or similar. The same identifier on the same programme returns the original event.
* `Idempotency-Key` — send the same key to avoid duplicate grants.

Event `status` is `succeeded` when Yorlet matched a OneMove user, `pending` when there is no OneMove user yet, or `expired`.

If the programme has **Grant automatically** for a Yorlet occasion, do not also POST a program event for that occasion.

## 4. Cancel a grant

Cancel a succeeded grant with the <a href="/api/loyalty/loyalty-program-event-adjustments/create" target="_blank">Loyalty Program Event Adjustments API<APIBadge /></a>.

```bash Cancel a grant theme={"theme":"dracula"}
curl https://api.yorlet.com/v1/loyalty/program_event_adjustments \
  -H "Authorization: Bearer {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
        "loyalty_program_event": "lpe_9xY8wV7uT6sR5qP4",
        "type": "cancel"
      }'
```

## Notes for integrators

* Call from your backend after the external event succeeds, not from the vendor's frontend.
* Points are not spendable for 5 business days. You are billed when they are redeemed, not when they are granted.
* Do not combine automatic programmes with a workflow that grants on the same event.
* Operators who do not want to call this API can [grant with a workflow](/loyalty/grant-with-a-workflow) instead.
