Affiliate

674 переглядів Markdown

The nine endpoints that open an affiliate partnership, set the commission, run the payout and scan for suspicious referrals.

Overview

In an affiliate partnership a client earns commission on every sale they bring. These endpoints open the partnership, set the commission rule, run the payout and scan for suspicious referrals.

Commission has two periods: lifetime pays on every renewal the referred client makes, onetime only on the first sale. The choice is per partner.

Reference

Reading the Affiliate Status

get/api/v1/admin/clients/{id}/affiliate
Clients/GetClientAffiliate admin

Returns the client's affiliate status, commission settings and balance.

Response fields data — 8
is_affiliateboolWhether the client is an affiliate.
activated_atdatetimeWhen the partnership was opened.
disabledboolWhether the partnership is switched off.
disabled_reasonstringWhy it was switched off.
commission_valuefloatCommission rate as a percentage. Between 0 and 100.
commission_periodstringlifetime pays on every renewal, onetime only on the first sale. An empty value uses the default.
balancefloatThe partner's earnings balance.
currency_idintCurrency id of the balance.
Errors 2
not_found404No such client.
insufficient_scope403The key lacks the required scope.
Request
curl 'https://panel.example.com/api/v1/admin/clients/42/affiliate' \
  -H "Authorization: Bearer $API_KEY"
const res  = await fetch('https://panel.example.com/api/v1/admin/clients/42/affiliate', {
  headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/42/affiliate');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Clients()->GetClientAffiliate(['id' => 42]);

if (!($response['data']['is_affiliate'] ?? false)) {
    return;
}

Opening the Partnership

post/api/v1/admin/clients/{id}/affiliate
Clients/ActivateClientAffiliate admin

Makes the client an affiliate. On a client who already is one, it returns an error.

Body 3
commission_valuenumberCommission rate. Clamped to 0-100; a value outside that is not refused, it is trimmed.
commission_periodstringlifetime or onetime. Anything else becomes empty.
currency_idintBalance currency. List: reference/currencies.
Response fields data
dataobjectThe partnership opened, returned with 201. Same shape as the status endpoint.
Errors 4
not_found404No such client.
already_affiliate422The client is already an affiliate.
activate_failed500The partnership could not be opened.
insufficient_scope403The key lacks the required scope.
Request
curl -X POST 'https://panel.example.com/api/v1/admin/clients/42/affiliate' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"commission_value":10,"commission_period":"lifetime","currency_id":1}'
const res = await fetch('https://panel.example.com/api/v1/admin/clients/42/affiliate', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    commission_value: 10,
    commission_period: 'lifetime',
    currency_id: 1,
  }),
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/42/affiliate');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'commission_value'  => 10,
        'commission_period' => 'lifetime',
        'currency_id'       => 1,
    ]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Clients()->ActivateClientAffiliate([
    'id'                => 42,
    'commission_value'  => 10,
    'commission_period' => 'lifetime',
    'currency_id'       => 1,
]);

Updating the Partnership

patch/api/v1/admin/clients/{id}/affiliate
Clients/UpdateClientAffiliate admin blocking lives here

Changes the commission, the balance and the partner's state. Blocking goes through here too.

