Conversion postback for CPA campaigns
A pay-per-result (CPA) campaign is paid for the conversions your tracking platform reports to SofAds. It takes two steps: hand our click ID to your platform in the landing URL, and let your platform call our postback URL, server to server, when a conversion happens.
1. Pass the click ID in the landing URL
Every QR scan gets its own click ID. Your tracking platform has to receive it on the click, so it can send it back with the conversion. On a pay-per-result campaign the wizard asks which tracking platform you use, and SofAds adds the click ID to your landing URL under that platform's parameter name:
https://network.example/click?offer_id=123&aff_click_id={CLICK_ID}| Tracking platform | Parameter SofAds adds | Sent back in the postback as |
|---|---|---|
| TUNE (HasOffers) | aff_click_id={CLICK_ID} | {aff_click_id} |
| Everflow | sub1={CLICK_ID} | {sub1} |
| CAKE | s2={CLICK_ID} | #s2# |
| Affise | sub1={CLICK_ID} | {sub1} |
| impact.com | subId1={CLICK_ID} | {SubId1} |
| AppsFlyer (app attribution links) | clickid={CLICK_ID} | {clickid} |
| Other | the parameter name you enter | your platform's macro for it |
- Other: a name of letters, digits and
_, starting with a letter, up to 64 characters, for exampleclickidorsubid. - Already in your URL? When your landing URL already has
{CLICK_ID}somewhere, SofAds leaves it exactly as it is and adds nothing. - CAKE uses
s2, nots1: CAKE keepss1for steady values such as a source, and puts unique values ins2tos5. - The same tracking link can carry the other landing URL macros too, for example
sub2={SOURCE_ID}to see results per source in your own platform.

2. Your postback URL
The Conversion tracking page in the advertiser dashboard builds this URL for you, with your campaign's ID and your platform's placeholders already filled in. It looks like this:
https://t.sofadsrv.com/v1/postback?cid=cmp_7Kd2mQx9TfA3&key=YOUR_POSTBACK_KEY&click_id={aff_click_id}&event=purchase&txid={transaction_id}Paste it into your platform's postback (server-to-server) settings and replace YOUR_POSTBACK_KEY with the campaign's key. The key is shown once, when you create it on the campaign; if you lost it, make a new one there, and the old one stops working.
cid is your campaign's ID as the dashboard shows it, starting with cmp_. Numeric IDs are refused.
Parameters
| Parameter | Required | What it is |
|---|---|---|
cid | yes | Your campaign's ID (cmp_ and 12 letters or digits). |
key | yes | The campaign's secret postback key. |
click_id | yes | The click ID from the landing URL, sent back by your platform. |
event | no | The event name, for example purchase. Only the events chosen on the campaign are paid. |
payout | no | Only for campaigns that pay the payout you report: a decimal amount in USD, capped at the campaign's maximum. |
currency | no | USD, sent with payout. Other currencies are recorded but not paid. |
txid | no | Your transaction ID. The same transaction counts once, however often it is sent. |
GET and POST both work; on POST the parameters may also be a form body. When allowed IP addresses are set for the campaign, calls from other addresses are refused.
What we answer
Every answer is JSON with a status, and a reason when something is off:
{ "status": "accepted", "conversion_id": "0b6f3c3e-8a51-4c43-9e4d-2a8f0b7f1d6e" }| HTTP | status | Meaning |
|---|---|---|
| 200 | accepted | Recorded. |
| 200 | duplicate | Already recorded for this click and event, or this txid. Your platform can stop retrying. |
| 200 | rejected | The campaign can't take conversions: unknown_campaign (no campaign with this cid) or not_cpa (the campaign isn't pay-per-result). Fix the URL rather than retrying. |
| 400 | rejected | A parameter is missing or malformed: missing_parameter, invalid_cid, invalid_parameter or invalid_payout. |
| 403 | rejected | forbidden: wrong key, or a caller IP that isn't allowed. |
| 503 | rejected | no_snapshot: the server is starting up. Retry after the Retry-After header (60 seconds). |
When a conversion is paid
An accepted conversion is paid once, and only when its click ID belongs to a scan of the same campaign within the campaign's attribution window (30 days unless the campaign sets another). A conversion with an unknown click ID, a click from another campaign or a click outside the window is recorded but not paid. Paused and ended campaigns keep accepting postbacks for the length of the window, so late conversions from earlier scans still count.
Sources
Checked on October 8, 2026. Parameter and macro names differ per platform and account; your platform's own documentation wins.
- TUNE: How to use parameters, values and macros for tracking (
aff_click_id,aff_subtoaff_sub5) - Scaleo: TUNE (HasOffers) integration (
aff_click_id={click_id}on the TUNE link,{aff_click_id}in the postback) - Everflow: Your guide to parameters and macros (
sub1tosub10,sub1commonly the partner's click ID) - CAKE: Understanding the CAKE tracking link (
s1for steady source values,s2tos5for unique ones) - CAKE: How to implement a TUNE (HasOffers) postback URL
- Affise: Affise tracking link parameters (
sub1tosub8) - impact.com: Link parameters (
subId1tosubId3) - AppsFlyer: Attribution link specification for ad networks (
clickid)
Keep reading
- Pricing: fixed or reported payouts, and the attribution window.
- Stats: where accepted conversions show up.
- Ad policy: what ads and landing pages may and may not show.
- For advertisers: how SofAds campaigns work.