NAV
shell

Introduction

Welcome to the Merit Point Exchange API!.

This API has been created for partners to connect to our Point Exchange service

Base URL

Make sure to replace {{BaseURL}} with the relevant staging or production URL.

Base URL for Merit API endpoints

Description URL
Staging https://api.sandbox.meritincentives.com/points-exchange
Prod
Content-Type application/json
Authorization Bearer <TOKEN>

Authentication

Sample API call with the Authorization header

curl "{{BaseURL}}/path/to/resource" \
  -H "Authorization: Bearer <TOKEN>"

Make sure to replace with your Token.

All the API requests to the server should include Token in the Authorization header:

Authorization: Bearer <TOKEN>

You must replace TOKEN with your Token.

Token will be provided while onboarding.

Authorization (Client Credentials Flow)

Please use this flow only if specifically given access to by Merit. Else, fallback to the Authentication section.

This API uses OAuth 2.0 Client Credentials Grant for authentication.

Overview

To access this API, your application must first obtain a Bearer access token from the Token API using its assigned client_id and client_secret. The token must be included in the Authorization header (Authorization: Bearer <TOKEN>) of each API request. Each access token is valid for 24 hours (86400 seconds).

Token Endpoint

POST {{BaseURL}}/identity/v1/oauth2/token

Used to obtain the access token.

Headers

Header Relevance Value
Content-Type Required application/x-www-form-urlencoded

Body Parameters

Parameter Relevance Value
grant_type Required Must be set to client_credentials.
client_id Required The client ID issued to your application (e.g., 4f9xxxx8a1e3f6xxxxxx).
client_secret Required The client secret issued to your application (e.g., Sxxx8v9Gxxxx).

Response

If everything goes well, you'll receive a response with a 200 OK status and the following JSON data in the response body:

Key Type Description
access_token string An access token that can be used to authenticate and authorize subsequent API requests.
token_type string How the access token may be used: always "Bearer".
expires_in int The time period (in seconds) for which the access token is valid.

RESPONSE - 200 OK

{
  "access_token": "NgCXRKc...MzYjw",
  "token_type": "bearer",
  "expires_in": 86400
}

List my loyalty programs

GET {host}/api/v1/loyalty-programs?limit=10&offset=0

This API lists all the loyalty programs that (usually) belong to the API Client or that have given direct earn/burn access to the API client. Usually, the length of the response list is not more than 1.

Headers

Key Value
Accept-Language en
Authorization Bearer <TOKEN>

Possible error codes

Response:

Loyalty programs JSON structured like this:

[
  {
    "loyalty_program_id": "TOPSHERPA",
    "loyalty_program_name": "TopSherpa",
    "currency_id": "TOPSHERPA",
    "currency_name": "Top Sherpa Coins",
    "logo_url": "logo.com",
    "banner_url": "banner.com",
    "required_fields": [
      {
        "id": "id",
        "name": "Member ID"
      }
    ],
    "description": "",
    "terms_and_conditions": "",
    "category": "Airline",
    "brand": "TopSherpa",
    "is_default": true
  }
]

Loyalty Program

Field Description Type
loyalty_program_id Loyalty program slug string
loyalty_program_name Loyalty program name string
currency_id Currency slug used for exchange string
currency_name Currency name string
logo_url Loyalty program logo string
banner_url Loyalty program banner string
category Loyalty program category string
brand Loyalty program brand string
description Loyalty program description string
terms_and_conditions Loyalty program T&C string
required_fields Required fields to create exchange. Supported Required Fields object

List Exchange Out Loyalty Programs

GET {host}/api/v1/loyalty-programs/{loyalty_program_id}/exchange-out?limit=10&offset=0

List all the loyalty-programs to which your loyalty program can be exchanged to. The partnership details of the exchange are also returned as part of this response.

Headers

Key Value
Accept-Language en
Authorization Bearer <TOKEN>

Possible error codes

Loyalty Programs JSON structured like this:

