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

# WordPress / WooCommerce

> Connect a WordPress + WooCommerce store to LoyaltyDog.

The LoyaltyDog WordPress plugin syncs customers and orders from your WooCommerce store into your LoyaltyDog loyalty program in real time, and renders a loyalty UI in the customer's **My Account** area.

## What it does

* Creates a LoyaltyDog customer record the first time a WooCommerce customer places an order.
* Awards loyalty points on order completion, based on rules you configure in your program.
* Lets customers redeem points for discount codes at checkout.
* Surfaces the customer's points balance and the awards they can claim on the **My Account → Loyalty Program** page.

## Install

1. Sign in to your WordPress admin.
2. Install the **LoyaltyDog** plugin from the WordPress plugin directory (or upload the ZIP).
3. Activate the plugin, then open **LoyaltyDog → Settings**.
4. Paste the single-use **activation key** from your onboarding email — the plugin exchanges it for your program credentials automatically.

## Plugin-facing UI endpoints

The plugin calls the `/wordpress/ui/*` endpoints to render the customer's loyalty UI. These authenticate with the plugin's API token (a `loyaltydog_pk_*` registration key **or** the legacy WordPress webhook token) sent as a bearer token. `GET` requests identify the customer with `?email=` or `?customer_id=`.

### Listing a customer's eligible awards

`GET /v2/wordpress/ui/eligible-awards` returns the loyalty awards a customer can claim right now, and backs the **My Account → Loyalty Program** page so it can list redeemable awards without re-deriving eligibility client-side. Identify the customer with either `?email=` or `?customer_id=`.

Only **LIVE** offers the customer can afford are returned — offers costing more points than the customer's balance (and offers blocked by `pointsMin`/`pointsMax`, per-customer or availability limits, or an offer the customer has already claimed) are excluded.

```bash theme={null}
curl "https://api.loyalty.dog/v2/wordpress/ui/eligible-awards?email=ada@example.com" \
  -H "Authorization: Bearer loyaltydog_pk_live_..."
```

A successful call returns `200 OK` with a JSON array of awards. Each award carries an `id` (pass it as `offer_id` when claiming), a display `name`, and its points cost — exposed both as `pointsCost` and as the snake\_case alias `points_required` for the plugin UI. Offers may also include the balance bounds (`pointsMin`/`pointsMax`), a per-customer claim limit (`limitPerCustomer`), and an `endDate`, each `null` when unset.

```json theme={null}
[
  {
    "id": "prog_offer_123",
    "name": "Free Coffee",
    "points_required": 100,
    "pointsCost": 100,
    "pointsMin": 0,
    "pointsMax": null,
    "limitPerCustomer": null,
    "endDate": null
  }
]
```

The endpoint responds with `401` for a missing or invalid bearer token, `400` when neither `email` nor `customer_id` is supplied, and `404` when no customer matches the supplied identity in the program.

### Requesting a fresh activation code

`POST /v2/wordpress/resend-activation` is **public and unauthenticated** — unlike the `/ui/*` endpoints above, it's called before the plugin has any credentials. A merchant whose activation link expired or was lost can use it to have a fresh activation code emailed to them.

The endpoint is enumeration-safe: it always returns the same neutral `200` whether or not the email address is registered to a program, so it can't be used to probe which emails have LoyaltyDog accounts. Any failure looking up the account, minting a new code, or sending the email is swallowed server-side and reported to Sentry rather than surfaced to the caller.

```bash theme={null}
curl -X POST "https://api.loyalty.dog/v2/wordpress/resend-activation" \
  -H "Content-Type: application/json" \
  -d '{"email": "merchant@example.com"}'
```

A successful call always returns `200 OK` with the same static body:

```json theme={null}
{
  "message": "If an account exists for that email, we've sent a new activation code."
}
```

The endpoint is rate-limited in layers, none of which change the response the caller sees:

* **Per-IP** — 10 requests per hour, enforced before anything else runs.
* **Per-email** — 5 requests per hour per address (tracked by a hashed key, not the raw email).
* **Per-program** — the existing 60-second throttle on the underlying resend machinery.

A request that trips any of these limits still gets the same neutral `200` — never a `429` — so a spent budget is indistinguishable from a successful send.

## Troubleshooting

* **Orders aren't awarding points** — Confirm the plugin activated successfully (Settings shows a linked program) and that order-completion webhooks are reaching LoyaltyDog.
* **My Account page shows no awards** — The customer may not have enough points for any LIVE offer, or no offers are LIVE for the program. The `GET /v2/wordpress/ui/eligible-awards` response is empty in both cases. Check the program's offers in your dashboard.
* **Customer not created** — Ensure the WooCommerce customer has an email address; LoyaltyDog dedupes on email.
* **Merchant never received a new activation code** — `POST /v2/wordpress/resend-activation` always returns `200`, even when nothing was sent (unknown email, or the per-email/per-program rate limit was already spent). Confirm the merchant's email matches the one on their LoyaltyDog account, and check spam/junk folders before assuming the request failed.

<Tip>
  Awards listed by `GET /v2/wordpress/ui/eligible-awards` are claimed with `POST /v2/wordpress/ui/claim`, passing the award's `id` as `offer_id`.
</Tip>
