Links and clicks

Create tracked links and coupon codes, and understand what happens when someone clicks a /go link.

Embed
Preview of the card
<a href="https://referalli.com/docs/links-and-clicks"><img src="https://referalli.com/docs/_api/public/orgs/07f432f4-34b9-46a7-aaad-c0e395d2e4f9/spaces/2bf4f614-3630-4fbf-85f9-5ecafa6b7d97/pages/d041d50c-9bbf-45a6-a8c7-1d7a39173fe3/social-card.svg?v=3e7ae33eec8b" alt="Links and clicks" width="600" height="315"></a>

A tracked link is the starting point of most referrals. The partner shares /go/{trackingToken}/{code}, Referalli records a click, and the visitor is redirected to your site with the click id attached. Coupon codes are the clickless alternative.

curl -X POST https://referalli.com/v1/links \
  -H "Authorization: Bearer $KEY" \
  -H "content-type: application/json" \
  -d '{
    "programId": "prg_...",
    "partnerId": "ptr_...",
    "code": "dana-spring",
    "destinationUrl": "https://example.com/pricing"
  }'
{
  "id": "lnk_...",
  "programId": "prg_...",
  "partnerId": "ptr_...",
  "code": "dana-spring",
  "destinationUrl": "https://example.com/pricing"
}

Field

Required

Rules

programId

yes

A program in your org.

partnerId

yes

The partner who gets credit.

destinationUrl

yes

Absolute URL the visitor lands on.

code

no

1 to 64 characters of letters, digits and -. Stored lowercased (WJXQGLBW becomes wjxqglbw) and unique within your org ignoring case; a taken code returns 409. Omit it for a random 10-character code.

The shareable URL is https://referalli.com/go/{trackingToken}/{code}, where trackingToken comes from GET /v1/org.

Codes match in any case everywhere: /go/{trackingToken}/WJXQGLBW, ?via=WJXQGLBW and ?ref=WJXQGLBW all reach the link stored as wjxqglbw, so codes you already shared in upper case keep working.

  • GET /v1/links lists links, optionally filtered with ?programId= and ?partnerId=. Each item includes a clicks count.

  • GET /v1/links/{id}/qr returns an SVG QR code (image/svg+xml) that encodes the short link.

What a click does

When a visitor opens GET /go/{trackingToken}/{code}:

  1. Referalli looks up the org by tracking token and the link by code. Either one unknown returns 404.

  2. The click is fraud-scored and stored with its referer, user agent and a hashed IP.

  3. The visitor is redirected with 302 to the link's destinationUrl, with two query parameters added:

https://example.com/pricing?via=dana-spring&ref_click_id=clk_01JB...

Parameter

Value

via

The link code.

ref_click_id

The id of the click just recorded. Pass it to associate as referralToken for an exact match.

The tracking script reads both parameters on your landing page and stores them for you.

Fraud scoring

Each click gets a score from 0 to 100. A click scoring 70 or more is stored as ineligible: it stays in the data and in the referral journey, but attribution ignores it. Signals that add to the score include:

  • A missing user agent, or one that looks automated (headless browsers, curl/, wget/, python-requests, anything with bot, spider or crawler). Automated user agents are disqualifying on their own.

  • More than 20 clicks from the same IP in an hour, or more than 10 on the same link in a minute.

  • A referer on the same host as the destination, a sign of cookie stuffing.

  • Exceeding the redirect rate limits described in Errors and limits. The visitor is still redirected; the click is simply not eligible.

  • Paid-search traffic, when the program's allowPaidSearch setting is off. Disqualifying on its own.

Programs can also add UTM parameters to every redirect, and put a JavaScript check in front of the redirect to stop headless click floods. See Program settings.

Links are served from Referalli's domain by default. To use your own, such as go.yourcompany.com (Growth plan and up; see Plans and pricing):

