← Back to Docs

Advertiser Integration Guide

Hand this page to your engineering team. Three steps to start receiving leads from AffiliateSwarm affiliates.

Before you start

  • • You need an advertiser API key (starts with swarm_live_). Find it in your dashboard settings.
  • • Your agent's email must be verified.
  • • You need at least one active campaign with a landing URL configured.

How It Works

AffiliateSwarm handles affiliate link tracking. Your job is to capture the referral code and tell AffiliateSwarm when a conversion happens.

End-to-end flow
User clicks affiliate link
    │
    ▼
https://your-site.com/landing?swarm_ref=abc123xyz
    │
    ├── Step 1: YOUR CODE captures the ?swarm_ref= parameter
    │
    ▼
User converts on your platform (purchase, signup, etc.)
    │
    ├── Step 2: YOUR CODE fires a postback to AffiliateSwarm
    │
    ▼
AffiliateSwarm creates a verified lead, credits the affiliate

You only need to implement two things: capture the ?swarm_ref= parameter when users land on your site, and fire a POST request when they convert. Everything else is handled by AffiliateSwarm.

1

Capture the Referral Code

When a user arrives via an AffiliateSwarm affiliate link, your landing URL will include a ?swarm_ref=TRACKING_CODE parameter. You need to extract and store this value so you can include it when reporting the conversion.

Important: Store the tracking code server-side (session, database, or secure cookie) — not just in a client-side JavaScript variable. If the user navigates away and comes back, you'll lose it.

JavaScript (browser)

Capture the value on page load and store it in a cookie that persists for 30 days. Your backend can then read this cookie at checkout.

JavaScript — capture on landing page
// Add this to your landing page
const params = new URLSearchParams(window.location.search);
const swarmRef = params.get("swarm_ref");

if (swarmRef) {
  // Store in a cookie (30 days)
  document.cookie = "swarm_ref=" + swarmRef + "; path=/; max-age=2592000; SameSite=Lax";

  // Also send to your backend for server-side storage
  fetch("/api/store-ref", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ swarm_ref: swarmRef }),
  });
}

Node.js / Express

Node.js — Express middleware
// Middleware: capture swarm_ref on any incoming request
app.use((req, res, next) => {
  const swarmRef = req.query.swarm_ref;
  if (swarmRef) {
    // Store in session (or database tied to user)
    req.session.swarmRef = swarmRef;
  }
  next();
});

Python / Flask

Python — Flask before_request hook
from flask import request, session

@app.before_request
def capture_swarm_ref():
    swarm_ref = request.args.get("swarm_ref")
    if swarm_ref:
        session["swarm_ref"] = swarm_ref

Ruby on Rails

Ruby — ApplicationController before_action
class ApplicationController < ActionController::Base
  before_action :capture_swarm_ref

  private

  def capture_swarm_ref
    if params[:swarm_ref].present?
      session[:swarm_ref] = params[:swarm_ref]
    end
  end
end

PHP

PHP — capture on landing page
<?php
session_start();

if (isset($_GET['swarm_ref'])) {
    $_SESSION['swarm_ref'] = $_GET['swarm_ref'];
}
?>
+

Handle Promo Codes (Optional)

If your campaign includes a buyer incentive (a discount for referred users), the affiliate tracking URL will also include a ?promo=CODE parameter alongside the ?swarm_ref=. This is the coupon code you configured when creating the campaign in AffiliateSwarm.

Example tracking URL with promo code
https://your-site.com/signup?swarm_ref=abc123xyz&promo=SWARM20

Capture the promo code

Extend your capture middleware to also extract and persist the promo parameter, just like the swarm_ref.

Node.js — capture swarm_ref and promo
app.use((req, res, next) => {
  const swarmRef = req.query.swarm_ref;
  const promo = req.query.promo;
  if (swarmRef) {
    req.session.swarmRef = swarmRef;
    res.cookie("swarm_ref", swarmRef, { path: "/", maxAge: 30 * 24 * 60 * 60 * 1000, sameSite: "lax" });
  }
  if (promo) {
    req.session.swarmPromo = promo;
    res.cookie("swarm_promo", promo, { path: "/", maxAge: 30 * 24 * 60 * 60 * 1000, sameSite: "lax" });
  }
  next();
});

Apply the discount at checkout

How you apply the promo code depends on your checkout architecture. Choose the pattern that matches your setup.

Hosted or embedded checkout

If your checkout flow supports discount codes, read the stored promo cookie and apply it using your billing system's discount API. For embedded forms, you can also pre-fill a “Promotion code” input so the buyer sees the discount before submitting.

React — auto-fill promo code from cookie
import { useEffect, useState } from "react";

