Using API

Postback

Parameters in the link

    &web_id       - The affiliate's ID in your system (if you have one); maximum length - 100 symbols.
    &sub1         - additional data in any format; maximum length - 100 symbols.
    &sub2         - additional data in any format; maximum length - 100 symbols.
    &sub3         - additional data in any format; maximum length - 100 symbols.
    &sub4         - additional data in any format; maximum length - 100 symbols.
    &sub5         - additional data in any format; maximum length - 100 symbols.
    &utm_source   - maximum length - 100 symbols.
    &utm_medium   - maximum length - 100 symbols.
    &utm_campaign - maximum length - 100 symbols.
    &utm_content  - maximum length - 100 symbols.
    &utm_term     - maximum length - 100 symbols.
                    
    When a system has a new offer and its status changes, our system can notify a third-party system about it,
    by sending a HTTP request.
    Request can be sent with the help of POST or GET method (based on the profile settings),
    but data is transferred in URL (consequently, it's received in $_GET).

    Our system will send postback every minute during a week until it receives
    HTTP status 200.

    Postback can be active no more than 3 times - when a new order is created, when the status is changed to confirmed, held, canceled
    or trash and when it's changed from held to confirmed.

    Global URLs of postbacks are specified on cpa.tl administrator panel Traffic Light on profile page.
    You can also specify postbacks for each stream individually.

    You can specify a special URL status; if URL isn't specified for "trash", URL for
    "canceled" will be used.

    If there's no URL for confirmed or canceled, there won't be postback for these statuses.

    Information about the attempts to send postbacks is stored for 30 days.

    URL can include macros:
      {id}                  - Order ID in our system
      {id2}                 - Order ID in our system(if we received an order via API)
      {created_at}          - order creation time in ISO format (for example, '2015-06-12T17:50:39+00:00'), always UTC
      {created_at_unixtime} - order creation time in the form of POSIX timestamp (for example, 1434131439)
      {status}              - options new|cancelled|holded|confirmed
      {status_code}         - options 0|-10|5|10
      {status_id}           - 2 - new, 1 - confirmed, 4 - confirmed in hold, 3 - canceled
      {payout}              - payout for an affiliate for an order
      {payout_currency}     - the currency in which an affiliate gets funds in (for example, 'USD', 'EUR', 'RUB')
      {offer_id}            - offer's id in our system
      {country}             - client's country (for example, 'RU')
      {ip_address}          - client's IP address (let's say '176.59.124.152')

      {trash}               - numeric code if it's a trash order
      {comment}             - comment (as a rule, used in case of refusal); can be empty if a call center doesn't provide refusal comments
                              * trash description (in case of trash)

      {sub1}                - from a corresponding parameter in a link
      {sub2}                - from a corresponding parameter in a link
      {sub3}                - from a corresponding parameter in a link
      {sub4}                - from a corresponding parameter in a link
      {sub5}                - from a corresponding parameter in a link
      {web_id}              - from a corresponding parameter in a link

      {utm_source}          - from a corresponding parameter in a link
      {utm_medium}          - from a corresponding parameter in a link
      {utm_campaign}        - from a corresponding parameter in a link
      {utm_content}         - from a corresponding parameter in a link
      {utm_term}            - from a corresponding parameter in a link

      {fee}                 - the same as "payout" (outdated)
      {subaccount}          - the same as "sub1" (outdated)
      {ex}                  - the same as "sub2" (outdated)
      {extra}               - the same as "sub2" (outdated)

      Status (status):
      new - processing
      cancelled - canceled
      holded - confirmed as hold
      confirmed - confirmed

      Status code (status_code):
      0 - processing
      -10 - canceled
      5 - confirmed as hold
      10 - confirmed

    Example of URL postback:

    http://example.com/api/orders/postback/?o={id}&click_id={sub1}&offer={offer_id}&money={payout}&s={status}

                    