[
  {
    "loyalty_program_id": "TOPSHERPA",
    "loyalty_program_name": "TopSherpa",
    "currency_id": "TOPSHERPA",
    "currency_name": "Top Sherpa Coins",
    "logo_url": "logo.com",
    "banner_url": "banner.com",
    "category": "Airline",
    "brand": "TopSherpa",
    "origin_to_destination_rate": 5.00000000,
    "destination_min_amount": 10,
    "destination_max_amount": 1000,
    "required_fields": [
      {
        "id": "id",
        "name": "Member ID"
      }
    ],
    "description": "",
    "terms_and_conditions": "",
    "campaign": {
      "id": "56377294-572e-4155-b70a-c47867d6cd01",
      "name": "Campaign title",
      "start_at": 1727267961,
      "end_at": 1727267962,
      "reward_rule": {
        "type": "FIXED_PERCENTAGE",
        "rule": {
          "amount": 30
        }
      }
    }
  },
  {
    "loyalty_program_id": "EXAMPLE",
    "loyalty_program_name": "Example",
    "currency_id": "EXAMPLE",
    "currency_name": "Example Coin",
    "logo_url": "logo.com",
    "banner_url": "banner.com",
    "category": "Retail",
    "brand": "Example",
    "origin_to_destination_rate": 0.00816792,
    "destination_min_amount": 5,
    "destination_max_amount": 500,
    "required_fields": [
      {
        "id": "id",
        "name": "Member ID"
      },
      {
        "id": "first_name",
        "name": "Member name"
      }
    ],
    "description": "",
    "terms_and_conditions": "",
    "campaign": null
  }
]

Request fields

Field Description Type Required
limit By default limit is 10 integer false
offset integer false
loyalty_program_id Loyalty program ID string true

Partner Loyalty Program

Field Description Type
loyalty_program_id Loyalty program slug string
loyalty_program_name Loyalty program name string
currency_id Currency slug used for exchange string
currency_name Currency name string
logo_url Loyalty program logo string
banner_url Loyalty program banner string
category Loyalty program category string
brand Loyalty program brand string
description Loyalty program description string
terms_and_conditions Loyalty program T&C string
origin_to_destination_rate Conversion rate. How many origin currency you need for 1 destination currency float
destination_max_amount int
destination_min_amount int
required_fields Required fields to create exchange. Supported Required Fields object
campaign Campaign object

Campaign

Field Description Type
id Campaign id string
start_at Epoch time when campaign start int
end_at Epoch time when campaign end int
reward_rule Reward Rule object

Reward Rule

Field Description Type
type Reward type. Currently supported only FIXED_PERCENTAGE string
rule Reward rule. Object stores all the necessary information to properly calculate reward object

Validate Member

POST {host}/api/v1/validate/member

Headers

Key Value
Authorization Bearer <TOKEN>

Request body:

Validate member Request JSON structured like this:

{
  "currency_id": "MERIT-COIN",
  "member": {
    "id": "member1",
    "first_name": "Example first name",
    "last_name": "Example last name",
    "mobile_number": "123456789",
    "country_calling_code": "123",
    "email": "test@email.com"
  }
}
Field Description Type Required
currency_id Currency ID. string True
member The required fields in the member object depend on the loyalty partner. The specific set of required fields for a loyalty partner can be found in the required_fields object in the response of the List Exchange Out Loyalty Programs API.

Example:
For instance, if LoyaltyProgramA requires only id and last_name, then only these fields become mandatory. All other fields will be considered optional. Supported Required Fields
object True

Possible error codes

Response 200

Validate member JSON structured like this:

{
  "valid": true
}
Field Description Type
valid If member is invalid/inactive/does not exist, this value will be False. Otherwise True bool

Calculate Exchange

POST {host}/api/v1/calculate-exchange/

Headers

Key Value
Authorization Bearer <TOKEN>

Request body:

Create Exchange JSON structured like this:

{
  "origin_amount": 50,
  "origin_currency_id": "MERIT-COIN",
  "destination_currency_id": "TOPSHERPA",
  "campaign_id": "56377294-572e-4155-b70a-c47867d6cd01"
}
Field Description Type Required
origin_amount int True
origin_currency_id Origin Currency ID string True
destination_currency_id Destination Currency ID string True
campaign_id Campaign applied to transaction string False

Possible error codes

Response

Create Exchange JSON structured like this:

{
  "origin_amount": 50.0,
  "destination_amount": 100.0,
  "bonus_amount": 30,
  "total_amount": 130,
  "origin_currency_id": "MERIT-COIN",
  "destination_currency_id": "AL-FURSAN",
  "campaign_id": "56377294-572e-4155-b70a-c47867d6cd01"
}
Field Description Type
origin_amount int
destination_amount int
origin_currency_id Origin Currency ID string
destination_currency_id Destination Currency ID string
bonus_amount Bonus amount int
campaign_id Campaign applied to transaction str
total_amount Total amount of received points int

