User & Loyalty API Reference | Lynes Loyalty API

User & Loyalty API

User-focused endpoints let you manage loyalty points, reward history, offers, coupons, streaks, challenges, surveys, and notifications for individual users.

  • Authentication: All endpoints in this section require a valid JWT Bearer token.

See JWT Authentication for token structure and security best practices.

Endpoints Overview

  • POST /user – Create user
  • DELETE /user/{userId} – Delete user
  • GET /user/{userId}/coupon – Get user coupons
  • PUT /user/{userId}/coupon/{couponId} – Redeem coupon
  • GET /user/{userId}/points – Get user points
  • GET /user/{userId}/history – Get reward history
  • PUT /user/{userId}/notifications/overview – Mark overview as viewed
  • GET /user/{userId}/notifications/overview – Get notifications overview
  • PUT /user/{userId}/notifications/change – Reset notification counters
  • GET /user/{userId}/offer/feed – Get offer feed
  • POST /user/{userId}/offer/{offerId}/redeem – Redeem offer
  • GET /user/{userId}/offer/{offerId}/redeemable – Check if offer is redeemable
  • GET /user/{userId}/survey – Get available surveys
  • PUT /user/{userId}/survey/{surveyId} – Submit survey response
  • GET /user/{userId}/challenge – Get user challenges
  • GET /user/{userId}/challenge/{challengeId} – Get specific challenge
  • GET /user/{userId}/streak – Get user streaks
  • GET /user/{userId}/streak/{streakId} – Get specific streak

POST /user – Create User

Create a new user in the project and initialize their loyalty profile.

  • Method: POST
  • Path: /v1/user
  • Auth: Authorization: Bearer <JWT_TOKEN>

Request Body

{
  "userId": "user_123"
}
  • userId string (required): Unique user identifier in your system.

Responses

  • 201 Created

    {
      "message": "user created successfully",
      "userId": "user_123",
      "createdAt": 1730818799123
    }
  • 400 Bad Request

    • User with this userId already exists.
  • 500 Internal Server Error

    • Error while writing to the database.

DELETE /user/{userId} – Delete User

Delete a user and all associated loyalty data (events, coupons, etc.).

  • Method: DELETE
  • Path: /v1/user/{userId}
  • Auth: Authorization: Bearer <JWT_TOKEN>

Path Parameters

NameTypeRequiredDescription
userIdstringyesIdentifier of the user.

Responses

  • 200 OK

    {
      "message": "User data deletion process initiated successfully. Check logs for details.",
      "userId": "user_123"
    }

    The deletion is processed in batches in the background; see logs for any partial failures.

  • 400 Bad Request

    • userId missing.
  • 500 Internal Server Error

    • Error while deleting user data.

GET /user/{userId}/coupon – Get User Coupons

Return all active, unredeemed coupons for a user.

  • Method: GET
  • Path: /v1/user/{userId}/coupon
  • Auth: Authorization: Bearer <JWT_TOKEN>

Path Parameters

NameTypeRequiredDescription
userIdstringyesIdentifier of the user.

Response

  • 200 OK

    [
      {
        "PK": "USER#user_123#COUPON",
        "SK": "COUPON#abc123",
        "title": "10% Discount",
        "description": "Get 10% off your next order",
        "validUntil": 1762354799000,
        "redeemed": false,
        "metadata": {
          "shopId": "shop_1"
        }
      }
    ]

    Only coupons where redeemed === false and validUntil > now are returned.

  • 200 OK with []

    • User has no active coupons.
  • 400 Bad Request

    • userId missing.
  • 500 Internal Server Error

    • Error while querying coupons.

PUT /user/{userId}/coupon/{couponId} – Redeem Coupon

Mark a specific user coupon as redeemed.

  • Method: PUT
  • Path: /v1/user/{userId}/coupon/{couponId}
  • Auth: Authorization: Bearer <JWT_TOKEN>

Path Parameters

NameTypeRequiredDescription
userIdstringyesIdentifier of the user.
couponIdstringyesInternal coupon SK id.

Request Body

{
  "userId": "user_123",
  "SK": "COUPON#abc123"
}

The API currently expects the coupon SK in the body; the couponId in the path is used for validation/logical routing.

Response

  • 200 OK

    {
      "redeemed": true
    }
  • 400 Bad Request

    • userId missing.
  • 500 Internal Server Error

    • Error updating the coupon.

GET /user/{userId}/points – Get User Points

For a detailed conceptual guide, also see Get User Points.

Return the current loyalty point balance for a user.

  • Method: GET
  • Path: /v1/user/{userId}/points
  • Auth: Authorization: Bearer <JWT_TOKEN>

Path Parameters

NameTypeRequiredDescription
userIdstringyesIdentifier of the user.