Lead receiver


      POST http://api.cpa.tl/api/lead/send

      Request is sent to this URL with the help of POST method, message body contains data:

        "key": string                       - API access key, get it in a profile  https://cpa.tl/u/profile
        "id": string                          - Lead ID in your system
        "offer_id": int                    - offer's id in our system
        "stream_hid": string        - Stream id in our system
        "ip_address": string         - Lead IP address
        "name": sting                     - Client's name and surname
        "phone": string                 - Client's phone number
        "comments": string          - Comment on the order
        "country": string               - Client's country
        "address":string                - Client's address
        "tz": int                                - time zone, for example, GMT +3 for Europe/Moscow (how to get a time zone?)
        "web_id": string                - affiliate ID in your system
        "email": string                   - client's email
        "ip_address": string
        "user_agent": string

        "utm_source": string(100)
        "utm_medium": string(100)
        "utm_campaign": string(100)
        "utm_content": string(100)
        "utm_term": string(100)

        "sub1": string(255)
        "sub2": string(255)
        "sub3": string(255)
        "sub4": string(255)
        "sub5": string(255)

      If successful, the response will be HTTP 200, in json format, and will contain the following data:
"id" - lead id in our system;
"autologin_url" (optional) - link to the broker's website auto-login, if the offer category supports this feature;
"error" (optional) - error message if the request could not be successfully sent to the advertiser.
      Examples of a successful response:
          {"id": 123}
          {"id": 123, "autologin_url": "https://example.click?token=kfjdgksjd"}
      Example of a response with an error:
          {"id": 123, "autologin_url": "", "error": "Lead sending error to the advertiser"}
      required fields:
      key, offer_id, phone, country, ip_address

      it'd be very desirable to specify this for additional traffic control:
      name, web_id, user_agent

    In case of an error, HTTP 409 will be sent as an aswer, in json format, it will contain error status, for example:
    {"errmsg": "country is a required field"}

    Possible error statuses
    offer not found                                     - offer not found or no GEO matches the country field
    incorrect IP-address                            - wrong IP address
    country is a required field                 - 'country' field is required for saving an order
    incorrect country code; should be ISO2 or ISO3 format - wrong country code in a country field
    customer phone is a required field - 'phone' field is required for saving an order
    incorrect customer phone                  - wrong phone number
    customer name too long                    - 'name' field is too long
    wrong tz {}                                               - tz (timezone) field wrong format
    incorrect timezone                               - tz (timezone) field wrong format
    duplicate order [ {} ]                             - order double
    

Php example ( download ):

        <?php

        $apiKey = 'xxxxxxxxxxxxxxxxxxxxxxx';
        $offer_id = 0; // every offer has its own ID, clarify it on the administrator panel or contact the support
        $stream_hid = ''; // not required, if an order will be linked to a stream
        $apiUrl = 'http://api.cpa.tl/api/lead/send';

        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
            $data_post = $_POST;

            $data = array(
                    'key' => $apiKey,
                    'id' => microtime(true), // it's better to specify the value that will help you to identify your lead; it can remain microtime if you have no CRM
                    'offer_id' => $offer_id,
                    'stream_hid' => $stream_hid,
                    'name' => $data_post['name'],
                    'phone' => $data_post['phone'],
                    'comments' => $data_post['comments'],
                    'country' => $data_post['country'], // format ISO 3166-1 Alpha-2 - https://ru.wikipedia.org/wiki/ISO_3166-1
                    'address' => $data_post['address'],
                    'tz' => $data_post['timezone_int'], // it's desirable to take it from a landing page, but if it's impossible, leave this field empty or enter 3 (Moscow time zone)
                    'web_id' => '',
                    'ip_address' => isset($_SERVER["HTTP_CF_CONNECTING_IP"]) ? $_SERVER["HTTP_CF_CONNECTING_IP"] : $_SERVER['REMOTE_ADDR'],
                    'user_agent' => $_SERVER['HTTP_USER_AGENT'],
            );

            $options = array(
                    'http' => array(
                        'header' => "Content-type: application/x-www-form-urlencoded\r\n",
                        'method' => 'POST',
                        'content' => http_build_query($data),
                        'ignore_errors' => true,
                    )
            );

            $context = stream_context_create($options);
            $result = file_get_contents($apiUrl, false, $context);

            $obj = json_decode($result);

            if (null === $obj) {
                    // Error in an answer
                    print("Invalid JSON");
                } else if (!empty($obj->errmsg)) {
                    // Error in a request
                    print("Error: " . $оbj->errmsg);
                } else {
                    print('Order ID: ' . $obj->id);
            }

        }

        