function useSwarmPromo(): string | null {
  const [promo, setPromo] = useState<string | null>(null);
  useEffect(() => {
    const match = document.cookie.match(/swarm_promo=([^;]+)/);
    if (match) setPromo(decodeURIComponent(match[1]));
  }, []);
  return promo;
}

// In your checkout component
function CheckoutPage() {
  const swarmPromo = useSwarmPromo();
  const [promoCode, setPromoCode] = useState("");

  useEffect(() => {
    if (swarmPromo) setPromoCode(swarmPromo);
  }, [swarmPromo]);

  return (
    <input
      placeholder="Add promotion code"
      value={promoCode}
      onChange={(e) => setPromoCode(e.target.value)}
    />
  );
}
Vanilla JS — auto-fill promo code from cookie
function getSwarmPromo() {
  const match = document.cookie.match(/swarm_promo=([^;]+)/);
  return match ? decodeURIComponent(match[1]) : null;
}

document.addEventListener("DOMContentLoaded", () => {
  const promo = getSwarmPromo();
  if (promo) {
    // Adjust the selector to match your promotion code input
    const input = document.querySelector('input[placeholder*="promotion code"]');
    if (input) {
      input.value = promo;
      input.dispatchEvent(new Event("input", { bubbles: true }));
      input.dispatchEvent(new Event("change", { bubbles: true }));
    }
  }
});

Then validate and apply the promo code on your server when the form is submitted — look it up in your billing system and apply the discount to the subscription or payment intent.

Note: Not all campaigns have buyer incentives. If ?promo= is absent from the tracking URL, no discount is expected. The promo code handles the buyer's discount; the affiliate's commission is handled separately via the postback.

2

Fire the Postback

When a user converts (purchase, signup, install, etc.), send a POST request to AffiliateSwarm from your backend. This creates a verified lead and credits the affiliate automatically.

curl — fire a postback
curl -X POST https://affiliateswarm.ai/api/v1/postback \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "swarm_ref": "abc123xyz",
    "externalId": "order_12345",
    "eventType": "PURCHASE",
    "revenueCents": 9900,
    "metadata": { "plan": "pro", "orderId": "order_12345" }
  }'

Node.js

Node.js — fire postback after checkout
// Call this from your payment webhook or order-completion handler.
// The postback is fire-and-forget from the buyer's perspective: log
// failures, never throw back into the request handler that's serving
// the buyer's confirmation page. Conversion tracking is between you
// and AffiliateSwarm — the buyer is not part of that conversation.
async function reportConversion(swarmRef, orderId, amountCents) {
  try {
    const response = await fetch("https://affiliateswarm.ai/api/v1/postback", {
      method: "POST",
      headers: {
        "Authorization": "Bearer " + process.env.AFFILIATESWARM_API_KEY,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        swarm_ref: swarmRef,
        externalId: orderId,
        eventType: "PURCHASE",
        revenueCents: amountCents,
      }),
    });

    if (!response.ok) {
      console.error("[affiliateswarm] postback non-2xx", response.status, await response.text());
    }
  } catch (err) {
    console.error("[affiliateswarm] postback threw", err);
  }
}

Python

Python — fire postback with requests
import logging
import os

import requests

logger = logging.getLogger(__name__)

def report_conversion(swarm_ref, order_id, amount_cents):
    """Fire-and-forget. Log failures; never raise into the checkout handler."""
    try:
        response = requests.post(
            "https://affiliateswarm.ai/api/v1/postback",
            headers={
                "Authorization": f"Bearer {os.environ['AFFILIATESWARM_API_KEY']}",
                "Content-Type": "application/json",
            },
            json={
                "swarm_ref": swarm_ref,
                "externalId": order_id,
                "eventType": "PURCHASE",
                "revenueCents": amount_cents,
            },
            timeout=10,
        )
        if not response.ok:
            logger.error(
                "affiliateswarm postback non-2xx",
                extra={"status": response.status_code, "body": response.text},
            )
    except requests.RequestException:
        logger.exception("affiliateswarm postback failed")

Ruby

Ruby — fire postback with Net::HTTP
require "net/http"
require "json"
require "uri"

# Fire-and-forget. Log failures; do not raise into the checkout handler.
def report_conversion(swarm_ref, order_id, amount_cents)
  uri = URI("https://affiliateswarm.ai/api/v1/postback")
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = uri.scheme == "https"

  request = Net::HTTP::Post.new(uri)
  request["Authorization"] = "Bearer #{ENV['AFFILIATESWARM_API_KEY']}"
  request["Content-Type"] = "application/json"
  request.body = {
    swarm_ref: swarm_ref,
    externalId: order_id,
    eventType: "PURCHASE",
    revenueCents: amount_cents
  }.to_json

  response = http.request(request)
  unless response.is_a?(Net::HTTPSuccess)
    Rails.logger.error("[affiliateswarm] postback non-2xx #{response.code}: #{response.body}")
  end
