NAV
cURL Ruby JavaScript PHP

Introduction

Welcome to the Leevia API!

You can use Leevia API to access various functionality of Leevia platform for online contest.

In this documentation you will find information and examples on how to authenticate and use Leevia APIs. We provide examples for cURL, Ruby, JavaScript and PHP, but you can use the language you prefer to make API calls, as long as your language allows you to make HTTPS requests.

You can view code examples in the dark area to the right, and you can switch the programming language of the examples with the tabs in the top right.

Version

This is the documentation for API version 1.0

Last updated: December 20, 2022

Authentication

Every request to an API endpoint must be authenticated. Authentication is performed by a JWT token which must be supplied as an HTTP header of the request. The token http header is in the format: Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhY2NvdW50X2lkIjyxLCJleHAiOjE1Mzg3NDI5NjgsImlzc7I6IkxlZXZpYSBhcGkifQ.dE4M1UKC4yFBq3SvlruJieqi_vBQB6qy1DjYyDGjptA

To obtain this JWT token you first need to sign in to the API with your api_key and api_secret pair. You will find these keys in the API page of your contest.

Therefore authentication is performed in two steps:

  1. Obtain the JWT token using api_key and api_secret
  2. Call the desired endpoint providing the JWT token obtained

Obtain the JWT token

To Obtain the JWT token

require 'net/http'
require 'openssl'

uri = URI("https://app.leevia.com/api/v1/#{PRODUCT_TYPE}/#{PRODUCT_ID}/authenticate")
request = Net::HTTP::Get.new(uri)
request['Accept'] = 'application/vnd.leevia.api.v1+json'
request['Content-Type'] = 'application/json'
request['App-Key'] = KEY
timestamp = Time.now.to_i
request['Timestamp'] = timestamp
request['Signature'] = OpenSSL::HMAC.hexdigest('sha256', SECRET, "#{KEY}.#{timestamp}")

result = Net::HTTP.start(uri.hostname, uri.port) do |http|
  http.request(request)
end
result['Authorization']
timestamp=$(date +%s)
message="KEY.$timestamp"
signature=$(echo -n $message | openssl dgst -sha256 -hmac SECRET -binary | xxd -p -c 256)

curl -I --request GET \
  --url https://app.leevia.com/api/v1/PRODUCT_TYPE/PRODUCT_ID/authenticate \
  --header 'Accept: application/vnd.leevia.api.v1+json' \
  --header 'App-Key: KEY' \
  --header 'Content-Type: application/json' \
  --header "Signature: $signature" \
  --header "Timestamp: $timestamp"| grep Authorization | sed -e s/Authorization:[[:space:]]//
var CryptoJS = require("crypto-js");
const https = require('https');
var fs = require('fs');

var PRODUCT_TYPE = PRODUCT_TYPE;
var CAMPAIGN_ID = CAMPAIGN_ID;
var API_KEY = 'KEY';
var API_SECRET = 'SECRET';
var HOST = "app.leevia.com";

timestamp = new Date();
message = API_KEY + "." + timestamp;
signature = CryptoJS.HmacSHA256(message, API_SECRET).toString(CryptoJS.enc.Hex);

const options = {
  hostname: HOST,
  path: '/api/v1/' + PRODUCT_TYPE + '/' + PRODUCT_ID + '/authenticate',
  method: 'GET',
  headers: {
    'Accept': 'application/vnd.leevia.api.v1+json',
    'Content-Type': 'application/json',
    'App-Key': API_KEY,
    'Timestamp': timestamp,
    'Signature': signature
  }
};

const req = https.request(options, (res) => {
  console.log(res.headers['authorization']);
});
req.end();
  $product_type = PRODUCT_TYPE; // Replace with "campaigns" or "loyalty_portals"
  $product_id = PRODUCT_ID; // Replace with your product id
  $api_key = 'KEY'; // replace with your api key
  $api_secret = 'SECRET'; // replace with your api_secret
  $host_name = 'app.leevia.com';

  $today_date = new DateTime("now", new DateTimeZone('Europe/Rome'));
  $nowtime = time($today_date);
  $to_hash = $api_key.".".$nowtime;
  $signature = hash_hmac("sha256", $to_hash, $api_secret);
  $ch = curl_init();

  curl_setopt($ch, CURLOPT_HEADER, 1);
  curl_setopt($ch, CURLOPT_URL, "https://".$host_name."/api/v1/".$product_type."/".$product_id."/authenticate");
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
  curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    'Accept:application/vnd.leevia.api.v1+json',
    'App-Key:'.$api_key,
    'Content-Type:application/json',
    'Signature:'.$signature,
    'Timestamp:'.$nowtime
  ));

  $content = curl_exec($ch);

  foreach (explode("\r\n", $content) as $hdr) {
    $header_field = explode(': ', $hdr);
    if ($header_field[0] == 'Authorization') {
      $token = $header_field[1];
    }
  }
  print_r($token);