curl -X POST $BASE/v1/branding/link-domain -H "Authorization: Bearer $KEY" \
  -H "content-type: application/json" -d '{"domain": "go.yourcompany.com"}'
# { "domain": "go.yourcompany.com", "cnameTarget": "referalli.com",
#   "txtName": "_referally-verify.go.yourcompany.com", "txtRecord": "rfy-verify-…", "verified": false }
  1. Add a CNAME from your domain to cnameTarget (DNS only: if you use Cloudflare, turn its proxy off for this record).

  2. Add a TXT record on txtName with the txtRecord value.

  3. POST /v1/branding/link-domain/verify. Referalli checks both records, then routes the domain to its servers and issues its certificate by itself:

    • {"verified": true, "status": "active"}: done.

    • {"verified": false, "status": "provisioning"}: the records check out and the certificate is being issued. The domain switches on by itself, usually within a few minutes; GET /v1/branding shows linkDomainStatus going from provisioning to active.

    • 400: a record is missing or the CNAME points elsewhere; the message says which.

    Links stay on Referalli's domain until the status is active.

Once verified, every link the API returns (shortUrl on GET /v1/links, POST /v1/links, the partner portal's links, QR codes, the join page and the referral widget) uses your domain: https://go.yourcompany.com/go/tk_…/dana. Links already shared on Referalli's domain keep working. Your domain only serves your own links. A domain can belong to one organization (409 otherwise). DELETE /v1/branding/link-domain goes back to the default.

Your own partner portal domain

The partner portal can also live on your domain, such as partners.yourcompany.com (Pro plan and up). Set it with POST /v1/branding/domain ({"domain": "partners.yourcompany.com"}), add the CNAME and the TXT record on _referally-verify.<your domain> it returns, then POST /v1/branding/domain/verify. It works like a link domain: Referalli routes the domain and issues its certificate by itself, so the response is active or, for a few minutes while the certificate is issued, provisioning (portalDomainStatus on GET /v1/branding).

Once verified, that domain serves only the partner portal, in your branding (logo and the theme tokens from PUT /v1/branding): / opens the portal, your public join pages work at /join/<handle>, and nothing from the merchant dashboard is reachable there. Partners sign in on your domain as usual; sign-in briefly passes through Referalli's own domain and comes straight back, signed in. GET /v1/hosts/current tells any page which domain it's on (portal, link or app).

Coupon codes

A coupon code attributes a customer to a partner without any click. It also wins over clicks when both are present.

curl -X POST https://referalli.com/v1/coupons \
  -H "Authorization: Bearer $KEY" \
  -H "content-type: application/json" \
  -d '{"programId": "prg_...", "partnerId": "ptr_...", "code": "DANA20"}'
# { "id": "cpn_...", "code": "DANA20" }

code is 2 to 64 characters and unique within your org, ignoring case: DANA20 and dana20 are the same code, and a customer can type it either way. GET /v1/coupons lists them, optionally filtered with ?programId= and ?partnerId=.

Pass the code as couponCode when you call associate. For Shopify orders, Referalli reads the order's discount codes from the webhook and attributes the buyer automatically.

Record a click from your server

If the click happens somewhere Referalli cannot see, such as a link in your own app, record it directly:

curl -X POST https://referalli.com/v1/track/events \
  -H "Authorization: Bearer $KEY" \
  -H "content-type: application/json" \
  -d '{
    "type": "click",
    "programId": "prg_...",
    "partnerId": "ptr_...",
    "linkId": "lnk_...",
    "anonId": "a_k3j9...",
    "url": "https://example.com/pricing"
  }'
# { "clickId": "clk_..." }

linkId, anonId, url, ip and ua are optional. Clicks recorded this way are not fraud-scored. Passing the visitor's anonId lets a later associate call with the same anonId find the click.

The same endpoint takes "type": "event" to report a customer's product event (activation or a custom name) that can pay a bounty; see Commission rules.

Updated