The partner API
For a network or a publisher platform that places stores' links on many sites. List the programs that take networks, apply to them, get your links, and read every order and payout back by the sub-ID that names the site. Stores pay you through PayPal, as they pay every creator, so add your PayPal address under Payouts before your first payout. The API is free.
How a network gets its key
- Sign up and, on the second step, choose “We are a network or a publisher platform”. Give the company's legal name and website: the legal name is the party in every agreement you sign.
- Open Profile, then API access, and press “Make a key”. The key is shown once; Partnely keeps only a hash of it. A new key stops the old one at once.
- Set your postback address in the same panel to have orders sent to you as they happen.
Requests and answers
Send the key as Authorization: Bearer pn_partner_…. Every answer is JSON in snake case: { "data": …, "meta": … }, or { "error": { "code", "message", "details" } } with a status that says what went wrong. Money is in cents of the currency named beside it, and a percent rate is in basis points (1000 is 10%). Lists take limit (at most 200) and offset.
A key may make 600 requests a minute; past it the answer is 429 with Retry-After. An unknown key, a closed account or a creator's account is 401.
List programs
GET /api/partner/v1/programs?updated_since=2026-10-01&limit=50&offset=0 lists every program that takes networks, by name. meta carries total and next_offset. GET /api/partner/v1/programs/<slug> is one; a program that is not listed is a 404.
curl -H "Authorization: Bearer pn_partner_…" https://partnely.app/api/partner/v1/programs/marlowe-skin
{ "data": {
"slug": "marlowe-skin",
"name": "Marlowe Skin",
"program_kind": "affiliate",
"hosts": ["marloweskin.example.com", "marlowe-skin.myshopify.com"],
"commission": { "type": "percent", "value": 1200, "window_days": 30, "renewals": false, "renewals_limit": null, "gift_cards": false },
"payouts": { "method": "paypal", "schedule": "monthly", "minimum_cents": 2500, "currency": "USD" },
"reviews_by_hand": true,
"join_url": "https://partnely.app/join/marlowe-skin",
"agreement_url": "https://partnely.app/join/marlowe-skin/agreement",
"deep_link_rule": "Add ?to=<the page's address> to the tracked link …",
"questions": [],
"required_channels": [],
"asks_audience": false,
"terms_stamp": "2::USD:",
"membership_status": null,
"updated_at": "2026-10-17T08:23:48.698Z"
} }Read the agreement at agreement_url before you apply: applying signs it.
Apply
POST /api/partner/v1/programs/<slug>/apply signs the program's agreement with signature, the legal name you signed up with, and applies. The signature must be that name exactly (any other is 400 invalid_signature, and nothing is signed). Send the terms_stamp you read, which is required: if the terms changed since, nothing is signed and the answer is 409 terms_changed; GET the program again and send its new stamp. Answer the program's questions in answers by their id, and its required_channels in channels.
Where asks_audience is true, send audience_countries: one to five country codes where your audience is, the largest first. A question of kind promotion is answered with an array of the method keys its options lists, every way you promote.
curl -H "Authorization: Bearer pn_partner_…" -H "Content-Type: application/json" \
-d '{ "signature": "Linkwell Media ApS", "terms_stamp": "2::USD:" }' \
https://partnely.app/api/partner/v1/programs/marlowe-skin/applyA program that approves at once answers 200 with status: approved; one that reviews by hand answers 202 with status: pending, and the store decides. Applying twice is 409 already_applied. A program that is not taking members, or a store's rule that declines the application, answers 403 with the same sentence the program page shows; an account Partnely has paused answers 403 creator_suspended. An account whose email address is not confirmed yet answers 403 email_unconfirmed, and nothing is written; open the link we sent when the account was made, then apply.
Where you stand
GET /api/partner/v1/programs/<slug>/membership answers your status, the rate in force (your own, a group's, or the program's, named in source), your coupon_code where the store set one, and the address of the agreement you signed.
Site rates
List the sites you place links on under Sites in your dashboard, and a store may pay one of them a rate of its own. The membership answers them as site_rates: the site's host, the rate's type and its value, in the program's own kind of rate. A site rate is always more than the rate in force, and it pays on an order whose click came from that site.
"site_rates": [ { "site": "harborlane.example", "type": "percent", "value": 1500 } ]- A click counts for a site when the visitor's browser says it came from a page on that site or one of its subdomains.
- A click whose browser names no page earns your own rate, and the sub-ID you add to a link never chooses a rate.
- A click counts for a site only where every hop between the site and the link keeps the browser's default referrer policy, or sends at least the origin.
Links and sub-IDs
Once approved, GET /api/partner/v1/programs/<slug>/links answers your store link and the two forms you build on it. Rewrite links on your publishers' sites yourself:
https://partnely.app/r/m89eqp7?to=https%3A%2F%2Fmarloweskin.example.com%2Fproducts%2Fserum&sid=dailyfolio.example
tois the page the reader opens, URL-encoded; it must be on one of the program'shosts. Anything else opens the store's home page and the click still counts.sidnames the site, in your own words. The first 64 characters are kept on the click and on the order that click earns, and you read them back on every order; a longer one is cut there, so keep it short.- Only a person's click counts: a request with no browser user agent, or one from a crawler, a link preview or a command-line tool such as curl, is redirected the same way but not counted. Test with a browser, or send a browser's User-Agent.
POST /api/partner/v1/programs/<slug>/links with product_id or destination_url and a label makes a link of its own.
Products
Once approved, GET /api/partner/v1/programs/<slug>/products?limit=&offset= lists the program's products on sale, each with your own tracked link to its page. id is what POST …/links takes as product_id, and price_cents is in the store's own currency, or null where the product carries no price. meta carries total and next_offset.
{ "data": [ {
"id": "1m54fh7c2q0wz8yq3n6t2r4v",
"external_id": "vitamin-c-serum",
"title": "Vitamin C Serum",
"price_cents": 4800,
"currency": "USD",
"categories": ["Serums"],
"image_url": "https://marloweskin.example.com/images/serum.jpg",
"product_url": "https://marloweskin.example.com/products/vitamin-c-serum",
"tracked_url": "https://partnely.app/r/m89eqp7?to=https%3A%2F%2Fmarloweskin.example.com%2Fproducts%2Fvitamin-c-serum"
} ], "meta": { "limit": 1, "offset": 0, "total": 14, "next_offset": 1 } }Orders
GET /api/partner/v1/conversions?from=&to=&program=&sub_id=&status=&page= answers your own orders, newest first. status is pending, approved, paid (the payout that carried it was sent) or reversed; a reversed order says why in reversal. Approved commission is final.
A program's commission is its base rate. A product, a collection, a campaign or a commission rule of the store may pay a different rate on a line, so commission_lines gives each line's rate and what it earned; source is one of program, group, own, site, campaign, category, product or rule, and name names the collection or the rule.
{
"id": "1m54fhqk9qc9r8t9nts59417",
"order_id": "1002",
"program": "marlowe-skin",
"occurred_at": "2026-10-17T08:27:11.847Z",
"status": "pending",
"reversal": null,
"subtotal_cents": 8400,
"currency": "USD",
"commission_cents": 1512,
"commission_lines": [
{ "title": "Vitamin C Serum", "quantity": 1, "subtotal_cents": 8400, "commission_cents": 1512,
"rate": { "type": "percent", "value": 1800, "source": "category", "name": "Serums" } }
],
"sub_id": "dailyfolio.example",
"click_id": "1m54fh5y5452x7gb0rjsmyy5zxyxspr0",
"click_at": "2026-10-17T08:26:53.766Z",
"link_code": "m89eqp7",
"campaign": null,
"coupon_code": null,
"approved_at": null,
"reversed_at": null
}Payouts
GET /api/partner/v1/payouts?program= answers what each store paid you, one row per payout and currency, with PayPal's reference.
Report by sub-ID
GET /api/partner/v1/report?group=sub_id&from=&to= answers clicks, orders and commission per sub-ID, 30 days back by default. Commission is listed per currency and never added across them.
{ "data": [ { "sub_id": "dailyfolio.example", "clicks": 1, "orders": 2, "commission": [ { "currency": "USD", "cents": 1680 } ] } ] }Postback
Set one https address under API access. Each of your own orders as it is recorded, approved or reversed, and each payout as it is paid, is posted there as one signed JSON body: the event in type, the store in brand_id, and the order (an order row as above) or the payout in data.
The address is posted to from our servers, so it is held to the rule a store's own webhooks are: A public https address on the standard port: a domain name that points to a public address, so not localhost, an IP on a private network or a port of its own. To test on your own machine, run a tunnel that gives your listener a public https address and save that. Then press Send a test, or call the test endpoint below, and watch the delivery arrive.
- A refused delivery is tried again after 1 minute, 5 minutes, 30 minutes, 2 hours and 12 hours, then given up.
- 20 failures in a row pause the postback and email you once; press Resume once it is fixed.
POST /api/partner/v1/postback/testsends a sample withtest: true;GET /api/partner/v1/postbackreads its state and the last deliveries.
A creator sets the same postback on Links, under More ways to use your links.
Checking the postback
Every postback carries X-Partnely-Event, X-Partnely-Delivery (dedupe on it), X-Partnely-Timestamp and X-Partnely-Signature: v1= and the hex HMAC-SHA256 of the timestamp, a dot and the raw body, keyed with your signing secret. The secret is shown once, right after you save the address under API access (every save makes a new one), so copy it then. It is the recipe a store's own webhooks use. Refuse a timestamp more than five minutes from your clock.
secret whsec_example
timestamp 1700000000
body {"id":"evt_2","type":"order.recorded"}
signed 1700000000.{"id":"evt_2","type":"order.recorded"}
header X-Partnely-Signature: v1=26a6f8e28c33eb8c08a9948db6295c64f2d9c6284f30a5612796bd1cdd552356A creator with one site of their own uses the publisher tag for links and the same postback for orders.