Make sure to replace KEY with your API key.
Make sure to replace SECRET with your API Secret.
Make sure to replace PRODUCT_TYPE with the type of your product: campaigns or loyalty_portals.
Make sure to replace PRODUCT_ID with the id of your product.

Full examples at: leevia-api-samples

In this section we describe how to obtain the JWT token that can be used to authenticate API calls.

A valid request must provide 3 headers: App-Key, Timestamp, Signature. Signature is a SHA256 HMAC hexdigest of the string App-Key.Timestamp

Headers

Header Description Example
App-Key this is your api_key TSOePKXvYHQ4fUu8Lv-DePB2a-o
Timestamp a timestamp in a format like %Y-%m-%d %H:%M:%S %Z or integers 2018-10-08 12:29:16 +0200 or 1538994556
Signature SHA256 HMAC digest of the string App-Key.Timestamp, How to generate digest in different languages 99a7f67ea1f8b2beb488de470dd0e541fb1c0a70f7741dca0dffea8bdafb2d24

Product

You need to specify product type and id in the REST url to obtain the correct authentication token for your subsequent request to work. Product can be of type campaign, whatsapp_campaign or loyalty_portal, therefore urls can be for example:

Signature

To verify if you are creating correct signature you can use the following input to test your algorithm:

timestamp = 1234
key = "my_app_key"
secret = "my_super_secret_key"

which means that "message" will be my_app_key.1234. And your signature must be equal to 90f5df1c0df13ba3701bc21566e4a529720939b7f12b9a60be1d611727a09d0c

Response

Example response's HTTP Headers

HTTP/1.1 204 No Content
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhY2NvdW50X2lkIjoxLCJleHAiOjE1MzkwOTY5NTIsImlzcyI6IkxlZXZpYSBhcGkifQ.IkuNktOM5gFZIdnvGzdz4q4szocJ7AZGiZn7v7iPrvI
x-expires: 1539096952

When the request succeeds you get an empty response with status code 204 (No Content). You will find the JWT Token in the Authorization response header.

There will be also present an header x-expires that contains a timestamp in which the token will expires and hence it would not be possible to use this token anymore.

Tokens expire after one day they have been issued.

Headers

Any request made to the Leevia API must include two specific headers: Accept and Content-Type. These are used to determine the version of the API and the format of the request's body.

Header Value
Accept application/vnd.leevia.api.v1+json
Content-Type application/json

Create participants for Instant Win

Instant Win is a family of contest types. An instant win could be Rush and Win, Insta Win, Coupon or Giveaway, which differ for the way prizes are assigned.

You can find more information about Instant Win family here.

Register Participant

To register a new Participant

require 'net/http'
require 'openssl'
require 'json'

uri = URI("https://app.leevia.com/api/v1/campaigns/instant_wins/#{CAMPAIGN_ID}/participants")
request = Net::HTTP::Post.new(uri)
request['Accept'] = 'application/vnd.leevia.api.v1+json'
request['Content-Type'] = 'application/json'
request['Authorization'] = 'Bearer JWT_TOKEN'

participant = {
  first_name: "John",
  last_name: "Doe",
  email: "john.doe@leevia.com",
  acceptances: {rules: true, newsletter: false},
  custom_data: {
    date_of_birth: "1971-05-18",
    a_custom_field: "some text"
  },
  registration_ip: "192.168.0.4"
}

request.body = participant.to_json

result = Net::HTTP.start(uri.hostname, uri.port) do |http|
  http.request(request)
