affilink

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.

Using WooCommerce? A dedicated plugin that automates all four steps is planned. Until it ships, the PHP snippets below implement the same thing directly - about 30 lines total.

How the flow works

  1. An affiliate shares their tracking link: https://go.affilink.co.il/r/{code}
  2. A visitor clicks it. affilink records the click and redirects to your product page with ?aff_click={click_id} appended.
  3. Your site stores that aff_click value and keeps it through checkout (steps 1-3 below).
  4. 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,
    ]);
});
Note for WooCommerce: the order-completion hook runs in a context that has the customer's cookies only when triggered by the customer's own request flow. If your orders complete asynchronously (e.g. a payment webhook), save the cookie value onto the order as meta at checkout time (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

Testing your integration

  1. Create a test tracking link for your offer and click it yourself - you'll land on your site with ?aff_click=....
  2. Verify the _affilink_click cookie was set (browser dev tools → Application → Cookies).
  3. Place a test order and confirm you get a 200 with a conversion_id back.
  4. Send the same call again - you should get a 409 with the same conversion_id (idempotency working).

Full endpoint contract, error codes, subscriptions and CRM/staged conversions: Postback API reference.