Conversions API
Register conversions when users complete desired actions. For affiliate networks, use the server-to-server postback instead.
Endpoint
No authentication: it is called from your own pages. It never reveals whether a click ID exists, so it can't be used to probe for valid IDs.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| click_id | string | Yes | The click ID from the tracked link (utm_click, or wherever you placed {click_id}) |
| value | number | No | Revenue for this conversion |
| currency | string | No | ISO code, defaults to USD when a value is sent |
| event_type | string | No | sale | lead | signup | any label |
| event_name | string | No | Human label, e.g. Checkout |
| order_id | string | No | Order ID. A second order from the same click counts separately |
| user_id | string | No | Your internal user or customer ID |
| name | string | No | The customer's name |
| string | No | The customer's email address | |
| host | string | No | Domain where the conversion happened |
| page_url | string | No | Page where the conversion happened |
| metadata | string | No | Anything else, as a JSON string |
Example Request
curl -X POST "https://api.gettrack.link/api/conversions" \
-H "Content-Type: application/json" \
-d '{
"click_id": "C9HW979UQ47Q69",
"event_type": "sale",
"value": 49.99,
"currency": "USD",
"order_id": "ord_987",
"user_id": "cust_12345",
"email": "[email protected]"
}'Responses
{ "success": true }The same response comes back for an unknown click ID and for a repeat of a conversion already recorded. Only a malformed body is rejected:
HTTP 400
{
"statusCode": 400,
"message": ["click_id must be a string"],
"error": "Bad Request"
}JavaScript Example
async function registerConversion(clickId, order) {
await fetch('https://api.gettrack.link/api/conversions', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
click_id: clickId,
event_type: 'sale',
value: order.total,
currency: order.currency,
order_id: order.id,
}),
});
}
// t.js stores the click ID under this key when the visitor lands
const clickId = localStorage.getItem('link_track_id');
if (clickId) {
await registerConversion(clickId, { total: 49.99, currency: 'USD', id: 'ord_987' });
localStorage.removeItem('link_track_id');
}Reading conversions back
Recording is public and unauthenticated (anyone with a click ID can report a conversion for it). Reading needs an API key, and lives on the v1 API:
GET https://api.gettrack.link/api/v1/conversionsGET https://api.gettrack.link/api/v1/conversions/unmatchedThe list takes link_id, status (pending, approved, declined, refunded), event, start_date, end_date, limit (100 max) and offset. Each row carries the click it belongs to, including the short click ID your network sends back. /unmatched returns postbacks whose click ID matched no click, kept for 90 days — nothing was recorded for those, so they are not conversions.
There is no write endpoint on v1: conversions come from your network's postback or the tracking script, never from a key. The same reads are available as tl conversions ls in the CLI and the list_conversions MCP tool, and the conversion.created and conversion.updated webhooks push each one as it happens.
Notes
- A click can have several conversions: one per event type and order ID
- Conversions appear on the link detail page and the Conversions page
- Conversion rate is the share of clicks that produced at least one conversion
- The click ID is a 14-character code; letter case doesn't matter