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

# Issue OneMove points from your product

> Create OneMove users by granting points to an email. If they do not have an account yet, OneMove invites them to sign up.

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>;
};

If you run your own product — a shop, PMS, or app — you can issue OneMove points when someone buys from you. You do not create OneMove accounts. Grant to an email; if they already have OneMove, the points post immediately, and if they do not, OneMove emails them to sign up and claim the grant.

You need a Yorlet account with Loyalty enabled. Points are billed when they are redeemed, not when they are granted. See [Loyalty](/loyalty) for the product overview.

Do not create [residents](/loyalty/residents) for this flow. Residents are for people who live in your buildings.

## Prerequisites

Before you issue points, ensure you have:

* Loyalty enabled on the account.
* A [secret key](/development/api-keys), or a **restricted key** (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

A vendor that only awards points typically needs:

| Permission                     | Grants                                               |
| ------------------------------ | ---------------------------------------------------- |
| `loyalty.programs.read`        | Look up the programme you grant against              |
| `loyalty.programs.write`       | Create the programme if you do not use the Dashboard |
| `loyalty.program_events.write` | Grant points                                         |

## 1. Create a programme

Create a **Custom** programme with the <a href="/api/loyalty/loyalty-programs/create" target="_blank">Loyalty Programs API<APIBadge /></a>, or from the Dashboard. Set `recipient_restriction` to `none` so anyone in OneMove can receive the points.

Use a fixed `points` reward when every purchase earns the same amount. Use `earn_rate` when points should scale with spend — `currency_unit` is in pence, so `100` with `points_per_unit` of `1` is 1 point per £1.

```bash Create a purchase programme theme={"theme":"dracula"}
curl https://api.yorlet.com/v1/loyalty/programs \
  -H "Authorization: Bearer {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
        "name": "Shop purchases",
        "description": "Points when you buy from us.",
        "statement_descriptor": "Shop purchase",
        "type": "custom",
        "recipient_restriction": "none",
        "reward": {
          "type": "earn_rate",
          "currency_unit": 100,
          "points_per_unit": 1
        }
      }'
```

You can also set **Earn rate** on a **Custom** programme in the Dashboard.

## 2. Grant points after a purchase

Create a program event with the <a href="/api/loyalty/loyalty-program-events/create" target="_blank">Loyalty Program Events API<APIBadge /></a>. Use `recipient.type: "email"` and the shopper's email. You do not need a Yorlet customer.

Send `identifier` as your order id so a retry returns the original grant instead of creating a duplicate. Also send `Idempotency-Key` on the HTTP request. `identifier` is durable; the idempotency key is for short retries.

For an earn-rate programme, send `earn_rate.spend_amount` in pence.

```bash Grant points for a purchase 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: order_123" \
  -d '{
        "loyalty_program": "lp_a1B2c3D4e5F6g7H8",
        "identifier": "order_123",
        "earn_rate": { "spend_amount": 2500 },
        "recipient": { "type": "email", "email": "jane@example.com" }
      }'
```

Event `status` is:

* `succeeded` — there is already a OneMove account for that email. Points post to the wallet.
* `pending` — there is no OneMove account yet. OneMove emails them to sign up. When they complete OTP, the grant is claimed automatically.
* `expired` — the grant expired before it was claimed.

Call this from your backend after the purchase succeeds, not from the browser.

## 3. What the shopper does next

If they do not have OneMove, they receive an email with a link to sign in (`/login` with their email pre-filled). After they verify, pending grants for that email become spendable — after **5 business days**, like any other grant.

They redeem in OneMove as cash or an offer. Rent credit and rent discount need a Yorlet tenancy, so shoppers who only buy from you will not see those options.

You are billed when points are redeemed, not when they are granted.

## Notes

* Omit `identifier` if you do not need durable de-dupe. Two grants with no identifier are two grants.
* The same `identifier` on the same programme always returns the first event.
* To cancel a succeeded grant, use [program event adjustments](/development/integrations/loyalty/grant-rewards#4-cancel-a-grant).
* Yorlet operators who enrol residents and grant from a PMS should follow [Grant rewards](/development/integrations/loyalty/grant-rewards) instead.