Create Exchange

API to add points to the destination loyalty program.

This API operates asynchronously; the API accepts the request and responds immediately after performing a few validations.

The actual addition of points to the destination loyalty program accounts occur in the background.

Clients will be notified of the final status either through Webhooks or they can query using Get Exchange By reference_id.

POST {host}/api/v1/exchange/

Headers

Key Value
Authorization Bearer <TOKEN>

Request body:

Create Exchange JSON structured like this:

{
  "reference_id": "gvdags-abdvas-dabbwhf",
  "origin_amount": 50,
  "destination_amount": 100,
  "origin_currency_id": "MERIT-COIN",
  "destination_currency_id": "TOPSHERPA",
  "bonus_amount": 30,
  "campaign_id": "56377294-572e-4155-b70a-c47867d6cd01",
  "destination_member": {
    "id": "member1",
    "first_name": "Example name"
  },
  "origin_member": {
    "id": "example_id"
  }
}
Field Description Type Required
reference_id Unique value string True
origin_amount int True
destination_amount int True
origin_currency_id Origin Currency ID string True
destination_currency_id Destination Currency ID string True
destination_member Member Object. Object consists of Supported Required Fields object True
origin_member Member Object. Object consists of data representing origin member object True
bonus_amount Bonus amount int False
campaign_id Campaign applied to transaction string False

Possible error codes

Response

Create Exchange JSON structured like this:

{
  "reference_id": "gvdags-abdvas-dabbwhf",
  "origin_amount": 50.0,
  "destination_amount": 100.0,
  "status": "IN_PROCESS",
  "status_code": "SUCCESS",
  "origin_currency_id": "MERIT-COIN",
  "destination_currency_id": "AL-FURSAN",
  "bonus_amount": 30,
  "campaign_id": "56377294-572e-4155-b70a-c47867d6cd01",
  "total_amount": 130,
  "destination_transaction": {
    "reference_id": "328ccb61acd4ae6ce63b4ae34c42ebbe7f2f02d8"
  },
  "destination_member": {
    "id": "77250666"
  },
  "origin_member": {
    "id": "test"
  }
}
Field Description Type
reference_id Unique value string
origin_amount int
destination_amount int
status Transaction Status string
status_code Status Code string
origin_currency_id Origin Currency ID string
destination_currency_id Destination Currency ID string
destination_member Member Object. Object consists of Supported Required Fields object
origin_member Member Object. Object consists of data representing origin member object
bonus_amount Bonus amount int
campaign_id Campaign applied to transaction string
total_amount Total amount of received points int
destination_transaction Used for internal purposes at Merit. We do not recommend our clients to use the contents of this field. Provides the details relevant to the transaction at the destination loyalty program. Should be accessed only if the status of this transction is COMPLETED object

Get Exchange By reference_id

GET {host}/api/v1/exchanges/{reference_id}

Headers

Key Value
Authorization Bearer <TOKEN>

Request fields

Field Description Type Required
reference_id Reference ID string true

Possible error codes

Response

Get Exchange JSON structured like this:

{
  "reference_id": "gvdags-abdvas-dabbwhf",
  "origin_amount": 50.0,
  "destination_amount": 100.0,
  "status": "COMPLETED",
  "status_code": "SUCCESS",
  "origin_currency_id": "MERIT-COIN",
  "destination_currency_id": "AL-FURSAN",
  "bonus_amount": 30,
  "campaign_id": "56377294-572e-4155-b70a-c47867d6cd01",
  "total_amount": 130,
  "destination_transaction": {
    "reference_id": "328ccb61acd4ae6ce63b4ae34c42ebbe7f2f02d8"
  },
  "destination_member": {
    "id": "77250666"
  },
  "origin_member": {
    "id": "test"
  }
}
Field Description Type
reference_id Unique value string
origin_amount int
destination_amount int
status Transaction Status string
status_code Status Code string
origin_currency_id Origin Currency ID string
destination_currency_id Destination Currency ID string
destination_member Member Object. Object consists of Supported Required Fields object
origin_member Member Object. Object consists of data representing origin member object
bonus_amount Bonus amount int
campaign_id Campaign applied to transaction str
total_amount Total amount of received points int
destination_transaction Used for internal purposes at Merit. We do not recommend our clients to use the contents of this field. Provides the details relevant to the transaction at the destination loyalty program. Should be accessed only if the status of this transction is COMPLETED object

Transaction

