Connecting your tracking link
How a partner's tracking link works end to end, where affiliates get it, and how to wire up postbacks so conversions land correctly in the dashboard.
How it works Concept
Every offer + affiliate pair has its own unique tracking link:
https://smthprj.xyz/t/<slug>
When someone opens that link:
- The server records a click and generates a unique
click_id, along with IP, user-agent, and referer. - The link's click count increases by one — visible immediately to both the partner and the affiliate.
- The visitor is 302-redirected to the offer's destination URL, with these params appended:
?click_id=...&offer_id=...&affiliate_id=...
Later, when a real conversion happens on the partner's site (a sale, lead, signup), the partner's site must send a postback — a server-side request carrying that same click_id plus the partner's api_key — so the conversion is authenticated and credited to the right affiliate.
Getting your link
A link only exists once an offer is live and an affiliate has been approved on it. The full lifecycle:
The partner creates the offer. It starts as pending_admin and isn't visible in the marketplace yet.
An admin approves the offer → it becomes active and appears in the marketplace.
The affiliate applies to the active offer from Marketplace → Offer. (Applying to an offer that isn't active returns offer_not_live.)
The partner approves the application from Affiliates → Applications.
Only now is the unique link generated — it appears for the affiliate under My Offers, ready to copy.
Where to place it
Treat it like a normal URL — it doesn't point straight at the partner's site, it routes through the tracking domain smthprj.xyz, which redirects (302) instantly to the real landing page. This adds no noticeable delay for the visitor.
- Use it directly in ads, posts, newsletters, bio links — same as any URL.
- You can append your own UTM parameters; they pass through to the landing page alongside
click_id. - Avoid placing it where a link preview is fetched automatically without a real visit (some messaging apps) — most don't trigger a real click, but verify on a specific channel before a large campaign.
How a click is counted
A click is recorded the moment a GET request hits /t/<slug> — i.e. at the moment of the redirect itself, before the visitor even sees the partner's site. One visit = one click.
Setting up a postback
A postback is a server-to-server request your site, CRM, or checkout sends to us at the moment of conversion. The base template a partner sees and copies from Settings → Global postback URL:
https://somethingproject.com/api/postback?api_key={api_key}&click_id={click_id}&status={status}&amount={amount}&commission={commission}
api_key — either as a query/body parameter (api_key=…) or the X-API-Key header. Find it under Settings → API & Webhooks. The server checks that the key belongs to the partner who owns the offer; a missing key returns 401 api_key_required, a wrong key returns 403 invalid_api_key. This stops anyone from spoofing conversions with a guessed click_id. Keep the key secret; regenerating it in Settings invalidates the old one immediately.
/api/postback, not just /postback. Without /api/, the request will return a 200 response but the conversion will not be recorded — that path doesn't process anything.
Replace {click_id}, {status}, etc. with the actual macros from your platform (Keitaro, a landing page builder, your own backend), or with real values if you're calling it directly from code.
(click_id, transaction_id). Sending the same pair twice is safe — it returns ok and is not double-counted. To record several conversions from a single click (e.g. an upsell), send each with a distinct transaction_id. If you omit transaction_id, it defaults to the click_id — meaning a second postback for the same click with no transaction id is treated as a duplicate and ignored.
Postback parameters
| Param | Required | Description |
|---|---|---|
api_key (or X-API-Key header) | Yes | Your partner API key from Settings → API & Webhooks. Must belong to the offer's owner |
click_id (or cid) | Yes | Click identifier from the redirect params on the landing page |
status | No | Conversion status. Defaults to confirmed. See statuses below — it decides whether the conversion counts toward earnings. |
amount | No | Order/sale amount. Defaults to 0. |
commission | No | Affiliate commission for this conversion. Defaults to 0. |
currency | No | Currency code, e.g. USD. Defaults to USD; stored uppercased. |
transaction_id (or txn, order_id) | No | Your own order/transaction ID, for reconciliation. Defaults to the click_id. |
Both GET and POST are supported. Parameter names are read identically from the query string or the request body.
Conversion statuses & earnings
The status you send determines whether the conversion contributes to an affiliate's earned balance:
| Status | Counts toward earnings? |
|---|---|
confirmed · approved · paid | Yes |
pending | No — shown, awaiting confirmation |
rejected (or any other value) | No |
Every conversion you send still appears in the partner's Conversions list and in the total/pending counters regardless of status — but only confirmed/approved/paid roll up into the affiliate's earned commission.
(click_id, transaction_id) and is not updated by later postbacks for the same pair — re-posting a pending conversion as confirmed with the same transaction_id is ignored, so it stays pending and earns nothing. Send the status you actually want recorded. If you use a hold/approval period, send the postback only once the conversion is final (or send distinct transaction_ids for genuinely separate events).
Integration examples
Direct call from your backend (Node.js)
await fetch(
"https://somethingproject.com/api/postback" +
"?api_key=" + SOMETHING_API_KEY + // from Settings → API & Webhooks
"&click_id=" + clickId +
"&status=confirmed" +
"&amount=" + orderAmount +
"&commission=" + commissionAmount +
"¤cy=USD"
);
// Or pass the key as a header instead of a query param:
await fetch("https://somethingproject.com/api/postback?click_id=" + clickId + "&status=confirmed", {
method: "POST",
headers: { "X-API-Key": SOMETHING_API_KEY },
});
cURL (for testing or a server script)
curl "https://somethingproject.com/api/postback?api_key=sp_live_xxx&click_id=clk_xxx&status=confirmed&amount=99&commission=10"
Webhook from a payment provider / CRM
If your payment provider or CRM supports outgoing webhooks on payment, point it at the same URL with the real click_id substituted in. You'll need to persist that click_id on your side at the moment the visitor lands (e.g. a hidden form field or localStorage) and carry it through to the order.
Test your setup Recommended
Copy your tracking link from My Offers and open it in a browser.
Confirm you land on the real offer page and the address bar shows a click_id.
Copy that click_id, grab your api_key from Settings → API & Webhooks, and send a test postback (browser address bar or curl):
https://somethingproject.com/api/postback?api_key=YOUR_API_KEY&click_id=YOUR_CLICK_ID&status=confirmed&amount=10&commission=1
Check the Conversions section in the dashboard — the test conversion should show up immediately.
Troubleshooting
| Symptom | Likely cause |
|---|---|
| Clicks aren't increasing | Wrong link was used (e.g. the landing page URL was copied instead of the /t/... link), or the offer is inactive |
| Postback returns 200 but no conversion appears | The URL is missing /api/ before postback — the request is hitting the wrong path |
Postback returns api_key_required | No api_key parameter (or X-API-Key header) was sent. Add your key from Settings |
Postback returns invalid_api_key | The key is wrong, was regenerated, or doesn't belong to the partner who owns that offer |
Postback returns click_not_found | The click_id is wrong, stale, or was never captured on your side at the moment of the click |
Postback returns click_id_required | The click_id / cid parameter is missing or empty in the request |
| Conversion shows but earnings stay at 0 | Its status is pending/rejected — only confirmed/approved/paid count. Status is fixed at first insert and won't change on re-post |
| Second conversion from the same click doesn't appear | Same (click_id, transaction_id) pair was reused — it's deduplicated. Use a distinct transaction_id |
Affiliate can't apply (offer_not_live) | The offer isn't active yet — it still needs admin approval before it appears in the marketplace |
| The link redirects to the Something Project homepage | The offer is paused/not yet approved, or the affiliate's application hasn't been approved by the partner |