end
result.body
curl --request POST \
  --url https://app.leevia.com/api/v1/campaigns/instant_wins/CAMPAIGN_ID/participants \
  --header 'Accept: application/vnd.leevia.api.v1+json' \
  --header 'Authorization: Bearer JWT_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{"first_name":"John", "last_name":"Doe", "email":"john.doe@leevia.com", "registration_ip":"192.168.0.4", "acceptances":{ "rules":"true", "newsletter":"false"}, "custom_data":{"date_of_birth": "1971-05-18", "a_custom_field":"some text"} }'
  var campaign_id = CAMPAIGN_ID;
  var token = JWT_TOKEN;
  var HOST = "app.leevia.com";

  const https = require('https');

  var participant_data = {
    first_name: 'Mario',
    last_name: 'Rossi',
    email: 'mario.rossi@example.com',
    registration_ip: '192.168.0.4',
    custom_data: {
      date_of_birth: '1989-10-03'
    },
    acceptances: {
      rules: true,
      newsletter: false
    }
  };

  var participant_data_string = JSON.stringify(participant_data)

  const options = {
  hostname: HOST,
  path: '/api/v1/campaigns/instant_wins/' + campaign_id + '/participants',
  method: 'POST',
  headers: {
    "Accept": 'application/vnd.leevia.api.v1+json',
    'Content-Type': 'application/json',
    'Content-Length': participant_data_string.length,
    "Authorization": token
    }
  };
  const req = https.request(options, (res) => {
    res.on('data', (chunk) => {
      console.log(`BODY: ${chunk}`);
    });
  });
  req.write(participant_data_string);
  req.end();
  $campaign_id = CAMPAIGN_ID;
  $token = JWT_TOKEN;

  $participant = new \stdClass();
  $participant->first_name = "Mario";
  $participant->last_name = "Rossi";
  $participant->email = "mario.rossi@example.come";
  $participant->registration_ip = "192.168.0.4";

  $custom_data = new \stdClass();
  $custom_data->date_of_birth = '1989-10-03';
  $participant->custom_data = $custom_data;

  $acceptances = new \stdClass();
  $acceptances->rules = true;
  $acceptances->newsletter = false;
  $participant->acceptances = $acceptances;

  $participant_json = json_encode($participant);

  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, "https://".$host_name."/api/v1/campaigns/instant_wins/".$campaign_id."/participants");
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
  curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    'Accept:application/vnd.leevia.api.v1+json',
    'Content-Type:application/json',
    "Authorization:".$token,
    "Content-Length:".strlen($participant_json)
  ));
  curl_setopt($ch, CURLOPT_POSTFIELDS, $participant_json);

  $content = curl_exec($ch);

  print_r($content);

  curl_close($ch);

Make sure to replace CAMPAIGN_ID with the id of your campaign/contest.
Make sure to replace JWT_TOKEN with your JWT token.

Full examples at: leevia-api-samples

A participant is a person who participate to an Instant Win contest. A participant must provide First Name, Last Name, Email(and/or Phone) and other fields depending on the contest setup.

HTTP Request

POST https://app.leevia.com/api/v1/campaigns/instant_wins/CAMPAIGN_ID/participants

CAMPAIGN_ID is the id of your contest.

Fields

Field Type Description Example
first_name String First Name of the participant Alice
last_name String Last Name of the participant Wonderland
email String Email of the participant alice.wonderland@leevia.com
phone String Phone of the participant 02 7421 2398 262
acceptances Object An hash containing rules and newsletter with boolean value (rules must be true) {"rules": true, "newsletter": false}
custom_data Object An hash containing any other field required by the contest {"pet": "bunny"}
registration_ip String The IP address of the participant at the registration time 127.0.0.1

acceptances can contain additional custom fields. For more information on the type and format of various fields see the specific section: Privacy Acceptances

custom_data must contain all the mandatory additional field that were specified during contest creation. For more information on the type and format of various fields see the specific section: Additional User Data

Depending on your contest setup you can use the field email or phone as an "identifier" for the participant. Therefore an accepted request may contain only the field phone if the contest does require the field phone and not email.

Also depending on your contest setup, you may not need to include first_name and/or last_name in the request.

registration_ip should contain the real IP address of the participant.
Note that if your system is behind a reverse proxy, firewall or CDN (like Cloudflare) you should make sure to fill this value with the IP of whom is making the request, not with the IP of the CDN! In this case you can usually find the original IP address in a special header added to the request by your proxy, for example X-Forwarded-For header.
IMPORTANT If you don't have this information or, for any reason, you don't want to send it, use one of our "safe and whitelisted" IPs: 0.0.0.0 or 127.0.0.1. Never leave the field blank and never send other values rather than valid IP address.

Response

Example of a successful response

