Loyalty

SmileRewards owns the loyalty points ledger and all money math. Customer 360 owns the identity: which unified customer a SmileRewards memberId belongs to. This makes Customer 360 the single dedup authority, so a member is never double-created. These endpoints take the loyalty scope.


What Customer 360 owns

A customer's SmileRewards memberId is stored as an identity row on the customer. A uniqueness constraint guarantees one customer per member id, and the service enforces one member per customer, merge-safe: a late merge repoints the member onto the survivor.

The bridge flow is: before creating a member on SmileRewards, ask Customer 360 whether the merge-resolved customer already has one. If not, create it on SmileRewards, then record the id back here. Because the record call is idempotent and the dedup authority, a member is never double-created even though SmileRewards does not enforce uniqueness itself.


POST/v1/loyalty/members/resolve

Resolve or record a member

POST /v1/loyalty/members/resolve resolves a customer's member id, and records one if memberId is supplied. It is idempotent and merge-safe.

Identify the customer by customerId (a customer public id from the feed) or zbIdSubject; at least one is required, else invalid_selector (422).

  • Resolve only (omit memberId): the response says whether a member must be created via needsMember.
  • Record (include memberId): attach the id the bridge just created on SmileRewards.

On a record call, the response tells you which happened:

  • Name
    created: true
    Description

    The id was newly attached to this customer.

  • Name
    created: false, alreadyMember: true
    Description

    The same id was already attached.

  • Name
    created: false, conflict: customer_has_other
    Description

    The customer already had a DIFFERENT member id (returned in memberId, with attemptedMemberId). Orphan the fresh SmileRewards member.

A memberId already mapped to a different customer returns 409 member_taken with detail.memberId and detail.ownerCustomerPublicId.

Resolve only

POST
/v1/loyalty/members/resolve
curl -X POST https://c360-api.lioncapventures.com/v1/loyalty/members/resolve \
  -H "X-API-Key: $C360_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "customerId": "cus_a1b2c3" }'

Resolve only (needs a member)

{
  "success": true,
  "data": {
    "customerPublicId": "cus_a1b2c3",
    "zbIdSubject": "b1e5c8a0-...",
    "platform": "smilerewards",
    "memberId": null,
    "hasMember": false,
    "created": false,
    "linkedAt": null,
    "needsMember": true
  }
}

Record a member

POST
/v1/loyalty/members/resolve
curl -X POST https://c360-api.lioncapventures.com/v1/loyalty/members/resolve \
  -H "X-API-Key: $C360_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "customerId": "cus_a1b2c3", "memberId": "SR-90210" }'

Record (created)

{
  "success": true,
  "data": {
    "customerPublicId": "cus_a1b2c3",
    "zbIdSubject": "b1e5c8a0-...",
    "platform": "smilerewards",
    "memberId": "SR-90210",
    "hasMember": true,
    "created": true,
    "linkedAt": "2026-08-10T09:20:00.000Z",
    "needsMember": false
  }
}

member_taken (409)

{
  "success": false,
  "error": {
    "code": "member_taken",
    "message": "That memberId is already mapped to a different customer.",
    "detail": {
      "memberId": "SR-90210",
      "ownerCustomerPublicId": "cus_zzz999"
    }
  }
}

GET/v1/loyalty/members

Read the current mapping

GET /v1/loyalty/members returns the current SmileRewards member mapping for a customer, read-only. Query by customerId or zbIdSubject. It returns hasMember: false when the customer is unenrolled, and duplicateMemberIds (a reconciliation flag) if a merge ever left more than one member on the customer.

Request

GET
/v1/loyalty/members
curl "https://c360-api.lioncapventures.com/v1/loyalty/members?customerId=cus_a1b2c3" \
  -H "X-API-Key: $C360_API_KEY"

Response (200)

{
  "success": true,
  "data": {
    "customerPublicId": "cus_a1b2c3",
    "zbIdSubject": "b1e5c8a0-...",
    "platform": "smilerewards",
    "memberId": "SR-90210",
    "hasMember": true,
    "created": false,
    "linkedAt": "2026-08-10T09:20:00.000Z",
    "needsMember": false
  }
}

Was this page helpful?