Python example ( download ):
    import time
    import requests

    API_KEY = 'xxxxxxxxxxxxxxxxxxxxxxxxx'
    API_URL = 'http://api.cpa.tl/api/lead/send'
    offer_id = 0    # offer ID in our system, every offer has its own ID, clarify it on the administrator panel or contact the support
    stream_hid = '' # not required, if an order will be linked to a stream

    data = {
        'key': API_KEY,
        'id': int(round(time.time() * 1000)),    # it's better to specify the value that will help you to identify your lead
        'offer_id': offer_id,  # every offer has its own ID, clarify it on the administrator panel or contact the support
        'stream_hid': stream_hid,
        'name': 'tester (refuse)',
        'phone': '8 999 1111122',
        'comments': 'test lead',
        'country': 'RU', # format ISO 3166-1 Alpha-2 - https://ru.wikipedia.org/wiki/ISO_3166-1
        'address': '',
        'tz': 3,  # it's desirable to take it from a landing page, but if it's impossible, leave this field empty or enter 3 (Moscow time zone)
        'web_id': '',
        'ip_address': '194.58.117.14',   # ip of the order. in Django, you can get request.META.get('REMOTE_ADDR')
        'user_agent': '',   # in Django, you can get HTTP_USER_AGENT
    }


    r = requests.post(API_URL, data=data)
    if r.status_code == 200:
        lead_id = r.json().get('id')
        print('Order ID: {}'.format(lead_id))
    elif r.status_code == 404:
        print('check API_KEY, API_URL, offer_id')
    elif r.status_code == 409:
        err_mess = r.json().get('errmsg')
        print(err_mess)

    

How to get a time zone :

    You can get client's time zone with the help of javascript and specify it in the hidden field of the form

    <input id="tz" name="timezone_int" type="hidden" value="" />

    <script>
        $(document).ready(function () {
            $("#tz").val(new Date().getTimezoneOffset() / -60);
        });
    </script>
    

Lead feed

    GET http://api.cpa.tl/api/lead/feed?key=<key>&[filters]

      provides a list of leads in json format.

      <key> - API access key, get it in a profile https://cpa.tl/u/profile

      possible filters:
       - id  - search by lead ID in our system
       - id2 - search by lead ID in your system
       - offset - download shift (if no info = 0)

      You can check several leads at the same time by specifying several id or id2, for example
         - id2[]=1&id2[]=2&id2[]=3&id2[]=N

       - date - if 1 value is specified - leads for this date; if 2 - all leads for the specified period (the date ‘before’ is not included in the sample).
         period (the date ‘before’ is not included in the sample). For example:
         - date=2024-06-01 - see leads for June 1st, 2024
         - date[]=2024-06-01&date[]=2024-07-01 - see leads for June
       - date_to_with - if true, the "before" date will be included in the selection. For example:
         - date[]=2024-06-01&date[]=2024-06-30&date_to_with=true - see leads for June
       - by_status_changed_at - if the parameter is true, filtering will be carried out by
         last status change date, not by creation date.

      Answer description:
        count - number of leads in the answer
        total - total number of leads based on requested filters
        offset - current download shift
        limit - requested number of leads
        leads - the list of leads

      Lead field description:
        "status_code": int          - 0 - new, 10 - confirmed, 5 - confirmed in hold, -10 - canceled
        "status": string                    - "new" - new, "confirmed" - confirmed, "holded" - confirmed in hold, "cancelled" - canceled
        "status_id": int            - 2 - new, 1 - confirmed, 4 - confirmed in hold, 3 - canceled
        "trash": int or null       - numeric code if it's a trash order
        "comment": string or null          - comment (for example, failed to contact a client), as a rule, used in case of a refusal; it can be empty if a call center doesn't provide refusal comments
                                              * trash description (in case of trash)
        "payout": string                    - payout for an affiliate for an order (for example, '500.00' или '90')
        "payout_currency": string           - the currency in which an affiliate gets funds in (for example, 'USD', 'EUR', 'RUB')
        "created_at": string                - order creation time in ISO format (for example, '2015-06-12T17:50:39+00:00'), always UTC
        "created_at_unixtime": int  - order creation time in the form of POSIX timestamp (for example, 1434131439)
        "id": 43179                         - lead/order ID in our system
        "id2": 205                          - lead/order ID in your system (it may be 'null')
        "offer_id": 5                       - offer's id in our system
        "country": string                   - client's country (for example, 'RU')
        "ip_address": string                - client's IP address (let's say '176.59.124.152')
        "order_page": string                - Order page URL address

        "utm_source"                        - value specified when creating a lead
        "utm_medium"                        - value specified when creating a lead
        "utm_campaign"                      - value specified when creating a lead
        "utm_content"                       - value specified when creating a lead
        "utm_term"                          - value specified when creating a lead
        "web_id"                            - value specified when creating a lead

        "sub1"                              - value specified when creating a lead
        "sub2"                              - value specified when creating a lead
        "sub3"                              - value specified when creating a lead
        "sub4"                              - value specified when creating a lead
        "sub5"                              - value specified when creating a lead

        "fee"                               - the same as "payout" (устаревшее)
        "subaccount"                        - the same as "sub1" (устаревшее)
        "ex"                                - the same as "sub2" (устаревшее)
        "extra"                             - the same as "sub2" (устаревшее)
        "trash_reason"                      - the same as "trash" (устаревшее)
    