{
  "id": 11,
  "email": "john.doe@leevia.com",
  "created_at": "2018-10-02T12:25:50.380+02:00",
  "campaign_id": 80,
  "is_winner": false,
  "coupon_code": null,
  "prize_id": 1,
  "prize_title": "iPhone",
  "prize_image_url": "https://picsum.photos/600/600"
}

Example of an error response

{
  "email": "john.doe-leevia.com",
  "campaign_id": 80,
  "is_winner": false,
  "coupon_code": null,
  "prize_id": 1,
  "errors": {
      "email": [
        "is invalid"
      ],
      "rules": [
        "Accept the terms to participate"
      ]
  }
}

The response will be a JSON object.
In case of a successful response the JSON will contain information about the participant created.

When the request fails the JSON response will list all the errors with their explanation, like in the example beside. The response status code will be 422 (Unprocessable Entity).
The following table has some example of possible errors and their messages:

Error Message Explanation
"errors":{"email":["has already been taken"]} The email provided is already associate to an existing user
"errors":{"email":["is invalid"]} The email provided is not valid: not correct address like john.doe@leeviacom or disposable email like john.doe4@10minutemail.de
"errors":{"email":["can't be blank"]} The parameter email is empty thus not valid
"errors":{"parameters_missing":{"param":"registration_ip"}} One, or more, of the mandatory field is missing (registration_ip)
"errors":{"occupation":["can't be blank"]} A custom field is missing or blank (occupation)
"errors":{"rules":["Accept the terms to participate"]}} User did not accept the contest rules
errors":{"rules":["Accept the terms to participate"], "email":["has already been taken"]} Multiple errors in the same request

With the exception of the error on a missing required field which has the format "errors":{"parameters_missing":{"param":"PARAM_NAME"}} all the other possible errors follow the format: "errors":{"PARAM_NAME":["MESSAGE"]}. In the former case MESSAGE is a text which can be safely shown to the user of the application.

Prize Data

If the contest permits the users to win a prize the following fields will be returned:

Field Type Description Example Value
prize_title String the prize title "iPhone"
prize_image_url String blank if no image was configured "https://picsum.photos/600/600"
is_winner Boolean it is "true" if the participant has won the prize "true", "false"

Technically there is a substantial difference between PRIZE and WIN. So if a user plays he is associated with a PRIZE but it is not said that he has won. This is because if there are several prizes configured at the same time, the system could randomly associate a user with a prize and then always randomly decide whether he wins or not.

So that response is telling that the user has been associated with a certain Prize. To see if the user was also the winner, look at the is_winner field. Then if a Prize image was configured from setup, here you have also the prize_image_url to show it to the user along with prize_title.

Additional User Data

When creating or editing a contest it is possible to specify multiple additional user data that a user needs to provide to participate to the contest.
In this section we explain how to communicate these data through Leevia API.

An additional user data can be marked as mandatory or not. When it is mandatory you must provide it when creating a participant through API otherwise the request will not be accepted.

Additional user data must be sent in the field custom_data when registering a participant

Pre-configured fields

Example of a JSON containing multiple pre-configured additional user data

{
  "first_name":"John",
  "last_name":"Doe",
  "email":"john.doe@leevia.com",
  "acceptances": {
    "rules":true,
    "newsletter":false
  },
  "custom_data": {
    "gender": "M",
    "date_of_birth": "1971-05-18",
    "phone": 123456789,
    "city": "Milano",
    "province": "MI",
    "zip_code": "20156",
    "occupation": "Student"
  }
}

Additional user data are inside the field custom_data

Leevia provides some pre-configured fields that you can enable. These are:

Field Type Description Example Value
gender String a gender type M/F "gender": "M" or "gender": "F"
date_of_birth String a date "date_of_birth": "yyyy-mm-dd"
phone Number a phone number "phone": 3458921863525
city String a city name "city": "Milano"
province String a province name or identifier "province": "MI"
zip_code String a postal address identifier "zip_code": "20152"
occupation String a profession "occupation": "Software Developer"

Custom fields

Example of a JSON containing multiple custom additional user data

{
  "first_name":"John",
  "last_name":"Doe",
  "email":"john.doe@leevia.com",
  "acceptances": {
    "rules":true,
    "newsletter":false
  },
  "custom_data": {
    "custom_text": "Lorem ipsum dolor sit amet",
    "custom_text_area": "Lorem ipsum dolor sit amet, consectetur adipiscing elit ...",
    "custom_numeric": 123,
    "custom_generic_date": "2018-10-01",
    "custom_date_of_birth": "1971-05-18",
    "custom_time": "20:15",
    "custom_dropdown": "answer",
    "custom_multiple_choice": "choice1",
    "custom_search": "searching"
  },
  "custom_file_field": "the_file"
}

