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.

Online conversions

Redemptions at a Square register, at a POS counter scan, or through Shopify's linked discounts reach partners automatically. For any other online store, report the sale yourself: call POST /api/v1/redeem with the pass code the customer used at checkout plus your orderId, amount and currency. Pass Studio redeems the pass, fires the redeem postback with {amount}, {currency} and {orderId} filled in, and counts the value as revenue in the report below. Calls are idempotent per order id, so a retried checkout hook never double-pays a partner. The WooCommerce recipe is a fifteen-line hook; any platform that can run a request after checkout works the same way.

Managing partners by API

Everything in Settings › Partners is also on the REST API for agencies and networks that onboard partners programmatically: GET/POST /api/v1/partners to list and create (the secret is returned once), GET/PATCH/DELETE /api/v1/partners/{partnerId} plus /test for a synthetic postback, and GET /api/v1/partners/report for the numbers. Partners are workspace-wide, so pass-restricted keys cannot manage them; read-only keys can list and read the report.

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