Partner API / Live stream
Live stream
wss://api.affiliatepipe.com:2053/v1/streamOne open WebSocket that pushes your clicks, filtered clicks and sales the moment they happen. For live dashboards, bots and alerts, without polling.
wss://api.affiliatepipe.com:2053/v1/streamAuthorization: Bearer header, or as the first messageclick, click.filtered, conversion.created, conversion.approved, conversion.paid, conversion.clawed_backsince replays up to 200 events from the last 24 hoursParameters
eventsstring, optionalComma separated event types to receive. Default: all of them.sinceISO 8601 time, optionalReplay events after this time first (24 hours back at most, 200 events), each marked "replay": true. Use the time of the last event you saw to fill a gap after reconnecting.Both go in the address (?events=click,conversion.created&since=...) or in the auth message. Never put the key in the address: it is refused.
Connect
From a server, send the key as a header. A browser cannot set headers, so it sends {"type":"auth","key":"..."} as its first message, within 10 seconds. Keep the key on your server where you can: anyone who has it can read your figures.
const ws = new WebSocket("wss://api.affiliatepipe.com:2053/v1/stream");
ws.onopen = () => ws.send(JSON.stringify({ type: "auth", key: process.env.AP_KEY }));
ws.onmessage = (e) => {
const ev = JSON.parse(e.data);
if (ev.type === "conversion.created") console.log("Sale on", ev.data.domain, ev.data.commission / 100);
};
ws.onclose = (e) => setTimeout(connect, 3000); // reconnect with since= to catch upimport asyncio, json, os, websockets # pip install websockets
async def main():
url = "wss://api.affiliatepipe.com:2053/v1/stream?events=click,conversion.created"
async with websockets.connect(url, additional_headers={"Authorization": "Bearer " + os.environ["AP_KEY"]}) as ws:
async for msg in ws:
ev = json.loads(msg)
print(ev["type"], ev.get("data", {}).get("domain"))
asyncio.run(main())curl -i -N --http1.1 \ -H "Connection: Upgrade" -H "Upgrade: websocket" \ -H "Sec-WebSocket-Version: 13" -H "Sec-WebSocket-Key: $(openssl rand -base64 16)" \ -H "Authorization: Bearer $AP_KEY" \ https://api.affiliatepipe.com:2053/v1/stream
Messages
Every message is one JSON object with a type. After the key is accepted you get ready, then events as they happen. Replayed events carry "replay": true.
{"type": "ready", "events": ["click", "click.filtered", "conversion.created", "conversion.approved", "conversion.paid", "conversion.clawed_back"], "replayed": 0, "ping_seconds": 25} Every event
conversion.createdA sale through your link is recorded. Commission is pending for 45 days.conversion.approvedThe 45 days have passed with no refund. The commission counts towards your next payout.conversion.paidThe commission was included in a payout to your Stripe account.conversion.clawed_backThe sale was refunded or disputed within 120 days, so its commission is reversed.clickSomeone opened your link and the click was counted.click.filteredA click on your link was filtered and will never earn. reason says why, for example self_referral, bot, duplicate or embedded, as in the public filtered-click log.conversion.created
{
"type": "conversion.created",
"data": {
"id": 412,
"domain": "greyhoundapi.com",
"amount": 4900,
"commission": 490,
"currency": "usd",
"status": "pending",
"recurring": true,
"created_at": "2026-10-20T09:21:11Z"
}
}conversion.approved
{
"type": "conversion.approved",
"data": {
"id": 412,
"domain": "greyhoundapi.com",
"amount": 4900,
"commission": 490,
"currency": "usd",
"status": "approved",
"recurring": true,
"created_at": "2026-10-20T09:21:11Z"
}
}conversion.paid
{
"type": "conversion.paid",
"data": {
"id": 412,
"domain": "greyhoundapi.com",
"amount": 4900,
"commission": 490,
"currency": "usd",
"status": "paid",
"recurring": true,
"created_at": "2026-10-20T09:21:11Z"
}
}conversion.clawed_back
{
"type": "conversion.clawed_back",
"data": {
"id": 412,
"domain": "greyhoundapi.com",
"amount": 4900,
"commission": 490,
"currency": "usd",
"status": "clawed_back",
"recurring": true,
"created_at": "2026-10-20T09:21:11Z"
}
}click
{
"type": "click",
"data": {
"id": "clk_9f2a41c07be3d855e1a0",
"domain": "greyhoundapi.com",
"country": "GB",
"device": "desktop",
"referrer_host": "reddit.com",
"utm_source": "reddit",
"utm_medium": "social",
"utm_campaign": "october",
"created_at": "2026-10-20T09:14:02Z"
}
}click.filtered
{
"type": "click.filtered",
"data": {
"id": "clk_4b70e2a19c38f6d02e5b",
"domain": "greyhoundapi.com",
"country": "US",
"device": "bot",
"referrer_host": null,
"utm_source": null,
"utm_medium": null,
"utm_campaign": null,
"created_at": "2026-10-20T09:14:40Z",
"reason": "bot"
}
} conversion.* data is the same as the webhook events. Money is in cents.
Staying connected
- We send a WebSocket ping every 25 seconds; any client library answers it. You can also send
{"type":"ping"}and getpong. - The stream restarts about once an hour and closes with code
1012. Reconnect after a few seconds withsinceset to your last event's time, and nothing is missed. - Close codes:
4001wrong, missing or revoked key, or a paused account;4029more than 3 streams on one key. - For sales you must never miss, use webhooks as well: they are retried for a day if you are offline.