In addition to the Pre-configured fields it is also possible to add completely custom fields. When creating a custom fields you need to choose type and label.

type will be used to specify the value type.

label will be used to identify your custom field, thus if you have a custom field with label "My Custom Field" you will need to pass to the API something like "my_custom_field": "my value". You can see the identifier of your custom field in the "API" page of your contest.

When creating custom fields from the backoffice you can choose different types of input, these types are converted in JSON format like in the following table:

Input Type Field Type Example Value
Text String "my_custom_field": "Lorem ipsum dolor sit amet"
Text Area String "my_custom_field": "Lorem Ipsum faucibus purus in massa tempor"
Numeric Number "my_custom_field": 123
Generic Date String "my_custom_field": "yyyy-mm-dd"
Date of Birth String "my_custom_field": "yyyy-mm-dd"
Time String "my_custom_field": "10:56"
Dropdown String "my_custom_field": "Lorem"
Multiple Choice String "my_custom_field": "Ipsum"
Search String "my_custom_field": "dolor"

File Custom field

Example of file uploading

require 'rest-client'

response = RestClient.post( "https://app.leevia.com/api/v1/campaigns/instant_wins/#{CAMPAIGN_ID}/participants" ,
  {
    first_name: "John",
    last_name: "Doe",
    email: "john.doe@leevia.com",
    acceptances: {rules: true, newsletter: false},
    custom_data: {
      phone: 1234
    },
    file_field: File.new('FILE_PATH')
  },
  {
    "Accept": 'application/vnd.leevia.api.v1+json',
    "Authorization": 'Bearer JWT_TOKEN'
  }
)

puts response.code
puts response.body
// add the following line to the participant_data
file_field: fs.readFileSync('FILE_PATH'),
// Add the following line to the previous example
$participant->file_field = json_encode(file('FILE_PATH'));

Make sure to replace CAMPAIGN_ID with the id of your campaign/contest.
Make sure to replace JWT_TOKEN with your JWT token.
Make sure to replace FILE_PATH with your file.

Full examples at: leevia-api-samples

In the back-office site you can choose either file input type:

Input Type Field Type Example Value
File Binary "my_file_field": BINARY_FILE

There is an important difference between File custom fields and the other types: The file field has to be stored at the same level of the key custom_data.

See the ruby sample to understand how to properly create the JSON to send via API call.

Privacy Acceptances

Example of a JSON containing a custom privacy acceptance

{
  "first_name":"John",
  "last_name":"Doe",
  "email":"john.doe@leevia.com",
  "acceptances": {
    "rules":true,
    "newsletter":false,
    "a_custom_acceptance":true,
  },
  "custom_data": {
    "gender": "M"
  }
}

Additional acceptances are inside the field acceptances

When creating or editing a contest it is possible to specify multiple acceptances that a user needs to accept to participate to the contest.
In this section we explain how to communicate these data through Leevia API.

By default Leevia provides two fields: rules and newsletter.

Field Type Description Example Value
rules Boolean Privacy policy for the contest. must be true true
newsletter Boolean Acceptances to receive newsletter communications true/false

When more additional custom privacy acceptances are added, you can see their identifier in the "API" page of your contest. You need to pass this identifier in the api call along with a true/false value.

Export Participants

To Get a list of Participants

require 'net/http'
require 'openssl'
require 'json'

uri = URI("https://app.leevia.com/api/v1/#{PRODUCT_TYPE}/#{PRODUCT_ID}/participants")
request = Net::HTTP::Get.new(uri)
request['Accept'] = 'application/vnd.leevia.api.v1+json'
request['Content-Type'] = 'application/json'
request['Authorization'] = 'Bearer JWT_TOKEN'


result = Net::HTTP.start(uri.hostname, uri.port) do |http|
  http.request(request)
