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:
- Obtain the JWT token using
api_key
andapi_secret
- 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 replaceSECRET
with your API Secret.
Make sure to replacePRODUCT_TYPE
with the type of your product:campaigns
orloyalty_portals
.
Make sure to replacePRODUCT_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:
https://app.leevia.com/api/v1/campaigns/123/authenticate
https://app.leevia.com/api/v1/whatsapp_campaigns/321/authenticate
https://app.leevia.com/api/v1/loyalty_portals/456/authenticate
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 replaceJWT_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 |
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 replaceJWT_TOKEN
with your JWT token.
Make sure to replaceFILE_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
orloyalty_portals
.
Make sure to replacePRODUCT_ID
with the id of your product.
ReplaceJWT_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:
https://app.leevia.com/api/v1/campaigns/123/participants
https://app.leevia.com/api/v1/whatsapp_campaigns/321/participants
https://app.leevia.com/api/v1/loyalty_portals/456/participants
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:
https://app.leevia.com/api/v1/campaigns/123/participants?page=3
https://app.leevia.com/api/v1/whatsapp_campaigns/321/participants?page=13
https://app.leevia.com/api/v1/loyalty_portals/123/participants?page=17
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:
- a list of participants data
- an handle for next and previous pages of participants data
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.
ReplaceJWT_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:
https://app.leevia.com/api/v1/loyalty_portals/456/campaigns
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:
- a list of campaigns data
- an handle for next and previous pages of campaigns data
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:
- Contest is not yet started
- Contest is terminated.
- Limit of participants has been reached
- No more available coupon codes