Field Description Type
id Transaction ID string
reference_id Unique value string
origin_amount int
destination_amount int
status Transaction Status string
status_code Status Code string
origin_currency_id Origin Currency ID string
destination_currency_id Destination Currency ID string
destination_member Member Object. Object consists of data taken from required_fields in LoyaltyProgram object
origin_member Member Object. Object consists of data representing origin member object
bonus_amount Bonus amount int
campaign_id Campaign applied to transaction str
total_amount Total amount of received points int

Supported Required Fields

Field Description Type
id The unique loyalty membership identifier string
first_name The first name used by the member to register with the loyalty program string
last_name The last name used by the member to register with the loyalty program string
mobile_number The mobile number used by the member to register with the loyalty program. This is without the country code prefix. Example: "5555555555". Country code should be passed separately under country_calling_code. string
country_calling_code The country code of the mobile number. Example: "966" (Without a '+') string
email The email used by the member to register with the loyalty program string

Transaction Status

Status Description
CREATED This is when the order is created in the system. The API Client will never see this status.
QUEUED This occurs when the client is a prepaid client and the client doesn't have enough funds in their account.
IN_PROCESS For prepaid clients, this is after successful payment. For postpaid clients, this is the first status of the transaction that the API Client sees.
COMPLETED Transaction completed successfully
FAILED Transaction failed

Status Code

Addition to Transaction Status. Status code gives more context about Status.

Status Code Description
SUCCESS Operation complete successfully
INSUFFICIENT_FUNDS Special status code for QUEUED. If client doesn't have enough funds in their account.
INVALID_MEMBER Member used to create transaction does not exists.
EXCHANGE_PARTNER_NOT_REACHABLE Something goes wrong. We cannot reach partner to create transaction
UNABLE_TO_PROCESS Transaction failed completely. Reach out to dev team for more details
MAXIMUM_AMOUNT_REACHED Amount of points used to create the transaction exceeds the limit.

Errors

Errors structure

Field Description Type
code Error Code string
message Short description of the error string
details Optional Object. Details about error Optional object([string][string])

Validation Error Example:

{
  "code": "VALIDATION_ERROR",
  "message": "Validation error",
  "details": {
    "field": "body.destination_amount",
    "issue": "Input should be a valid integer, got a string"
  }
}

Client Error Example:

{
  "code": "HAVE_NO_ACCESS_TO_LOYALTY_PROGRAM",
  "message": "You have no access to loyalty program",
  "details": {}
}
Error Code HTTP code Meaning
VALIDATION_ERROR 400 Bad Request -- Your request is invalid.
DUPLICATE_REFERENCE_ID 400 Bad Request -- Reference id should be unique.
INCORRECT_DESTINATION_AMOUNT 400 Bad Request -- Destination amount does not much destination amount after exchange.
INCORRECT_BONUS_AMOUNT 400 Bad Request -- Bonus amount does not much bonus amount for campaign.
CAMPAIGN_NOT_AVAILABLE 400 Bad Request -- Campaign expired or does not exists
MIN_AMOUNT_LIMIT_NOT_REACHED 400 Bad Request -- Destination amount has not reached the minimum amount limit.
MAX_AMOUNT_LIMIT_REACHED 400 Bad Request -- Destination amount has reached the maximum amount limit.
UNAUTHORIZED 401 Unauthorized -- Your API key is wrong.
ACCESS_DENIED 403 Forbidden -- You have no access to resource. General error.
HAVE_NO_ACCESS_TO_LOYALTY_PROGRAM 403 Forbidden -- You have no access to loyalty program.
NOT_FOUND 404 Not Found -- The specified resource could not be found.
METHOD_NOT_ALLOWED 405 Method Not Allowed -- You tried to access a resource with an invalid method.
INTERNAL_ERROR 500 Internal Server Error -- We had a problem with our server. Try again later.

Webhooks

Integration Guide

Webhooks provide a powerful mechanism to receive real-time notifications about events occurring within your Merit account. By registering a webhook URL, Merit can directly push event data to your application, enabling immediate and automated responses.

Registering a Webhook URL

To begin receiving webhook notifications, provide your webhook URL to the Merit team. Merit will register this URL and subscribe it to relevant events based on your application needs. For instance, accounts using Merit's Points-Exchange APIs will have their webhooks subscribed to related transaction events.
Upon registration, a secret key will be shared with you, essential for the secure verification of incoming notifications.

Webhook Endpoint Requirements

