Partner attribution & postbacks

Know which publisher, affiliate, or agency drove each pass save and each redemption, and tell them in real time with a signed postback.

How it works

  1. Create a partner in Settings → Partners: a name, a postback URL template, and which events they get. You receive a partner id and a secret once.
  2. Give the partner a tracking link: any share or enrollment link with ?pid=<partner id>&cid=<their click id>. Your existing UTM parameters keep working alongside.
  3. When a holder saves the pass, the partner id and click id are stamped on that holder's instance, next to first-touch UTM, for the life of the pass.
  4. On save, and again on every redemption, Pass Studio resolves the template's macros, signs the request, and calls the partner's URL. Retries with backoff for up to two hours.
  5. Settings shows saves, redemptions, and attributed order revenue per partner over the last 30 days.
https://www.thepassstudio.com/share/SHARE_TOKEN?pid=p_k3f9xq2mza&cid=CLICK_ID
https://www.thepassstudio.com/enroll/SHARE_TOKEN?pid=p_k3f9xq2mza&cid=CLICK_ID
  • pid is the partner id shown in Settings. cid is whatever the partner uses to identify the click; clickid and click_id are accepted as aliases. Values up to 200 characters, stored verbatim.
  • Issuing through the API? Send partnerId and clickId in the issue body and the same stamping applies.
  • Attribution is first-touch: a holder who already has the pass keeps the partner that acquired them.

Postbacks

The postback URL is a template. Every {macro} is replaced and URL-encoded. GET by default; choose POST to receive the same values as a JSON body.

MacroValue
{clickId}The click id from the tracking link (cid), verbatim.
{partnerId}The partner id (pid) the link carried, e.g. p_k3f9xq2mza.
{event}save when the pass entered a wallet; redeem when it was redeemed.
{passId}The pass the holder saved.
{instanceId}The holder's instance — stable across save and later redemptions.
{orderId}The order reference on a redemption, when there is one (store orders, API orders). Empty for counter scans.
{amount}Order amount on a redemption when known; empty otherwise.
{currency}ISO currency of amount when known; empty otherwise.
{ts}Unix seconds when the postback was signed.
{sig}HMAC-SHA256 hex of "{partnerId}.{clickId}.{instanceId}.{event}.{ts}" with the partner secret.

Example

Template:  https://track.partner.example/pb?c={clickId}&e={event}&amt={amount}&t={ts}&s={sig}
Sent:      GET https://track.partner.example/pb?c=abc123&e=redeem&amt=42.00&t=1789344000&s=9f2c…
Headers:   X-PassStudio-Partner: p_k3f9xq2mza
           X-PassStudio-Event: redeem
           X-PassStudio-Timestamp: 1789344000
           X-PassStudio-Signature: sha256=9f2c…

Verifying the signature

expected = HMAC_SHA256(secret, partnerId + "." + clickId + "." + instanceId + "." + event + "." + ts)
accept if hex(expected) == sig  (and ts is within your tolerance window)
  • Any 2xx response counts as delivered. Anything else is retried after 1, 5, 30, and 120 minutes, then marked exhausted.
  • One postback per partner, instance, and event. A repeat redemption with a distinct order id is a new conversion; a retried webhook for the same one is not.
  • Use Send test in Settings to receive a synthetic save postback before any traffic.

Report and data

The partner id and click id appear on every event as acqPartnerId and acqClickId: in the event reference, in Segment as acquisitionPartner and acquisitionClickId, and on the instance returned by the API. A partner who wants to pull their own numbers can be given a read-only key restricted to the passes they promote.

Related