REST API Option

Abstract

This document outlines the use of a REST API provided as an alternative to SFTP for partners to manage merchant offers with near real-time updates. The API uses the same JSON schema as the SFTP method, but with differences including the absence of a header record, offer status being inferred from HTTP methods (PUT for upsert, DELETE for removal), and processing one offer per request.

Partners interact with the API by sending PUT requests to create or update offers and DELETE requests to remove them, referencing external merchant and offer IDs. The system enforces daily call quotas and requests-per-second (RPS) limits, returning HTTP 429 on quota violations. Notably, offers deleted by partners remain redeemable for customers who previously activated them, lasting until a 30-45 day redemption window expires.

The document includes endpoint details and emphasizes that proper usage ensures ongoing campaign flights and compliance with consumer obligations.

How It Works

  • In addition to SFTP, we also provide a REST API to make it easier to provide near real-time offer updates instead of the daily offers file dump
  • We use the same JSON schema as defined for SFTP to have 1:1 parity but with the following differences:
    • No Header Record.
    • No Offer Status - it will be derived from the HTTP method, HTTP Method PUT -> offerStatus=UPSERT, HTTP Method DELETE -> offerStatus=DELETED
    • One Offer Record at a time

Recommendations on how to call

  • Partner begins with calling Offer endpoint “PUT /api/v1/partner/merchants/{external_merchant_id}/offers/{external_offer_id} one offer at a time within the limits of the RPS.
  • Partner doesn’t need to resend the same merchant offer again unless there are any changes to the offer details or the offer needs to be taken down.
  • To Update an offer partner must call the same endpoint PUT /api/v1/partner/merchants/{external_merchant_id}/offers/{external_offer_id}as used for the creation.
  • To Delete an offer partner must call the “DELETE /api/v1/partner/merchants/{external_merchant_id}/offers/{external_offer_id}endpoint with no request payload
  • From the merchant offers provided, Cardlytics will handle running continuous engagement based campaign flights with various redemption windows ranging from 30-45 days.

Important: Because offers running on our network across our supply are activatable card-linked offers, we are obligated to our consumers who activated to retain the offers until redemption expiry. When a Partner sends a signal to "take down" or delete a merchant offer, note that for any customer that activates the offer, it will continue to be valid and paid out for redemption until the remaining 30-45 day redemption period winds down.

Guard Rails

  • Every Partner will be provided with a daily quota of the number of calls they can make, this should be ~ Number of merchant offers
  • Every Partner will be provided with an RPS, for example, 10 calls per second
  • API will return HTTP 429 if a Partner exceeds the quota of the daily limit or RPS

Endpoint Details

Create or Update the offer using the partner’s self-provided offer ID and merchant ID

Hostnameapi.cardlytics.com
Endpoint/api/v1/partner/merchants/{external_merchant_id}/offers/{external_offer_id}
MethodPUT
Content-Typeapplication/json
Request PayloadPayload
Response{ "message":"Request accepted for processing", "trace_id": "req-98765" }

Delete the offer using the partner’s self-provided offer ID and merchant ID

Hostnameapi.cardlytics.com
Endpoint/api/v1/partner/merchants/{external_merchant_id}/offers/{external_offer_id}
MethodDELETE
Content-Typeapplication/json
Response{ "message":"Request accepted for processing", "trace_id": "req-98765" }

Request Payload

{
    "offerId": "323232",
    "merchantId": "2121",
    "offerName": "Test merchant",
    "assets":
    {
        "logo":
        {
            "type": "IMAGE",
            "value":
            {
                "large":
                {
                    "url": "htttps://google.com"
                }
            }
        },
        "largeRectangle":
        {
            "type": "IMAGE",
            "value":
            {
                "large":
                {
                    "content": "<BASE_64_ENCODED_STRING>"
                }
            }
        }
    }
}


What’s Next