← Blog
shippedintegrationsapi

Conversions by click id, a full API reference, and commission slabs

Lenders, CRMs, affiliate networks and Stripe Payment Links can report a conversion with nothing but the click id, in one API call or through a postback URL. The whole API is now documented from an OpenAPI 3.1 spec, and commission rules can pay more on bigger sales or as a partner's results grow.

OpenPartner’s usual path ties a conversion to a person: your site runs the SDK, calls identify() when someone signs up, and later events for that user are credited to the partner whose link they clicked. That works well when the conversion happens in your own app.

Plenty of conversions don’t. A lender funds a loan days after the application. A sales rep closes a deal from a lead form. An affiliate network confirms a sale in its own system. A buyer pays on a Stripe Payment Link, where no page of yours runs at all. None of those systems know your user ids. What they can carry is the click id.

This release adds three things:

  • Conversions by click id, in one API call or through a postback URL.
  • A full API reference, generated from an OpenAPI 3.1 spec.
  • Commission slabs and performance tiers: pay a higher rate on larger sales, or as a partner’s results grow.

Conversions by click id

1. Get the click id to the system that converts

Every click on a partner link gets a click id, a 26-character ULID. By default the click router appends it to your destination as ?cref=<click id>. To put it in a parameter the destination already reads, write the {click_id} macro into the program’s destination URL:

https://apply.lender.example/start?ref={click_id}
https://buy.stripe.com/abc123?client_reference_id={click_id}

The router fills it in and appends no cref. The receiving system stores the id with the lead, application or order.

The macro is strict on purpose: {click_id}, single braces, lower case. Anything else in braces, such as {{click_id}} (Zapier and Make syntax) or {CLICK_ID}, is refused when you save the program, because the router would send it through as literal text and no conversion could ever be matched. A buy.stripe.com destination without client_reference_id={click_id} saves with a warning for the same reason.

2a. Report it with one call

When the conversion happens, post it with the click id and an API key that has the events:write scope. There’s no identify step:

Terminal window
curl -X POST <base>/attribution/conversions \
-H "Authorization: Bearer <your-key>" \
-H "Content-Type: application/json" \
-d '{
"clickId": "01K7ZQ3M5X8V2N4P6R8T0W2Y4A",
"type": "loan_funded",
"value": 5000.00,
"currency": "USD",
"externalEventId": "loan-881:funded"
}'

OpenPartner stitches the click, records the event, attributes it and accrues the commission in one request. From Node, trackConversion() in @openpartner/sdk/server makes the same call.

Every recorded conversion answers 200, and an outcome field says what it came to:

  • attributed: a partner was credited and a commission accrued.
  • attributed with reason no_commission: credited, but nothing accrued, for example because a cap was reached or the program has ended.
  • no_matching_rule: credited, but no commission rule takes this type. Usually a typo’d stage name, like funded for loan_funded.
  • not_attributed, with a reason such as outside_window: no partner was credited.

So you can tell “recorded and earning” apart from “recorded and nothing happened” without opening the dashboard. A commission that accrued is still approved and paid out later, on your usual schedule. Stage names that matched no rule in the last 30 days also show as a warning under Admin → Integrations.

2b. Or hand the sender a postback URL

Affiliate networks and many lenders can only fire a URL. Under Admin → Integrations → Postback URL, Generate postback URL gives you one to paste into their system:

<base>/postback?click_id=CLICK_ID&type=sale&amount=AMOUNT&currency=USD&txn_id=TRANSACTION_ID&token=opcp_…

Replace CLICK_ID, AMOUNT and TRANSACTION_ID with the sender’s own macros. Set type to the conversion name your commission rule pays on (loan_funded, say, not sale), and currency to the currency of the amount.

  • The token can only record conversions. It isn’t an API key and isn’t accepted as one. It’s still in the URL, so treat a postback URL as public once you hand it out, and prefer an API key wherever a sender can set a header.
  • Pin it to the sender’s IP addresses with Allowed source IPs, and rotate or turn it off from the same panel.
  • It answers in plain text, ok attributed or ok not_attributed outside_window, or a 1×1 GIF with &pixel=1 for senders that fire an image pixel.

Retries are safe, if your ids are unique

externalEventId on the API and txn_id on the postback make a retry harmless: the same id answers with the original result and records nothing new. Two rules trip senders up:

  • Ids are unique across your whole account. Reusing one for a different conversion is a 409, and that conversion isn’t recorded.
  • Use one id per stage, not per deal: loan-881:approved, then loan-881:funded. If two senders could both have an order 881, prefix each sender’s ids.

Limits worth knowing

  • Keys and the postback token cover every program. A sender holding one can report a conversion for any of your clicks. Give each sender its own key where you can.
  • A flagged click earns nothing. A click flagged for velocity, or from a revoked partner, is refused with 422 click_flagged.
  • Earlier events aren’t backfilled. Only the conversion you report is attributed.
  • The attribution window still decides who wins the customer. Their first conversion is credited only while the click is inside the program’s window. After that the partner keeps them, and later conversions stay with that partner however old the click, but past the window only commission rules marked recurring pay.

The click conversions guide covers each of these, plus Stripe Payment Links on self-hosted and hosted OpenPartner.

A full API reference

The API you integrate with is now documented at app.openpartner.dev/api/docs: the endpoints your own systems call, with their request and response shapes, error codes, scopes and webhook payloads. Routes that only the dashboard uses, such as bounty setup, aren’t in it. It’s rendered from an OpenAPI 3.1 document at /api/openapi.json, which you can feed to a client generator or an API tool. A self-hosted install serves the same pair from its own API.

The spec is built from the same schemas the API validates requests with, and a contract test in the repository runs real requests against it, so the reference describes what the API actually accepts and returns.

Two worked examples sit alongside it in the repository:

  • An in-app referral guide for running a “Refer & earn” page inside your own app, where customers never sign in to OpenPartner. Your backend creates each customer’s partner and link, and shows them their stats and earnings.
  • A runnable sample of that guide: a dependency-free Node server and one HTML page.

Commission slabs and performance tiers

A commission rule used to pay one rate. Now each rule has a Rate varies option with two shapes:

  • Slabs by value: the rate depends on the size of the conversion. For example, 10% on a loan up to $50,000 and 15% above it.
  • Performance tiers: the rate depends on what the partner has already driven in the program. For example, once their attributed revenue this month reaches $100,000, every further invoice pays 15%.

The rule’s own rate is the base, and you add up to 10 steps above it.

Slabs pay in one of two ways. On a $60,000 loan with 10% up to $50,000 and 15% above:

  • The whole conversion at its slab’s rate (the default): 15% × $60,000 = $9,000. This works for fixed amounts too: a fixed rule pays the slab’s amount.
  • Each slice at its own rate, like tax brackets: 10% × $50,000 + 15% × $10,000 = $6,500. Percent rules only.

Tiers count attributed revenue or attributed conversions over the partner’s lifetime, the calendar month (UTC), or a rolling window of 1 to 3,650 days. They count one event type, the rule’s own or one you pick, and only the partner’s earlier events of that type in the program. A conversion split between partners counts as their share of it (a 50/50 split is half a conversion), and clicks flagged as fraud don’t count.

Limits worth knowing

  • Tiers aren’t retroactive. The conversion that takes a partner over a threshold is paid at the old rate, and the ones after it at the new rate. Earlier commissions are never recomputed.
  • Late or simultaneous data can pay the lower rate. A tier is read from the history recorded when each commission accrues. If an earlier conversion is reported after a later one, or two arrive at the same moment, the later one can be paid at the lower rate. Each commission records the count it used, so the difference is visible.
  • Refunds don’t lower a tier’s count.
  • One rule can have slabs or tiers, not both. For both effects, use two rules: their commissions add up.
  • No currency conversion. Thresholds are compared in each conversion’s own currency, which suits a single-currency program. You can pin thresholds to one currency. Then a slab rule pays its base rate on a conversion in any other currency, and a revenue tier only adds up conversions in the pinned one.

Each commission records the slab or tier that set its rate, so a partner asking “why 15% on this one?” has an answer in the data. Per-partner terms can use slabs and tiers too. See Rate varies for the details.

Get started