Response

  • 200 OK (user exists)

    {
      "userId": "user_123",
      "points": 1200,
      "userExists": true
    }
  • 200 OK (user not found)

    {
      "userId": "user_123",
      "points": 0,
      "userExists": false
    }
  • 400 Bad Request

    • userId missing.
  • 500 Internal Server Error

    • Error while loading the loyalty item.

GET /user/{userId}/history – Get Reward History

Return the event-based reward history (events, redemptions, challenges, streak-related items) for a user.

  • Method: GET
  • Path: /v1/user/{userId}/history
  • Auth: Authorization: Bearer <JWT_TOKEN>

Path Parameters

NameTypeRequiredDescription
userIdstringyesIdentifier of the user.

Query Parameters

NameTypeRequiredDefaultDescription
sortstringnodescasc or desc by timestamp.
showAllbooleannofalseIf false, filter out entries with points === 0.

Response

  • 200 OK

    [
      {
        "id": "EVENT#purchase_completed#1730818799123",
        "timestamp": 1730818799123,
        "type": "EVENT",
        "points": 50,
        "image": "https://example.com/icons/purchase.png",
        "description": {
          "de": "Du hast deinen ersten Einkauf getätigt",
          "en": "You made your first purchase"
        },
        "title": {
          "de": "Event abgeschlossen",
          "en": "Event completed"
        },
        "userId": "user_123",
        "globalEventDateKey": "2025-11-05"
      }
    ]
  • 204 No Content

    • No history entries for the user.
  • 400 Bad Request

    • userId missing.
  • 500 Internal Server Error

    • Error while querying history.

PUT /user/{userId}/notifications/overview – Mark Overview as Viewed

Mark a notification screen as viewed by the user (affects unseen flags).

  • Method: PUT
  • Path: /v1/user/{userId}/notifications/overview
  • Auth: Authorization: Bearer <JWT_TOKEN>

Path Parameters

NameTypeRequiredDescription
userIdstringyesIdentifier of the user.

Request Body

{
  "screenId": "rewards"
}
  • screenId string (required): One of "rewards", "challenges", "surveys".

Responses

  • 200 OK

    {
      "message": "Screen viewed successfully"
    }
  • 400 Bad Request

    • screenId missing or invalid.
  • 500 Internal Server Error

    • Error updating the user profile.

GET /user/{userId}/notifications/overview – Get Notifications Overview

Return whether the user has unseen challenges, rewards, or surveys.

  • Method: GET
  • Path: /v1/user/{userId}/notifications/overview
  • Auth: Authorization: Bearer <JWT_TOKEN>

Response

  • 200 OK

    {
      "userId": "user_123",
      "hasUnseenChallenges": true,
      "hasUnseenRewards": false,
      "hasUnseenSurveys": true
    }

If the user profile does not exist yet, all flags are false.


PUT /user/{userId}/notifications/change – Reset Notification Counters

Reset which notification sections are considered “viewed” for a user.

  • Method: PUT
  • Path: /v1/user/{userId}/notifications/change
  • Auth: Authorization: Bearer <JWT_TOKEN>

Request Body

{
  "resetChallenges": true,
  "resetRewards": false,
  "resetSurveys": true
}
  • Each field is optional; when true, the corresponding last-viewed marker is cleared.

Response

  • 200 OK

    {
      "message": "User viewed screens reset successfully"
    }

GET /user/{userId}/offer/feed – Get Offer Feed

Return an offer feed for the user (e.g. local vs online offers).

  • Method: GET
  • Path: /v1/user/{userId}/offer/feed
  • Auth: Authorization: Bearer <JWT_TOKEN>

Query Parameters

NameTypeRequiredDescription
typestringnoOffer type, e.g. LOCAL or ONLINE

Response

  • 200 OK

    [
      {
        "offerId": "offer_123",
        "title": "Free Coffee",
        "description": "Get a free coffee with any purchase",
        "type": "LOCAL",
        "points": 100,
        "shopId": "shop_1"
      }
    ]

The exact shape matches your offer model; the feed is filtered by type if provided.


POST /user/{userId}/offer/{offerId}/redeem – Redeem Offer

Redeem an offer for a user (deducts points and logs an event).

  • Method: POST
  • Path: /v1/user/{userId}/offer/{offerId}/redeem
  • Auth: Authorization: Bearer <JWT_TOKEN> (strict for certain hosts like VAG)

Path Parameters

NameTypeRequiredDescription
userIdstringyesIdentifier of the user.
offerIdstringyesIdentifier of the offer.

Request Body

The body is passed through to the redeem handler; include any required metadata (e.g. quantity, context).

{
  "metadata": {
    "source": "app"
  }
}

Response

  • 200 OK

    • Offer redeemed successfully; response mirrors your redeem handler payload.
  • 400 / 401 / 403 / 404

    • Standard error responses for invalid offer, insufficient permissions, or missing user.