Your endpoint must be a HTTPS POST endpoint listening on port 443.

Webhook Request Details

Payload Description
Body Each webhook notification contains an Event Object in JSON format.
Headers Critical headers include X-Merit-Signature, which is used for message verification.
{
  "Content-Type": "application/json",
  "X-Merit-Signature": "t=1492774577,v1=ansdoj213e9,v0=6ffbb59b"
}

Handling Webhook requests

Receiving and processing webhook notifications consists of two primary steps:

  1. Receipt of Message:
  1. Verification of Message:

import hmac
import hashlib
import base64
import re


def generate_base64_hmac(timestamp, raw_payload, secret_key):
  message = f"{timestamp},{raw_payload}"
  secret_key_bytes = str(secret_key).encode('utf-8')
  message_bytes = message.encode('utf-8')
  hmac_obj = hmac.new(secret_key_bytes, message_bytes, hashlib.sha256)
  hmac_digest = hmac_obj.digest()
  hmac_base64 = base64.b64encode(hmac_digest)
  return hmac_base64.decode()


def verify_signature(signature_header, raw_payload, secret_key):
  # Extracting timestamp
  timestamp_match = re.search(r't=(\d+)', signature_header)
  if not timestamp_match:
    return False
  timestamp = timestamp_match.group(1)
  computed_hash = generate_base64_hmac(timestamp, raw_payload, secret_key)
  # Extracting all hashes
  hashes = re.findall(r'v\d+=([^,]+)', signature_header)
  # Verifying each hash
  for expected_hash in hashes:
    if hmac.compare_digest(computed_hash, expected_hash):
      return True
  return False


# Example Use Case
signature_header = "t=1734111281,v1=yo7/S+zHFBg6sYWVcNkmb2j3EzIJ2XEQ0TlYWOaFWm0=,v1=T0kP94n+cL6Za34J8Y8nhAxpcdJxDVfXY5WxGo4Y1Ho="

raw_payload = '{"id":"evt_6820f736-5466-4a64-9858-ac8bf514f421","created_at":1686089970,"event_type":"points_exchange.transaction.created","data":{"id":"id_of_the_transaction","reference_id":"21212gvdags-abdvas-dabbwh12111212f","origin_amount":50,"destination_amount":100,"status":"COMPLETED","origin_currency_id":"MERIT-COIN","destination_currency_id":"AL-FURSAN","bonus_amount":30,"campaign_id":"56377294-572e-4155-b70a-c47867d6cd01","total_amount":130,"destination_member":{"id":"77250666"},"origin_member":{"id":"test"}}}'

secret_key = "0y1SVxadCBUY7MiACPEdPCJGD"

verification_result = verify_signature(signature_header, raw_payload, secret_key)

print("Verification successful." if verification_result else "Verification failed.")

Retry Policy

Merit attempts to deliver a given event to your webhook endpoint for up to 2 days with an exponential back-off. A max of 15 requests will be attempted.

Events

EventObject Example:

{
  "id": "evt_1NG8Du2eZvKYlo2CUI79vXWy",
  "created_at": 1686089970,
  "event_type": "transaction.completed",
  "version": "1.0",
  "data": {
    "id": "e38f42f7-474d-4720-872b-63b23e516314",
    "reference_id": "gvdags-abdvas-dabbwhf",
    "origin_amount": 50.0,
    "destination_amount": 100.0,
    "status": "COMPLETED",
    "status_code": "SUCCESS",
    "origin_currency_id": "MERIT-COIN",
    "destination_currency_id": "AL-FURSAN",
    "bonus_amount": 30,
    "campaign_id": "56377294-572e-4155-b70a-c47867d6cd01",
    "total_amount": 130,
    "destination_member": {
      "id": "77250666"
    },
    "origin_member": {
      "id": "test"
    }
  }
}

Event object

name description
id Unique identifier of the event
created_at Unix epoch of the time at which this event is generated
event_type The type of the event. The entire list of event types can be found here EventTypes
version The version of the resource object which is available in the data field. This will be of no significance unless there is a version change which impacts the data structure.
data Object containing the data associated with the event. The structure of the object depends on the event_type. The details of the structure can be found in Transaction

Event Types

This is a list of all the types of events we currently send. We may add more at any time, so in developing and maintaining your code, you should not assume that only these types exist.

name description
points_exchange.transaction.completed Occurs when a points-exchange transaction is completed.
points_exchange.transaction.failed Occurs when a points-exchange transaction is failed