end
result.body
curl --request GET \
  --url https://app.leevia.com/api/v1/PRODUCT_TYPE/PRODUCT_ID/participants \
  --header 'Accept: application/vnd.leevia.api.v1+json' \
  --header 'Authorization: Bearer JWT_TOKEN' \
  --header 'Content-Type: application/json'
  var product_type = PRODUCT_TYPE;
  var product_id = PRODUCT_ID;
  var token = JWT_TOKEN;

  var HOST = "app.leevia.com";

  const https = require('https');

  const options = {
    hostname: 'app.leevia.com',
    path: '/api/v1/' + product_type + '/' + product_id + '/participants',
    method: 'GET',
    headers: {
      "Accept": 'application/vnd.leevia.api.v1+json',
      'Content-Type': 'application/json',
      "Authorization": token
      }
  };
  const req = https.request(options, (res) => {
    res.on('data', (chunk) => {
      console.log(`BODY: ${chunk}`);
    });
  });
  req.end();

Make sure to replace PRODUCT_TYPE with the type of your product: campaigns or loyalty_portals.
Make sure to replace PRODUCT_ID with the id of your product.
Replace JWT_TOKEN with your authorization token

Using this endpoint, It is possible to export a list of participants to your product (LoyaltyPortal or Contest). You will receive a list of participants and the data they have filled when registering to your product. Which include name, email, custom fields and privacy authorizations.

Note: If a participant asks to be anonymous, it will be present in the export with all the sensible data masked. You can tell when a participant is anonymous by the flag pseudoanonymized with value true.

Product can be of type campaigns, whatsapp_campaigns or loyalty_portals, therefore urls can be for example:

Filters

It is possible to filter results by passing parameters in the url.

page: Results are paginated. Using this filter you can request a specific page.

Example URLs:

Campaigns Filters

When using this endpoint on campaigns it is also possible to filter participants with the following parameters:

type: It returns only participants corresponding to the specified type. This filter can be used on products that have different phases, like Instant Poll, or Instant Win with registration. It could be useful to differentiate participants that registered from participants that are actually players for the campaign. Possible values:

Value Description
registration export participants that registered to the campaign
participation export participants that played in the campaign (they can be not unique)
winner export participants that won a prize

Example URL: https://app.leevia.com/api/v1/campaigns/123/participants?type=winner

WhatsApp Campaigns Filters

When using this endpoint on whatsapp_campaigns it is also possible to filter participants with the following parameters:

type: It returns only participants corresponding to the specified type.

Value Description
none (default) export all participants
winner export only participants that won a prize

Example URL: https://app.leevia.com/api/v1/whatsapp_campaigns/321/participants?type=winner

Response

Example of a response