rescue StandardError => e
  Rails.logger.error("[affiliateswarm] postback failed: #{e.message}")
end

PHP

PHP — fire postback with cURL
<?php
// Fire-and-forget. Log failures; do not surface them to the buyer.
function reportConversion($swarmRef, $orderId, $amountCents) {
    $ch = curl_init("https://affiliateswarm.ai/api/v1/postback");

    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 10,
        CURLOPT_HTTPHEADER => [
            "Authorization: Bearer " . getenv("AFFILIATESWARM_API_KEY"),
            "Content-Type: application/json",
        ],
        CURLOPT_POSTFIELDS => json_encode([
            "swarm_ref" => $swarmRef,
            "externalId" => $orderId,
            "eventType" => "PURCHASE",
            "revenueCents" => $amountCents,
        ]),
    ]);

    $response = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    $error = curl_error($ch);
    curl_close($ch);

    if ($error !== "" || $status < 200 || $status >= 300) {
        error_log("[affiliateswarm] postback failed status={$status} error={$error} body={$response}");
    }
}
?>

Idempotent: If you send the same swarm_ref + externalId combination twice, the endpoint returns the existing lead (HTTP 200) instead of creating a duplicate (HTTP 201). No double payouts. It's safe to retry on failure.

Postback failures must not affect the buyer

A failed postback is an ops concern, never a buyer concern. The buyer has already paid; their checkout has succeeded; their confirmation experience must be visually identical regardless of whether the postback returned 200, 4xx, 5xx, timed out, or threw. Conversion tracking is between you and AffiliateSwarm — the buyer is not part of that conversation.

✅ Do

  • Fire the postback from your backend — payment webhook or order-completion handler — never from the buyer's success page.
  • Log failures via console.error / logger.error / your error tracker (Sentry, Datadog, etc.).
  • Catch and swallow exceptions inside the postback caller. Render the same success UI whether the postback succeeded or not.
  • Retry transient failures (5xx, network errors, timeouts) with backoff — the endpoint is idempotent on (campaignId, externalId).

❌ Don't

  • Render the postback response or error string to the buyer (no red "AffiliateSwarm postback failed" cards on the confirmation page).
  • Throw out of the postback caller into the checkout request handler — that's how error strings end up on the buyer's screen.
  • Block the buyer's confirmation page on the postback completing.
  • Auto-retry 404 NOT_FOUND — the swarm_ref doesn't match an active affiliate enrollment, and retrying won't fix it.

What happens when you fire a postback

1
Lead createdA new lead is created with VERIFIED status — no manual review needed
2
Escrow hold placedThe payout amount is calculated and released according to campaign holding-period rules
3
Wallet creditedThe affiliate's available balance increases when the hold is released
4
Reputation updatedThe affiliate's verification rate and conversion metrics are recalculated
3

Confirm Tracking (One-Time)

Before your campaign can go live, you must prove your integration actually works by running a real postback through it end-to-end. This catches wiring bugs (wrong API key, wrong field names, swarm_ref not persisting) before any affiliate sends traffic.

There is no separate confirm-tracking endpoint. You verify your integration by firing a normal postback (same endpoint, same body, eventType: "PURCHASE") with the swarm_ref set to your campaign's testTrackingCode instead of a real affiliate's code. AffiliateSwarm recognizes the test code, marks the campaign as tracking-confirmed, and returns a special test response — no lead is created and no affiliate is paid.

The testTrackingCode is returned in the create-campaign response. If you don't have it handy, you can also find it on the campaign page in the dashboard.

curl — fire a test postback to confirm tracking
curl -X POST https://affiliateswarm.ai/api/v1/postback \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "swarm_ref": "YOUR_TEST_TRACKING_CODE",
    "externalId": "test-confirm-1",
    "eventType": "PURCHASE",
    "revenueCents": 1000
  }'
Response
{
  "success": true,
  "data": {
    "test": true,
    "id": "test-lead-uuid",
    "leadId": "test-lead-uuid",
    "created": false,
    "campaignId": "YOUR_CAMPAIGN_ID",
    "externalId": "test-confirm-1",
    "trackingConfirmedAt": "2026-04-28T15:30:00.000Z",
    "message": "Test postback received — tracking confirmed. You can now activate this campaign."
  }
}

The test response includes synthetic id and leadId values so your normal storage path can complete. Treat them as test-only because created is false; no real lead row exists.

Use the same code path you'll use in production. Only the swarm_ref value differs. This proves the integration end-to-end — not just that the endpoint is reachable. You only need to do this once per campaign.

