Merchant integration guide
affilink attributes a sale to an affiliate through one server-to-server call your site makes when an order completes. There is no pixel and no client-side fallback - every reported conversion is cryptographically signed, which is what keeps the numbers trustworthy for everyone. Four steps get you there.
How the flow works
- An affiliate shares their tracking link:
https://go.affilink.co.il/r/{code} - A visitor clicks it. affilink records the click and redirects to your product page with
?aff_click={click_id}appended. - Your site stores that
aff_clickvalue and keeps it through checkout (steps 1-3 below). - On order completion, your server calls the Postback API with the stored
click_id(step 4 below).
Step 1 - Capture aff_click on landing
The redirect lands the visitor on your page with ?aff_click=...
in the URL - but only for that single page view. A small site-wide snippet
must read it and set a first-party cookie on your own domain
(affilink's domain cannot set a cookie your checkout will ever see):
// Site-wide snippet (or equivalent server-side middleware)
(function () {
var m = new URLSearchParams(location.search).get("aff_click");
if (m) {
var days = 30; // MUST match the offer's attribution window
document.cookie =
"_affilink_click=" + encodeURIComponent(m) +
"; max-age=" + days * 86400 +
"; path=/; SameSite=Lax; Secure";
}
})(); Step 2 - Match the cookie's lifetime to the attribution window
Buyers often don't purchase on the first visit. If the cookie expires
before the offer's attribution window does (default: 30 days), a real,
in-window conversion loses its click_id and can never be
attributed - the affiliate silently loses a commission they earned. Set
days above to the offer's configured window.
Step 3 - Let it ride through checkout
Nothing to do here: first-party cookies are sent automatically with every request to your domain, including the final order-confirmation request - as long as step 1 set the cookie correctly.
Step 4 - Report the completed order, server-side
At whatever moment your backend considers an order "done", read the
cookie from the incoming request, build the JSON payload, sign it, and
POST it. The webhook_secret stays server-side only - an
environment variable or secret store, never in browser code.
Node.js
import crypto from "node:crypto";
async function reportConversion(clickId, order) {
const body = JSON.stringify({
offer_id: process.env.AFFILINK_OFFER_ID,
click_id: clickId, // from the _affilink_click cookie
external_order_id: order.id, // your own order id - idempotency key
event_type: "conversion",
amount: order.total, // major units, e.g. 149.90
currency: order.currency, // ISO 4217, e.g. "ILS"
});
const timestamp = Math.floor(Date.now() / 1000).toString();
const signature = crypto
.createHmac("sha256", process.env.AFFILINK_WEBHOOK_SECRET)
.update(`${timestamp}.${body}`)
.digest("hex");
const res = await fetch("https://api.affilink.co.il/v1/postback", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Affilink-Timestamp": timestamp,
"X-Affilink-Signature": signature,
},
body,
});
return res.json(); // { conversion_id, status, affiliate_commission, platform_fee }
} PHP / WooCommerce
// functions.php or a small mu-plugin.
// Secrets belong in wp-config.php:
// define('AFFILINK_OFFER_ID', 'off_...');
// define('AFFILINK_WEBHOOK_SECRET', '...');
add_action('woocommerce_order_status_completed', function ($order_id) {
$click_id = isset($_COOKIE['_affilink_click'])
? sanitize_text_field($_COOKIE['_affilink_click'])
: null;
if (!$click_id) return; // not an affiliate-referred order
$order = wc_get_order($order_id);
$body = wp_json_encode([
'offer_id' => AFFILINK_OFFER_ID,
'click_id' => $click_id,
'external_order_id' => (string) $order_id,
'event_type' => 'conversion',
'amount' => (float) $order->get_total(),
'currency' => $order->get_currency(),
]);
$timestamp = (string) time();
$signature = hash_hmac('sha256', $timestamp . '.' . $body, AFFILINK_WEBHOOK_SECRET);
wp_remote_post('https://api.affilink.co.il/v1/postback', [
'headers' => [
'Content-Type' => 'application/json',
'X-Affilink-Timestamp' => $timestamp,
'X-Affilink-Signature' => $signature,
],
'body' => $body,
'timeout' => 10,
]);
}); woocommerce_checkout_create_order) and read it
from the order meta here instead.
Refunds
Send the same payload with "event_type": "refund" and the
same external_order_id. affilink reverses the conversion and
claws back the commission automatically.
Retry policy
- Retry with exponential backoff on
5xxonly. Safe becauseexternal_order_idmakes the call idempotent - a retry can never double-count. - Never retry on
4xx- those are permanent rejections (bad signature, expired window, unknown click). Log them and investigate.
Testing your integration
- Create a test tracking link for your offer and click it yourself - you'll land on your site with
?aff_click=.... - Verify the
_affilink_clickcookie was set (browser dev tools → Application → Cookies). - Place a test order and confirm you get a
200with aconversion_idback. - Send the same call again - you should get a
409with the sameconversion_id(idempotency working).
Full endpoint contract, error codes, subscriptions and CRM/staged conversions: Postback API reference.