Report affiliate clicks and conversions to Infee directly from your own backend.
This guide is for custom stores (your own backend / headless storefront) that want to report affiliate clicks and conversions to Infee directly from their server. Shopify and WooCommerce stores do not need this - they are handled by the dedicated plugins.
S2S is the most reliable tracking method: because both the click and the conversion are sent server-side, it is unaffected by ad-blockers, Brave, or Safari ITP. No JavaScript snippet is required.
Base URL
Section titled “Base URL”https://<your-infee-host>/api/v1/trackingAll endpoints below are relative to that base.
Authentication
Section titled “Authentication”Every request must include two headers:
| Header | Value |
|---|---|
X-Store-UUID | Your store UUID (public identifier). |
X-Api-Key | Your S2S API key (secret). |
The API key may alternatively be supplied as a bearer token:
Authorization: Bearer <api-key>.
The key is issued from the Infee admin (StoreIntegration → Generate / rotate S2S API key). It is shown once at generation time - store it securely. Rotating the key immediately invalidates the previous one. The store UUID is public; the API key is a secret and must never be exposed in browser/client-side code.
Verify your credentials
Section titled “Verify your credentials”curl -s https://<host>/api/v1/tracking/s2s/ping \ -H "X-Store-UUID: $INFEE_STORE_UUID" \ -H "X-Api-Key: $INFEE_API_KEY"{ "ok": true, "store_uuid": "abc123", "store_name": "Acme Store" }The tracking flow
Section titled “The tracking flow”1. Shopper clicks an affiliate link → infee /t/<code> redirect. infee appends ?infee_code=<code>&infee_tid=<cookie_id> to your landing URL.
2. Your server reads infee_code + infee_tid from the landing URL and persists them (first-party HttpOnly cookie or server session) for the attribution window. Optionally POST /s2s/click to register the click explicitly.
3. Shopper checks out. Your server reads the stored infee_code (+ infee_tid) and POSTs /s2s/conversion with the order details.You are responsible for steps 1–2 (capturing and carrying the identifier). Capturing it server-side - rather than in a browser cookie set by JavaScript - is what makes S2S resilient to tracking restrictions.
Common request envelope
Section titled “Common request envelope”All requests carry an event envelope so retries and debugging stay sane:
| Field | Required | Notes |
|---|---|---|
event_id | yes | Your unique id for this event (max 64 chars). Use it to trace events. |
event_time | yes | ISO-8601 UTC, e.g. 2026-04-24T20:31:00Z. |
metadata | no | Free-form object echoed into storage. |
POST /s2s/click
Section titled “POST /s2s/click”Registers a click. Optional but recommended - it lets Infee attribute the conversion to
the exact click via cookie_id and capture referrer/IP data.
Request
Section titled “Request”{ "event_id": "clk_evt_001", "event_time": "2026-04-24T20:31:00Z", "tracking_code": "AB12CD34EF56", "cookie_id": "d6df56d9-20fc-48a8-9d4f-f6cf70901d76", "click_ids": { "gclid": "Cj0KC...", "fbclid": "IwAR..." }, "client": { "ip_address": "203.0.113.10", "user_agent": "Mozilla/5.0 ...", "referer": "https://publisher.example/post/123" }}tracking_code- required; must be an active tracking link belonging to your store.cookie_id- theinfee_tidvalue you captured from the landing URL.
Response 201
Section titled “Response 201”{ "ok": true, "event_id": "clk_evt_001", "click_id": "9bb88a20-3f66-4d34-a45b-372d264f3c6e", "tracking_code": "AB12CD34EF56", "received_at": "2026-04-24T20:31:01Z"}POST /s2s/conversion
Section titled “POST /s2s/conversion”Reports a sale. This is the event that creates commission.
Infee computes the commission, not you. You report the ordered
line_items; Infee resolves each to your synced product catalog and computes commission from each product’s configuredcommission_rate. There is nocommission_amountfield in the request - any value you send is ignored. This keeps S2S consistent with the Shopify/WooCommerce plugins and prevents commission drift.
Request
Section titled “Request”{ "event_id": "cnv_evt_001", "event_time": "2026-04-24T20:40:12Z", "platform": "custom", "order_id": "100045", "currency": "EUR", "order_total": "129.90", "tracking_code": "AB12CD34EF56", "line_items": [ { "sku": "WIDGET-BLUE-L", "quantity": 2, "unit_price": "49.95", "line_total": "99.90", "platform_product_id": "your-internal-id-optional" }, { "product_uuid": "0f8b...", "quantity": 1, "unit_price": "30.00" } ], "cookie_id": "d6df56d9-20fc-48a8-9d4f-f6cf70901d76", "customer": { "email_sha256": "optional_hash", "phone_sha256": "optional_hash" }, "click_ids": { "gclid": "optional", "fbclid": "optional" }, "raw_payload": { "any_source_payload": true }}| Field | Required | Notes |
|---|---|---|
platform | yes | One of custom, shopify, woocommerce. Use custom for S2S stores. |
order_id | yes | Your order identifier. |
currency | yes | USD or EUR. |
order_total | yes | Decimal string (used for commission sanity-capping). |
tracking_code | one of code/cookie | Active tracking link for your store (direct-code attribution). |
cookie_id | one of code/cookie | The infee_tid you captured (click attribution). |
line_items | yes | At least one item. Each is matched to a synced product. |
You must send tracking_code or cookie_id (or both). If neither resolves to a
link, the request is rejected and nothing is recorded.
Line item fields
Section titled “Line item fields”| Field | Required | Notes |
|---|---|---|
product_uuid | one of product_uuid/sku | Infee product UUID. Tried first. |
sku | one of product_uuid/sku | Your product SKU (must match the synced product’s SKU for the store). |
quantity | no | Defaults to 1. |
unit_price | yes | Decimal string. |
line_total | no | Decimal string. Defaults to unit_price * quantity. |
platform_product_id | no | Your own product id, stored for reference. |
Every line item must resolve to an enabled product in your store; an unknown or
disabled product returns 404 and the conversion is not recorded (so commission is
never silently dropped). Commission per item is
line_total * product.commission_rate / 100, summed into the conversion total.
Attribution
Section titled “Attribution”The link (and therefore the influencer + commission) is resolved one of two ways, mirroring the Shopify/WooCommerce plugins:
- Direct code —
tracking_codepresent → the link is resolved from the code (attribution_method: "direct_code"). If a matchingcookie_idclick also exists it is linked for analytics, but a stored click is not required. - Click — no
tracking_code, onlycookie_id→ the link is recovered from the most recent non-expired click registered via/s2s/clickfor that cookie (attribution_method: "click"). If no such click exists, the conversion is rejected with404(there is no link to attribute to).
attributed in the response reflects whether a click record is linked to the conversion.
If you intend to rely on cookie-only attribution, you must register the click with
/s2s/clickwhen the shopper lands (while the URL still carriesinfee_code/infee_tid). Otherwise sendtracking_codeon the conversion.
Idempotency
Section titled “Idempotency”Conversions are de-duplicated on (order_id, platform, tracking_code), enforced by a
database constraint and handled inside a transaction - concurrent duplicate posts are
safe and return the idempotent 200 response.
Payouts
Section titled “Payouts”On first creation the conversion is wrapped in a transaction that also writes per-line
ConversionProduct rows and initializes the payout snapshot, so S2S conversions enter
the influencer payout pipeline exactly like plugin conversions.
Response 201 (newly created)
Section titled “Response 201 (newly created)”{ "ok": true, "event_id": "cnv_evt_001", "conversion_id": 8421, "order_id": "100045", "attributed": true, "tracking_code": "AB12CD34EF56", "received_at": "2026-04-24T20:40:13Z"}Response 200 (idempotent replay)
Section titled “Response 200 (idempotent replay)”{ "ok": true, "event_id": "cnv_evt_001", "conversion_id": 8421, "order_id": "100045", "attributed": true, "tracking_code": "AB12CD34EF56", "idempotent_replay": true}GET /s2s/events/{event_id}
Section titled “GET /s2s/events/{event_id}”Best-effort debug lookup. Resolves a click by its click_id (UUID), or a conversion by
the event_id you sent on the conversion. Returns 404 if nothing matches.
Error contract
Section titled “Error contract”Business errors return:
{ "ok": false, "error": { "code": "invalid_payload|unauthorized|not_found|conflict|rate_limited|internal_error", "message": "Human readable message", "details": {} }}| Status | When |
|---|---|
400 | Semantically invalid payload (unsupported platform/currency, no line_items). |
401 | Missing/invalid X-Store-UUID + X-Api-Key. |
404 | Unknown/inactive tracking_code, an unresolved/disabled line-item product, or unknown event in the debug lookup. |
422 | Malformed request body (missing/wrong-typed fields - schema validation). |
500 | Unexpected server error. |
End-to-end example
Section titled “End-to-end example”# 1. Register the click (server-side, on your landing handler)curl -s -X POST https://<host>/api/v1/tracking/s2s/click \ -H "X-Store-UUID: $INFEE_STORE_UUID" \ -H "X-Api-Key: $INFEE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "event_id": "clk_001", "event_time": "2026-04-24T20:31:00Z", "tracking_code": "AB12CD34EF56", "cookie_id": "d6df56d9-20fc-48a8-9d4f-f6cf70901d76", "client": { "ip_address": "203.0.113.10", "user_agent": "Mozilla/5.0" } }'
# 2. Report the conversion (server-side, on order completion)curl -s -X POST https://<host>/api/v1/tracking/s2s/conversion \ -H "X-Store-UUID: $INFEE_STORE_UUID" \ -H "X-Api-Key: $INFEE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "event_id": "cnv_001", "event_time": "2026-04-24T20:40:12Z", "platform": "custom", "order_id": "100045", "currency": "EUR", "order_total": "129.90", "tracking_code": "AB12CD34EF56", "cookie_id": "d6df56d9-20fc-48a8-9d4f-f6cf70901d76", "line_items": [ { "sku": "WIDGET-BLUE-L", "quantity": 2, "unit_price": "49.95", "line_total": "99.90" }, { "sku": "GIFTWRAP", "quantity": 1, "unit_price": "30.00" } ] }'- Commission requires a synced product catalog for the custom store (products with a
commission_rateand matchingsku/uuid). Stores must sync products before sending conversions. - Per-store rate limiting is not yet enforced at the endpoint.
- Strict idempotency currently relies on the
(order_id, platform, tracking_link)uniqueness.