Affiliate Partners

8 views Markdown

The four endpoints that see, search, switch and remove the partners in the program.

Overview

The affiliate program is where a client earns commission on the customers their own link brings in. This article manages the people in that program: who is there, who is on and who goes.

Every record carries two numbers. id belongs to the partner record and owner_id to the client behind it. The assignment endpoints speak the client number, so mixing the two makes a quiet wrong assignment.

The search endpoint sits here because it feeds the assignment screens: an interface that does not know the number types a name first, then hands the number it gets to the assignment.

Reference

Listing the Partners

get/api/v1/admin/affiliates
Affiliates/GetAffiliates admin

Returns the partners in the program with their earnings and referral totals.

Query 4
pageintWhich page.
limitintRecords per page. 100 at the most.
searchstringSearches the name, company, e-mail, partner id and client id.
statusstringThe status filter: active or inactive.
Response fields data[] — 13 + meta — 4
idintThe partner record id.
owner_idintThe client behind the partner. This is the number assignments use.
user_idintThe same client id. A second name coming from the query join.
full_namestringThe client's name.
company_namestringThe company name.
emailstringThe e-mail address.
disabledintWhether it is off. 0 means on and 1 means off.
balancestringThe earnings waiting to be paid. Text with four decimals.
currencyintThe currency id of the earnings.
referralsintHow many clients they referred.
hitsintHow many clicks their link took.
total_paidstringWhat has been paid out so far. It counts completed requests alone.
datestringWhen they joined the program.
totalintHow many partners there are. It comes back under meta.
pageintThe page you are on.
limitintThe page size.
next_pageintThe next page. Zero on the last one.
Errors 1
insufficient_scope403The key lacks the required scope.
Request
curl 'https://panel.example.com/api/v1/admin/affiliates?status=active&limit=50' \
  -H "Authorization: Bearer $API_KEY"
const res = await fetch('https://panel.example.com/api/v1/admin/affiliates?status=active', {
  headers: { Authorization: `Bearer ${apiKey}` },
});

const { data } = await res.json();
const owing = data.filter((a) => Number(a.balance) > 0);
$ch = curl_init('https://panel.example.com/api/v1/admin/affiliates?status=active');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// id belongs to the PARTNER and owner_id to the CLIENT behind it; assignments speak owner_id.
$rows = Api::Affiliates()->GetAffiliates([], ['status' => 'active'])['data'];
$byClient = array_column($rows, 'balance', 'owner_id');

Searching Partners, Clients and Services

get/api/v1/admin/affiliates/select
Affiliates/SelectForAffiliate admin 10 results at most

The search behind the assignment screens; you type a name instead of an id.

Query 2
typestringWhat to look for: affiliate, client or service. It looks for a partner by default.
qstringThe search text. It is taken under the name search as well.
Response fields data — 2
typestringThe kind searched. The same one you sent.
resultsobject[]What was found.
idintThe id of the record found.
textstringThe label to show. A service gets its id and name, a person their name and company.
Errors 1
insufficient_scope403The key lacks the required scope.
Request
curl 'https://panel.example.com/api/v1/admin/affiliates/select?type=client&q=jane' \
  -H "Authorization: Bearer $API_KEY"
const url = new URL('https://panel.example.com/api/v1/admin/affiliates/select');
url.searchParams.set('type', 'client');
url.searchParams.set('q', term);

const res  = await fetch(url, { headers: { Authorization: `Bearer ${apiKey}` } });
const body = await res.json();
$qs = http_build_query(['type' => 'client', 'q' => $term]);
$ch = curl_init('https://panel.example.com/api/v1/admin/affiliates/select?' . $qs);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// It returns 10 results at most: this is a search box and not a listing.
$hits = Api::Affiliates()->SelectForAffiliate([], ['type' => 'client', 'q' => $term])['data'];
$exact = count($hits['results']) === 1 ? $hits['results'][0]['id'] : 0;

Turning a Partner On and Off

put/api/v1/admin/affiliates/{aid}/status
Affiliates/SetAffiliateStatus admin

Turns a partner on or takes them out of the program.

Body 1
enabledboolreqWhether the partner is on. It becomes the opposite-named field on the record.
Response fields data — 2
idintThe partner id.
enabledboolWhere it now stands.
Errors 2
not_found404No such partner.
insufficient_scope403The key lacks the required scope.
Request
curl -X PUT 'https://panel.example.com/api/v1/admin/affiliates/7/status' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"enabled":false}'
const res = await fetch(`https://panel.example.com/api/v1/admin/affiliates/${aid}/status`, {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ enabled: false }),
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/affiliates/' . $aid . '/status');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode(['enabled' => false]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// Turning a partner off DOES NOT wipe the earnings: the balance stays and payouts can still be asked for.
Api::Affiliates()->SetAffiliateStatus(['aid' => $aid, 'enabled' => false]);

Removing a Partner

delete/api/v1/admin/affiliates/{aid}
Affiliates/DeleteAffiliate admin cannot be undone

Removes a partner along with every record tied to them.

Response fields data — 2
deletedboolWhether the delete ran.
idintThe id of the partner removed.
Errors 2
not_found404No such partner.
insufficient_scope403The key lacks the required scope.
Request
curl -X DELETE 'https://panel.example.com/api/v1/admin/affiliates/7' \
  -H "Authorization: Bearer $API_KEY"
const res = await fetch(`https://panel.example.com/api/v1/admin/affiliates/${aid}`, {
  method: 'DELETE',
  headers: { Authorization: `Bearer ${apiKey}` },
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/affiliates/' . $aid);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'DELETE',
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// The panel asks for an admin password here while the API needs the scope alone. Consider turning them off first.
Api::Affiliates()->SetAffiliateStatus(['aid' => $aid, 'enabled' => false]);
// Api::Affiliates()->DeleteAffiliate(['aid' => $aid]);   // the earnings history goes as well

Pitfalls

Two numbers: the partner id and the client id

In the listing id is the partner record and owner_id the client behind it. The link kept on the client record also holds the client number rather than the partner one. Keep the two apart, since the assignment and status endpoints want different numbers.

Being off is spoken with two opposite words

The listing gives a disabled field where 0 means on. The status endpoint takes enabled where true means on. They are opposite names for one thing, and copying one into the other flips the state.

Removing takes the earnings history too

Removing a partner also takes the commission entries, the payout requests, the clicks, the history and the referral records, while the linked clients lose their link. The panel asks for an admin password at this step and the API settles for the scope. Turn a partner off rather than remove them when you mean to part ways.

The search endpoint gives ten results at most

The search endpoint is a search box and not a listing: it returns ten results at most no matter how many match, and there is no paging. Resting bulk work on it drops everything past the tenth without a word. Use the listing endpoint for the full set.

The paid total counts completed requests alone

The total_paid field is the sum of completed payout requests. A waiting or in-flight request does not enter that number, and it is not taken out of balance either. Do not answer "what do we owe" by adding the two figures.

Was this helpful?

Thanks for your feedback!

Still Need Help?

Our support team is here around the clock for anything you can't find above.