Send lead and sale events from your server
Record a sign-up and a purchase against the click that produced them — request bodies, the click id, amounts in cents, and idempotency.

Two requests do the whole job: one when someone identifies themselves, one when they pay. Both run on your server, with a secret API key, and both quote the click id that brought the person in.
Availability
- Plan: Pro and above, or the 14-day trial.
- Where: your backend. Create the key under Settings → API Keys.
Before you start
- Conversion tracking must be on for the link or QR code — Set up conversion tracking on your website.
- Have the click id available. It arrives as
?cq_id=…on your destination URL and is stored in thecq_idcookie. - Decide on a stable identifier for each customer in your own system. It ties the sale to the lead later.
Record a lead
Call this when a visitor becomes identifiable — sign-up, form submission, trial start.
curl -X POST https://api.codeqr.io/track/lead \
-H "Authorization: Bearer codeqr_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"clickId": "qcdyQHsX1oajEock",
"eventName": "Sign up",
"customerExternalId": "user_123",
"customerName": "Ada Lovelace",
"customerEmail": "ada@example.com"
}'The response confirms the click and returns the customer CodeQR now knows:
{
"click": { "id": "qcdyQHsX1oajEock" },
"customer": {
"id": "cmszcuwyr0003fdlpmoy3814u",
"name": "Ada Lovelace",
"email": "ada@example.com",
"externalId": "user_123",
"country": "BR"
}
}clickId— read it from thecq_idcookie or from the query parameter. An empty string is allowed: CodeQR then looks for an existing customer with the samecustomerExternalIdand reuses their click.eventName— free text, up to 255 characters. It is also the handle a later sale can point at.customerExternalId— required, your own id. Everything the customer does afterward attaches to it.
Record a sale
curl -X POST https://api.codeqr.io/track/sale \
-H "Authorization: Bearer codeqr_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"customerExternalId": "user_123",
"amount": 4990,
"currency": "usd",
"eventName": "Purchase",
"paymentProcessor": "stripe",
"invoiceId": "INV-001"
}'{
"eventName": "Purchase",
"customerId": "user_123",
"amount": 4990,
"paymentProcessor": "stripe",
"invoiceId": "INV-001",
"currency": "usd",
"metadata": null
}amountis in the smallest unit of the currency —4990is $49.90. This is the single most common mistake, and it is silent: a sale of49.90records 49 cents.paymentProcessoracceptsstripe,shopify,polar,paddle,revenuecat,customandmanual.invoiceIdmakes the call idempotent. Sending the same invoice twice returns the same response and does not double-count — which matters because payment webhooks retry.leadEventNameattaches the sale to a specific earlier lead instead of the most recent one.
Sales that arrive before the lead
You do not have to send them in order. A sale for a customer CodeQR has never seen creates the lead implicitly, provided the sale carries enough to find the click — the customerExternalId you used earlier, or a clickId.
Verify it works
Ask the link for its counters a few seconds later:
curl -sS "https://api.codeqr.io/links/info?domain=go.example.com&key=summer" \ -H "Authorization: Bearer codeqr_xxxxxxxxxxxxxxxxxxxxxxxx"
{ "key": "summer", "clicks": 1, "leads": 1, "sales": 1, "saleAmount": 4990 }Then open Customers and select the person: the panel shows the click, the lead and the sale in order, with the amount.
Troubleshooting
The call returns 404
The clickId does not exist or has expired. Confirm the value you read from the cookie, and remember that without conversion tracking the click id only lives for an hour.
The call returns 200 and nothing is recorded
A lead with an empty clickId and no matching customer is accepted and discarded — there is nothing to attribute it to. Send the click id, or send a customerExternalId CodeQR has already seen.
The revenue is a hundred times too small
amount is in cents. 4990, not 49.90.
The same sale counted twice
The two calls used different invoiceId values, or none. Idempotency is keyed on that field and holds for seven days.
The sale does not appear in the Events tab
Listing sale events currently fails. Use Customers, or the link counters shown above; both are correct.