Core API
Conversions
Report a sale to the affiliate platform. A conversion is your record that a real order happened — the API accepts it for asynchronous attribution and commission, then drives its status from the order's lifecycle.
Send POST /api/v1/conversions every time a customer completes an order you want attributed to an affiliate. The endpoint returns 202 Accepted the moment it has safely recorded the event — it does not attribute or pay a commission synchronously. Attribution (matching the order to an affiliate click) and commission calculation run asynchronously in a worker, so a successful 202 means “accepted”, not “commissioned”.
Request fields
The request body is a JSON object (CreateConversionDto). Amounts are sent as decimal strings to avoid floating-point rounding.
| Field | Type | Required | Description |
|---|---|---|---|
clickId | string | Optional | The captured affiliate_click_id. This is what drives attribution — send the value you stored at landing time. See Click ID. |
externalOrderId | string ≤160 | Required | Your order id. Must be unique per merchant — it is the idempotency key for this conversion. |
externalProductId | string | Optional | The product / SKU ordered. Enables product-level commission rules. |
orderAmount | decimal string | Required | Order total as a string, e.g. "2999.00". Drives the commission amount. |
currency | string | Optional | ISO currency code, e.g. NPR. |
orderStatus | enum | Optional | One of pending, confirmed, delivered, cancelled, returned, refunded. Drives the conversion lifecycle. See Order status. |
orderedAt | ISO 8601 | Optional | When the order was placed. Defaults to receipt time. |
couponCode | string | Optional | Coupon used on the order, for coupon-based attribution. |
metadata | object | Optional | Free-form key/value context stored with the conversion. |
Endpoint
Report a conversion
Report an order/conversion. Send the click_id you captured for accurate attribution. Duplicate (merchant + externalOrderId) is safely ignored and returns status DUPLICATE.
{
"clickId": "CLK_example123",
"externalOrderId": "ORD-1001",
"externalProductId": "SKU-100",
"orderAmount": "2999.00",
"currency": "NPR",
"orderStatus": "confirmed",
"orderedAt": "2026-06-14T09:30:00Z",
"couponCode": "DASHAIN10"
}{
"eventId": "cnvevt_ab12…",
"status": "RECEIVED",
"duplicate": false
}Duplicate & idempotency behavior
A conversion is unique on (merchant + externalOrderId). Re-sending the same externalOrderId never creates a second conversion and never produces a second commission — it returns 202 with status: "DUPLICATE" and the conversionId of the existing record. A brand-new ingest instead returns 202 with { eventId, status: "RECEIVED", duplicate: false }.
You can additionally send an optional X-Idempotency-Key header to make the HTTP layer itself safe to retry (for example, the same key after a network timeout). The domain unique key on externalOrderId is the durable guarantee; the header protects in-flight retries. See Idempotency for the full contract.
Commission calculation hierarchy
Once a conversion is attributed, the worker selects the commission rule by specificity, using the first rule that matches:
- Product — a rule for the ordered
externalProductId. - Campaign — the rule on the campaign the click belonged to.
- Merchant default — your account-wide default rate.
- Platform default — the fallback rate if nothing above is configured.
More specific rules win, so a product rule overrides a campaign rule, which overrides your merchant default, which overrides the platform default.
Tracking confidence
Attribution confidence depends on whether a valid clickId arrives with the conversion:
HIGH
A valid clickId that belongs to your merchant matched a recorded click. The conversion is attributed to that affiliate and a commission is created automatically.
LOW / fallback
The clickId is missing, or it does not match a known merchant-owned click. The conversion is still recorded, but it is not auto-commissioned and may need admin review before any payout. Always send the captured clickId — see Click ID for how to capture and persist it.
Conversion status lifecycle
A conversion advances through these states, driven by the order status you report:
- received — the event was accepted (the
202). - attributed — matched to an affiliate click via
clickId. - commission created — a commission is computed from the hierarchy above.
- hold — the commission is held pending the order settling (e.g. return window).
- payable — the order is final, so the commission is eligible for payout.
Status transitions are driven by order updates. A cancelled, returned or refunded order reverses a held or payable commission. Keep statuses current via Order status.
Failed events & safe retry
Treat 4xx as a request problem to fix (bad signature, validation error) — do not blindly retry those. On 5xx or a timeout, retry with the same X-Idempotency-Key (and the same externalOrderId). Because the conversion is unique on externalOrderId, a retry that actually succeeded the first time simply returns the existing record as a DUPLICATE — never a double commission. Remember to regenerate X-Timestamp and X-Signature for each retry attempt.
Signed example (cURL)
A full request that signs the exact body bytes it sends:
#!/usr/bin/env bash
# Report a confirmed order to the affiliate API.
# Requires: openssl, curl. Replace the key id and signing secret.
API_BASE="https://api.example.com"
API_KEY="live_pk_xxxxxxxxxxxxxxxx"
SIGNING_SECRET="sk_live_xxxxxxxxxxxxxxxxxxxxxxxx"
# Compact, byte-exact JSON body. The signature is computed over THIS exact text,
# so sign the raw bytes you actually send (do not re-serialise after signing).
BODY='{"clickId":"clk_8Q2c0r7m4w","externalOrderId":"SHOP-100245","externalProductId":"SKU-RED-42","orderAmount":"2999.00","currency":"NPR","orderStatus":"confirmed","orderedAt":"2026-06-14T09:30:00Z","couponCode":"AFF10","metadata":{"channel":"instagram"}}'
TS="$(date +%s)"
# X-Signature = lowercase hex HMAC-SHA256 of "${TS}.${BODY}" with your secret.
SIG="$(printf '%s.%s' "$TS" "$BODY" \
| openssl dgst -sha256 -hmac "$SIGNING_SECRET" -r \
| cut -d ' ' -f1)"
curl -sS -X POST "$API_BASE/api/v1/conversions" \
-H "Content-Type: application/json" \
-H "X-Api-Key: $API_KEY" \
-H "X-Timestamp: $TS" \
-H "X-Signature: $SIG" \
-H "X-Idempotency-Key: SHOP-100245" \
--data-raw "$BODY"
# 202 Accepted (new): {"eventId":"...","status":"RECEIVED","duplicate":false}
# 202 Accepted (duplicate): {"status":"DUPLICATE","conversionId":"...","duplicate":true}