GET /user/{userId}/offer/{offerId}/redeemable – Check Offer Redeemable

Check whether a user can redeem a specific offer (points, status, limits).

  • Method: GET
  • Path: /v1/user/{userId}/offer/{offerId}/redeemable
  • Auth: Authorization: Bearer <JWT_TOKEN>

Response

Returns a structured object from checkOfferIsRedeemable indicating whether the offer is redeemable and why (e.g. not enough points, expired, already redeemed).


GET /user/{userId}/survey – Get Available Surveys

List all currently active surveys that the user has not answered yet.

  • Method: GET
  • Path: /v1/user/{userId}/survey
  • Auth: Authorization: Bearer <JWT_TOKEN>

Response

  • 200 OK

    [
      {
        "surveyId": "nps_2025_01",
        "title": "How likely are you to recommend us?",
        "type": "SINGLE",
        "validFrom": 1730438400000,
        "validUntil": 1733116799000,
        "options": [
          { "optionId": "1", "label": "Not likely" },
          { "optionId": "10", "label": "Very likely" }
        ]
      }
    ]
  • 200 OK with []

    • No available surveys.

PUT /user/{userId}/survey/{surveyId} – Submit Survey Response

Submit responses for a survey, optionally awarding points.

  • Method: PUT
  • Path: /v1/user/{userId}/survey/{surveyId}
  • Auth: Authorization: Bearer <JWT_TOKEN>

Request Body

{
  "userId": "user_123",
  "surveyId": "nps_2025_01",
  "responses": ["10"]
}
  • userId string (required): Must match the path user.
  • surveyId string (required): Must match the path survey.
  • responses string[] (required): Array of selected option IDs.

Rules:

  • Only valid option IDs are accepted; invalid options trigger 400.
  • For SINGLE surveys, exactly one response is allowed.
  • Users can only answer each survey once.

Responses

  • 201 Created

    {
      "message": "Survey response submitted successfully"
    }
  • 400 Bad Request

    • Missing userId, surveyId, or responses.
    • Invalid options.
    • Survey already answered.
  • 404 Not Found

    • Survey does not exist.
  • 500 Internal Server Error

    • Error while updating statistics or points.

GET /user/{userId}/challenge – Get User Challenges

Return the list of challenges for a user, including progress and completion state.

  • Method: GET
  • Path: /v1/user/{userId}/challenge
  • Auth: Authorization: Bearer <JWT_TOKEN>

Query Parameters

NameTypeRequiredDefaultDescription
completedbooleanno-true for completed, false for open, omitted = all

Response

  • 200 OK

    {
      "challenges": [
        {
          "challengeId": "Favori-iY6sx",
          "status": "ACTIVE",
          "goal": 1,
          "points": 15,
          "title": {
            "de": "Stammstrecke: Favorisiere eine Verbindung",
            "en": "Go-to route: favor a route connection"
          },
          "type": "FIXED",
          "progress": 0,
          "completed": false,
          "completedDate": null,
          "seriesId": "BikeSeries-JE3ew",
          "isLastStep": false
        }
      ],
      "lastEvaluatedKey": null
    }
  • 400 Bad Request

    • userId missing.
  • 500 Internal Server Error

    • Error while fetching challenges or progress.

GET /user/{userId}/challenge/{challengeId} – Get Specific Challenge

Retrieve a single challenge by id for a user (including progress).

  • Method: GET
  • Path: /v1/user/{userId}/challenge/{challengeId}
  • Auth: Authorization: Bearer <JWT_TOKEN>

This is a convenience wrapper around the same data used in the list endpoint.


GET /user/{userId}/streak – Get User Streaks

Return all streaks and their current progress for a user.

  • Method: GET
  • Path: /v1/user/{userId}/streak
  • Auth: Authorization: Bearer <JWT_TOKEN>

Response

  • 200 OK

    {
      "streaks": [
        {
          "userId": "user_123",
          "streakId": "daily_login",
          "currentStartDate": 1730438400000,
          "timeFrame": "daily",
          "currentCount": 5,
          "longestStreak": 14,
          "lastCompletedAt": "2025-11-05T00:00:00.000Z",
          "missedCount": 0,
          "completedDates": [
            "2025-11-01",
            "2025-11-02"
          ],
          "milestonesClaimed": [7]
        }
      ],
      "count": 1
    }
  • 500 Internal Server Error

    • Missing userId or database error.

GET /user/{userId}/streak/{streakId} – Get Specific Streak

Return details for a single streak of a user (see streak reference for schema).

  • Method: GET
  • Path: /v1/user/{userId}/streak/{streakId}
  • Auth: Authorization: Bearer <JWT_TOKEN>

Uses the same data as GET /user/{userId}/streak, filtered by streakId.