{
  "participants":[
    {
      "id":64,
      "created_at":"2020-07-06T09:54:55.719+02:00",
      "email":"foo.bar@leevia.com",
      "first_name":"foo",
      "last_name":"bar",
      "language":"it",
      "date_of_birth":"1992-03-04",
      "phone":"3446259873",
      "city":"Abbadia San Salvatore",
      "occupation":"Ingegnere",
      "rules":"true",
      "newsletter":"false",
      "pseudoanonymized":"false"
    },
    {
      "id":95,
      "created_at":"2020-08-06T11:54:55.719+02:00",
      "email":"mario.rossi@leevia.com",
      "first_name":"Mario",
      "last_name":"Rossi",
      "language":"it",
      "date_of_birth":"1973-05-13",
      "phone":"3456479273",
      "city":null,
      "occupation":null,
      "rules":"true",
      "newsletter":"false",
      "pseudoanonymized":"false"
    }
  ]
  "pagination":{
    "current":1,
    "previous_page":null,
    "previous":"",
    "next_page":2,
    "next":"https://app.leevia.com/api/v1/loyalty_portals/1/participants?page=2",
    "per_page":100,
    "pages":3,
    "count":250
  }

This endpoint will return a JSON response containing:

In particular pagination will have the following fields:

Field Description
current the page number for the current request
previous the full URL for the previous page
previous_page the page number for the previous page
next the full URL for the next page
next_page the page number for the next page
per_page the maximum number of participants each page contains
pages the number of pages of participants
count the number of participant to the product

Export Campaigns from Loyalty Portal

To Get a list of Campaigns

require 'net/http'
require 'openssl'
require 'json'

uri = URI("https://app.leevia.com/api/v1/loyalty_portals/#{PRODUCT_ID}/campaigns")
request = Net::HTTP::Get.new(uri)
request['Accept'] = 'application/vnd.leevia.api.v1+json'
request['Content-Type'] = 'application/json'
request['Authorization'] = 'Bearer JWT_TOKEN'


result = Net::HTTP.start(uri.hostname, uri.port) do |http|
  http.request(request)
end
result.body
curl --request GET \
  --url https://app.leevia.com/api/v1/loyalty_portals/PRODUCT_ID/campaigns \
  --header 'Accept: application/vnd.leevia.api.v1+json' \
  --header 'Authorization: Bearer JWT_TOKEN' \
  --header 'Content-Type: application/json'
  var product_id = PRODUCT_ID;
  var token = JWT_TOKEN;

  var HOST = "app.leevia.com";

  const https = require('https');

  const options = {
    hostname: 'app.leevia.com',
    path: '/api/v1/loyalty_portals/' + product_id + '/campaigns',
    method: 'GET',
    headers: {
      "Accept": 'application/vnd.leevia.api.v1+json',
      'Content-Type': 'application/json',
      "Authorization": token
      }
  };
  const req = https.request(options, (res) => {
    res.on('data', (chunk) => {
      console.log(`BODY: ${chunk}`);
    });
  });
  req.end();

Make sure to replace PRODUCT_ID with the id of your product.
Replace JWT_TOKEN with your authorization token

Using this endpoint, It is possible to export a list of campaigns for your LoyaltyPortal. You will receive a list of campaigns and their data like start and end time, status, product type and some basic analytics.

Example of an url:

This endpoint accepts the following parameters to filter campaigns:

status

It returns only the campaign corresponding to the specified status, possible values:

Value Description
draft campaigns that are in the creation and editing phase
testable campaigns that are currently under testing
live campaigns that are online and users can participate
over campaigns that are terminated

Response

Example of a Response

{
  "campaigns":[
    {
      "id":25,
      "start_datetime":"2020-07-08T10:00:00.000+02:00",
      "end_datetime":"2020-09-28T23:59:59.999+02:00",
      "hashtag":"myfirstcampaign", // deprecated
      "product_identifier": "campaign-ABC123",
      "product":"quiz",
      "status":"over",
      "unique_visitors_count":2,
      "participants_count":1,
      "conversion_rate":50
    },

    {
      "id":55,
      "start_datetime":"2020-11-23T17:00:00.000+01:00",
      "end_datetime":"2021-03-31T23:59:59.999+02:00",
      "hashtag":"mysecondcampaign", // deprecated
      "product_identifier": "campaign-CDE456",
      "product":"insta_win",
      "status":"live",
      "unique_visitors_count":3,
      "participants_count":2,
      "conversion_rate":66
    }
  ],
  "pagination":{
    "current":1,
    "previous_page":null,
    "previous":"",
    "next_page":null,
    "next":"",
    "per_page":100,
    "pages":1,
    "count":9
  }
}

This endpoint will return a JSON response containing:

In particular pagination will have the following fields:

Field Description
current the page number for the current request
previous the full URL for the previous page
previous_page the page number for the previous page
next the full URL for the next page
next_page the page number for the next page
per_page the maximum number of campaigns each page contains
pages the number of pages of campaigns
count the number of participant to the product

Warning: The field hashtag on campaign is deprecated and will be removed in a future release. Use product_identifier as the unique identifier for campaigns.

Errors

The Leevia API may return the following error codes:

Error Code Meaning
400 Bad Request -- Your request is invalid.
401 Unauthorized -- You are not authorized. Ensure validity of token, keys or campaign_id
403 Forbidden -- You are not authorized. Ensure validity of token, keys or campaign_id
404 Not Found -- The requested resource could not be found.
422 Unprocessable Entity -- The request contains invalid parameters and the resource can not be read/write.
500 Internal Server Error -- We had a problem with our server. Try again later.
503 Service Unavailable -- We're temporarily offline for maintenance. Please try again later.

In addition to the status code, the response body may contain additional information about the failure reasons. This is especially true in the case of a 422 "Unprocessable Entity" error.

Contest Over

When the contest has terminated, no new participant can register to it. Therefore any POST request made to register a new participant will fail.

The status code returned will be a 422 "Unprocessable Entity" error.

Example errors: Contest is not yet started

{"errors":{"campaign":["not online"]}}

Example errors: Contest is terminated

{"errors":{"campaign":["not online","ended on 2020-06-24T12:00:00"]}}

Example errors: Limit of participants has been reached

{"errors":{"campaign":["not online","participation limit has been reached"]}}

Example errors: No more available coupon codes

{"errors":{"campaign":["not online","no more coupon codes available"]}}

Possible reasons for this failure are: