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:
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.attributedwith reasonno_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, likefundedforloan_funded.not_attributed, with a reason such asoutside_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¤cy=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 attributedorok not_attributed outside_window, or a 1×1 GIF with&pixel=1for 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, thenloan-881:funded. If two senders could both have an order881, 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
- Report conversions by click id: Conversions by click id
and the
POST /attribution/conversionsreference. - Browse the API: app.openpartner.dev/api/docs.
- Set up slabs or tiers: Admin → Programs → Edit, then Rate varies next to a rule’s rate. The Programs guide has worked examples.