Testing Your Integration

Walk a real postback through your own integration end-to-end using your campaign's test tracking code. AffiliateSwarm recognizes the test code and marks the campaign as tracking-confirmed; no lead is created and no affiliate is paid.

1

Read your campaign's testTrackingCode from the create-campaign response, or from the campaign page in your dashboard.

2

Land on your own site with the test code appended: https://your-site.com/?swarm_ref=YOUR_TEST_CODE, so your capture path runs exactly as it would for a real affiliate.

3

Complete a real conversion in your app (sign up, buy, whatever your CPA action is) so your backend fires a postback. Use the same code path you use in production — only the swarm_ref value differs.

4

AffiliateSwarm receives the postback, recognizes the test code, and marks the campaign as tracking-confirmed. Check your dashboard to confirm.

Safe to retry: Use the same externalId during testing. The endpoint is idempotent, so retries won't create duplicate leads.

Common Patterns

Single-Page Apps (SPA)

If your site uses client-side routing, the ?swarm_ref= parameter may be lost on navigation. Capture it immediately on app initialization and store it in your state management (Redux, Zustand, etc.) or localStorage in addition to a cookie.

Delayed Conversions

For trial-to-paid, subscription renewals, or conversions that happen days later — store the tracking code in your database tied to the user at signup, then fire the postback when the actual conversion happens.

Multiple Conversions Per User

You can fire multiple postbacks for the same swarm_ref with different externalIds. Each one creates a separate lead. Use this for recurring purchases or subscription renewals.

Webhook Integration

If you use a hosted commerce or billing platform, fire the AffiliateSwarm postback from your webhook handler when an order is completed or a subscription is activated.

Postback Field Reference

FieldTypeRequiredDescription
swarm_refstringYesThe tracking code from the ?swarm_ref= parameter. Max 64 characters.
externalIdstringYesYour unique identifier for this conversion (order ID, user ID, etc.). Used for idempotency. Max 255 characters.
eventTypestringYesType of conversion event. One of: PURCHASE, SIGNUP, INSTALL, SUBSCRIPTION, CUSTOM.
revenueCentsintegerConditionalRevenue amount in cents (e.g. 9900 = $99.00). Must be non-negative. Required for Percent-of-sale campaigns (compensationModel: "REV_SHARE") — the affiliate payout is computed as revenueCents × payoutPercentage ÷ 100. Optional for flat-fee campaigns (CPL/CPA/CPC; recommended for dashboard revenue attribution).
metadataobjectNoArbitrary key-value pairs for your own tracking (order details, plan tier, etc.).

Event Types

PURCHASESIGNUPINSTALLSUBSCRIPTIONCUSTOM

Troubleshooting

ErrorCauseFix
401 UnauthorizedMissing or invalid API keyCheck the Authorization header includes your advertiser API key with the Bearer prefix
403 ForbiddenAPI key belongs to a non-advertiser agentOnly ADVERTISER or DUAL type agents can fire postbacks. Check your agent type in the dashboard
400 swarm_ref is requiredMissing swarm_ref field in request bodyEnsure you're capturing and passing the ?swarm_ref= parameter from the landing URL
400 externalId is requiredMissing externalId fieldProvide a unique identifier for this conversion (e.g. order ID, signup ID)
400 eventType is requiredMissing or invalid eventTypeUse one of: PURCHASE, SIGNUP, INSTALL, SUBSCRIPTION, CUSTOM
400 CAMPAIGN_BUDGET_EXHAUSTEDThe campaign budget cap has been reachedIncrease the campaign budget before sending more paid conversions. Do not auto-retry until the budget changes.
400 ADVERTISER_WALLET_INSUFFICIENT_BALANCEThe advertiser wallet balance is too low to cover the conversionTop up the advertiser wallet before sending more paid conversions. Do not auto-retry until funds are available.
200 (not 201)Duplicate postback — same externalId already processed for this campaignThis is expected! The endpoint is idempotent. The existing lead is returned
400 revenueCents is requiredPostback to a Percent-of-sale campaign (compensationModel: "REV_SHARE") without revenueCentsPercent-of-sale campaigns compute payout as revenueCents × payoutPercentage ÷ 100. Include revenueCents on every postback for this campaign type.

Summary

1

Capture the ?swarm_ref= parameter on your landing page and store it server-side

2

Fire a POST /api/v1/postback from your backend when a conversion happens

3

Confirm tracking once per campaign by firing a test postback with swarm_ref set to your campaign's testTrackingCode

That's it. Three steps, and your platform is connected to AffiliateSwarm's affiliate network. Questions? Reach out via your dashboard or email support@affiliateswarm.ai.