Php example ( download ):

    <?php

    $apiKey = 'xxxxxxxxxxxxxxxxxxxxxxxxx';
    $apiUrl = 'http://api.cpa.tl/api/lead/feed';

    $data = array(
        'key' => $apiKey,

        // setting up the filters. You can use one or several filters
        // getting ID data in our system
        'id' => ['xxxxxx', 'xxxxxx'],

        // getting ID data in your system
        //'id2' => ['xxxxxx', 'xxxxxx'],

        // getting all orders for this date
        //'date' => ['2018-07-01', '2018-07-15'],

    );

    function http_build_query_noindex($a, $b=0, $c=0){
        if (!is_array($a)) { return false; }
        foreach ((array)$a as $k=>$v){
            if ($c) { $k = $b."[]"; }
            elseif (is_int($k)) { $k = $b . $k; }
            if (is_array($v)||is_object($v)) {
                $r[]=http_build_query_noindex($v, $k, 1);
                continue;
            }
            $r[] = urlencode($k) . "=" . urlencode($v);
        }
        return implode("&", $r);
    }

    $result = file_get_contents($apiUrl.'?'.http_build_query_noindex($data, ''));

    $obj = json_decode($result);

     if (null === $obj) {
        // Error in an answer
        echo "Invalid JSON";
    } elseif (!empty($jsonObj->errmsg)) {
        // Error in a request
        echo "Error: " . $jsonObj->errmsg;
    } else {
        print('Orders received: ' . $obj->count . " \n
"); foreach ($obj->leads as $lead) { print($lead->id . ' - ' . $lead->status. " \n
"); } }
Python example ( download ):
    import datetime
    import requests

    API_KEY = 'xxxxxxxxxxxxxxxxxxxxxxxxx'
    API_URL = 'http://api.cpa.tl/api/lead/feed'

    data = {
        'key': API_KEY,
        # forming filters
        # 'id': 'xxxxxx', # getting data on an order based on ID in our system
        # 'id': ['xxxxxx', 'xxxxxx', ], # getting data on several orders based on ID in our system
        # 'id2': 'xxxxxx',   # getting data on an order based on ID in your system
        # 'id2': ['xxxxxx', 'xxxxxx', ], # getting data on several orders based on ID in our system
        # 'date': '2018-07-15', # getting all orders for this date
        # 'date': datetime.date.today().strftime('%Y-%m-%d'),  # getting today's orders
        'date': ['2018-07-01', '2018-07-15', ],   # getting orders placed from July, 1st to July, 15th, 2018
    }

    r = requests.get(API_URL, params=data)

    if r.status_code == 200:
        data = r.json()
        print('received {} orders'.format(data.get('count')))

        for lead in data.get('leads'):
            lead_id = lead.get('id2')   # Lead ID in your system
            status_code = lead.get('status')

            if status_code == 'new':
                status = 'new'
            elif status_code == 'confirmed':
                status = 'confirmed'
            elif status_code == 'new|cancelled':
                status = 'canceled'
            else:
                status = '--'
            print('Order {} status {}'.format(lead_id, status))

    elif r.status_code == 404:
        print('check API_KEY, API_URL, offer_id')
    elif r.status_code == 409:
        err_mess = r.json().get('errmsg')
        print(err_mess)

    

Balance

    GET http://api.cpa.tl/api/user/balance?key=<key>

      provides information about the amount of funds in all currencies in json format.

      <key> - API access key, get it in a profile https://cpa.tl/u/profile

      answer description:
        currency - currency, may be provided in: 'RUB', 'USD', 'EUR', 'COIN'

        {
            currency: {
                "total": 0,         - total profit
                "payment": 0,       - payouts made
                "earn": 0,          - deposited
                "hold": 0,          - hold
                "balance": 0,       - account balance
            }
        }