Body 7
commission_valuenumberCommission rate; clamped to 0-100.
commission_periodstringlifetime ya da onetime.
currency_idintBalance currency.
balancenumberThe partner's balance. A negative value is pulled up to zero.
disabledboolSwitches the partnership off or back on.
disabled_reasonstringWhy it was switched off. Only written while disabled is on.
block_partnerboolBlocks the partner and cancels the waiting withdrawal requests.
Response fields data
dataobjectThe partnership as it now stands. Same shape as the status endpoint.
Errors 3
not_found404No such client.
not_affiliate422The client is not an affiliate.
insufficient_scope403The key lacks the required scope.
Request
curl -X PATCH 'https://panel.example.com/api/v1/admin/clients/42/affiliate' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"commission_value":15}'
const res = await fetch('https://panel.example.com/api/v1/admin/clients/42/affiliate', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ commission_value: 15 }),
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/42/affiliate');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'PATCH',
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode(['commission_value' => 15]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// Blocking also cancels the waiting withdrawal requests.
$response = Api::Clients()->UpdateClientAffiliate([
    'id'            => 42,
    'block_partner' => true,
    'block_reason'  => 'Fake referrals',
]);

Running a Fraud Check

post/api/v1/admin/clients/{id}/affiliate/fraud-check
Clients/CheckClientAffiliateFraud admin

Scans the partner's referrals and returns the suspicious patterns.

Body —
——No body is needed. The scan always covers the partner's whole referral history and cannot be narrowed.
Response fields data — 3
flaggedboolWhether the partner was flagged.
self_referralsintHow many referrals the partner made to themselves.
shared_ip_referralsarrayReferrals coming from the same IP as the partner.
Errors 3
not_found404No such client.
not_affiliate422The client is not an affiliate.
insufficient_scope403The key lacks the required scope.
Request
curl -X POST 'https://panel.example.com/api/v1/admin/clients/42/affiliate/fraud-check' \
  -H "Authorization: Bearer $API_KEY"
const res = await fetch('https://panel.example.com/api/v1/admin/clients/42/affiliate/fraud-check', {
  method: 'POST',
  headers: { Authorization: `Bearer ${apiKey}` },
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/42/affiliate/fraud-check');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Clients()->CheckClientAffiliateFraud(['id' => 42]);

// The scan only reports; blocking is your decision and your call.
if ($response['data']['flagged'] ?? false) {
    $selfReferrals = $response['data']['self_referrals'];
}

Listing Withdrawal Requests

get/api/v1/admin/clients/{id}/affiliate/withdrawals
Clients/GetClientAffiliateWithdrawals admin

Returns the requests the partner opened to withdraw their earnings.

Response fields data[] — 6
idintId of the withdrawal request.
amountfloatThe amount asked for.
gatewaystringThe method the payment goes out through.
statusstringawaiting, process, completed, rejected or cancelled.
status_msgstringA note on the status.
created_atdatetimeWhen the request was opened.
Errors 3
not_found404No such client.
not_affiliate422The client is not an affiliate.
insufficient_scope403The key lacks the required scope.
Request
curl 'https://panel.example.com/api/v1/admin/clients/42/affiliate/withdrawals' \
  -H "Authorization: Bearer $API_KEY"
const res  = await fetch('https://panel.example.com/api/v1/admin/clients/42/affiliate/withdrawals', {
  headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/42/affiliate/withdrawals');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Clients()->GetClientAffiliateWithdrawals(['id' => 42]);

Updating a Withdrawal Request

patch/api/v1/admin/clients/{id}/affiliate/withdrawals/{wid}
Clients/UpdateClientAffiliateWithdrawal admin a receipt can be attached

Changes the request status. If you made the payment, the receipt goes in the same request.

Body 4
statusstringrequiredThe new status. Older names are accepted: pending → awaiting, inprocess → process, paid → completed.
status_msgstringA message for the partner.
receiptstring | objectThe payment receipt. Accepted only on the move to completed. A base64 data URI, {filename, content} or {url}. Allowed: images and PDF.
remove_receiptboolRemoves the receipt already attached.
Response fields data
dataobjectThe request as it now stands. Same shape as an item in the request list.
Errors 4
not_found404No such client or withdrawal request.
status_required422status was empty.
file_invalid422The receipt could not be read or stored.
insufficient_scope403The key lacks the required scope.
Request
curl -X PATCH 'https://panel.example.com/api/v1/admin/clients/42/affiliate/withdrawals/5' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"status":"completed","receipt":{"url":"https://example.com/receipt.pdf"}}'
const res = await fetch('https://panel.example.com/api/v1/admin/clients/42/affiliate/withdrawals/5', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    status: 'completed',
    receipt: { url: 'https://example.com/receipt.pdf' },
  }),
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/42/affiliate/withdrawals/5');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'PATCH',
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'status'  => 'completed',
        'receipt' => ['url' => 'https://example.com/receipt.pdf'],
    ]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Clients()->UpdateClientAffiliateWithdrawal([
    'id'      => 42,
    'wid'     => 5,
    'status'  => 'completed',
    'receipt' => ['url' => 'https://example.com/receipt.pdf'],
]);

Listing Canned Texts

get/api/v1/admin/clients/affiliate/templates
Clients/GetAffiliateTemplates admin

Returns the canned texts used in affiliate work: deactivation reasons, withdrawal notes, block reasons.

Response fields data — 3
deactivate_reasonsstring[]Reasons for closing a partnership.
withdrawal_notesstring[]Notes put on a withdrawal request.
block_reasonsstring[]Reasons for blocking.
Errors 1
insufficient_scope403The key lacks the required scope.
Request
curl 'https://panel.example.com/api/v1/admin/clients/affiliate/templates' \
  -H "Authorization: Bearer $API_KEY"
const res  = await fetch('https://panel.example.com/api/v1/admin/clients/affiliate/templates', {
  headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/affiliate/templates');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Clients()->GetAffiliateTemplates();

Adding a Canned Text

post/api/v1/admin/clients/affiliate/templates
Clients/AddAffiliateTemplate admin

Adds a text to the type you choose.

Body 2
typestringrequiredMetnin türü: deactivate_reasons, withdrawal_notes or block_reasons.
valuestringrequiredThe text to add.
Response fields data — 2
typestringThe type the text went into.
itemsstring[]That type's list as it now stands.
Errors 3
type_invalid422The type is not one of the three values.
value_required422value was empty.
insufficient_scope403The key lacks the required scope.
Request
curl -X POST 'https://panel.example.com/api/v1/admin/clients/affiliate/templates' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"type":"block_reasons","value":"Fake referrals detected"}'
const res = await fetch('https://panel.example.com/api/v1/admin/clients/affiliate/templates', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ type: 'block_reasons', value: 'Fake referrals detected' }),
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/affiliate/templates');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'type'  => 'block_reasons',
        'value' => 'Fake referrals detected',
    ]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Clients()->AddAffiliateTemplate([
    'type'  => 'block_reasons',
    'value' => 'Fake referrals detected',
]);

Deleting a Canned Text

delete/api/v1/admin/clients/affiliate/templates
Clients/DeleteAffiliateTemplate admin deleted by text

Removes a text from the list. You send the text itself, not an id.

Body 2
typestringrequiredMetnin türü: deactivate_reasons, withdrawal_notes or block_reasons.
valuestringrequiredThe text to remove. It has to match the stored one exactly.
Response fields data — 2
typestringThe type the text came out of.
itemsstring[]That type's list as it now stands.
Errors 3
type_invalid422The type is not one of the three values.
value_required422value was empty.
insufficient_scope403The key lacks the required scope.
Request
curl -X DELETE 'https://panel.example.com/api/v1/admin/clients/affiliate/templates' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"type":"block_reasons","value":"Fake referrals detected"}'
const res = await fetch('https://panel.example.com/api/v1/admin/clients/affiliate/templates', {
  method: 'DELETE',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ type: 'block_reasons', value: 'Fake referrals detected' }),
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/affiliate/templates');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'DELETE',
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'type'  => 'block_reasons',
        'value' => 'Fake referrals detected',
    ]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Clients()->DeleteAffiliateTemplate([
    'type'  => 'block_reasons',
    'value' => 'Fake referrals detected',
]);

Pitfalls

Values are trimmed, not refused

The commission rate is clamped to 0-100, a negative balance is pulled up to zero, and an unrecognised commission period becomes empty. None of these raise an error; a wrong value is quietly stored as a corrected one. Read back after writing.

Blocking cancels waiting payouts

block_partner does not only stop the partner; it also cancels the waiting withdrawal requests. Look at the request list before blocking a partner who is about to be paid.

The fraud scan makes no decision

The scan only reports: how many self-referrals there are and which records came from the same IP. Blocking the partner is a separate request and the decision is yours.

Ця стаття була корисною?

Дякуємо за відгук!

Досі потрібна допомога?

Наша служба підтримки на зв’язку цілодобово з усього, чого ви не знайшли вище.