Skip to content

REST API

Authentication, pagination and endpoints of the REST API v1.

23 min read

Browse documentation

The ArcTrack REST API gives programmatic access to one workspace: offers and their events, partners (affiliates), advertisers, reports, the raw click log, conversions, outgoing postbacks and tracking links. It speaks JSON over HTTPS and is described by an OpenAPI 3.1 document you can load into Postman, Insomnia or a code generator:

URL
https://app.168-144-125-119.sslip.io/api/v1/openapi.json

Base URL of every endpoint below:

URL
https://app.168-144-125-119.sslip.io/api/v1

Authentication#

Every request carries an API key in the Authorization header:

Shell
curl https://app.168-144-125-119.sslip.io/api/v1/me \
  -H "Authorization: Bearer atk_AbCd1234_…"

Keys look like atk_<8-character prefix>_<40-character secret>. Network staff create keys under Settings → API keys; partners create their own key in the partner portal (API key) and advertisers in the advertiser portal (Profile → API key). The full key is shown once — we only store a SHA-256 hash of it, so a lost key cannot be recovered: revoke it and create a new one. The prefix is shown in the panel so you can tell keys apart. X-Api-Key: atk_… is accepted as an alternative header.

Roles#

The role of a key decides what it can see and do. The workspace (and, for partner/advertiser keys, the partner or advertiser) always comes from the key — never from the request.

RoleAccess
adminEverything in the workspace: read and write offers, events, partner access, affiliates, advertisers, conversions and postbacks; reports and clicks.
analystRead-only access to the same data. Every write returns 403 read_only_key.
affiliateThe partner-portal view: offers the partner can run or request (with their own payout and tracking link), their own clicks, conversions and reports — payout money only, never revenue, profit or advertiser data — and full control over their own postbacks.
advertiserThe advertiser-portal view: their own offers, the clicks, conversions and reports of those offers with revenue (their cost) only — never payout, profit, partner names or partner sub IDs.

Requests for a disabled workspace, a partner that is not active, or an inactive advertiser are refused with 403.

Conventions#

  • JSON in, JSON out. Send bodies with Content-Type: application/json (max 512 KB). Unknown fields in a body are rejected, so typos never pass silently.
  • Money is always a decimal string with 4 decimals ("12.5000"), in the currency of the row. On input you may send a string ("12.50") or a number; at most 4 decimals are accepted, and thousands separators like "1,234.50" are fine but ambiguous input like "1,5" is refused.
  • Times are ISO-8601 UTC instants ("2026-10-10T08:15:30.123Z").
  • IDs are integers, except conversions (UUID) and clicks (32 hex characters). Offers and affiliates can also be addressed by their public code in paths: /offers/Kx7Lm2Q = /offers/12.
  • Date ranges (from, to, tz, preset) work the same everywhere:
    • from / to as dates (2026-10-01) are calendar days in the time zone tz (default: the workspace time zone); a date in to includes that whole day.
    • from / to as timestamps with a zone (2026-10-01T00:00:00Z, 2026-10-01T05:30:00+05:30) are exact instants; to is exclusive.
    • Instead of from/to: preset=today|yesterday|last7|last30|this_month|last_month, always resolved against "today" in tz.
    • Responses echo the absolute range that was used: "range": {"from": …, "to": …, "tz": …}.

Pagination#

Lists return a page of results and an opaque cursor:

JSON
{
  "data": [ { "id": 12, "…": "…" } ],
  "next_cursor": "eyJrIjoib2ZmZXJzIiwidCI6IjIwMjYtMTAtMDFUMDk6MzA6MDAuMDAwMDAwWiIsImkiOiIxMiJ9"
}

Pass next_cursor back as ?cursor= with the same filters to get the next page; next_cursor is null on the last page. Results are ordered newest first (by creation time, or by conversion/click time for conversions and clicks), and the cursor is a keyset position, so rows created while you page never shift or repeat results. Use limit to choose the page size (defaults and maximums are listed per endpoint). A cursor from one list is refused by another with 400 invalid_cursor.

Reports use limit + offset instead (see Reports).

Errors#

Errors use a stable shape and a meaningful HTTP status:

JSON
{
  "error": {
    "code": "validation_failed",
    "message": "The request body failed validation.",
    "details": { "fields": { "payout": "Enter a decimal amount with at most 4 decimals, e.g. \"12.50\".", "bogus": "Unknown field." } }
  }
}
StatuscodeMeaning
400invalid_parameterA query parameter is invalid (details.param names it).
400invalid_cursorThe cursor is malformed or belongs to another list.
400invalid_jsonThe body is empty or not valid JSON.
400validation_failedThe body failed validation (details.fields maps field paths to messages).
400invalid_requestThe request is not possible as sent (message explains why).
401missing_api_key, invalid_api_keyNo key, a malformed key, or an unknown/revoked key.
403forbiddenThe key's role may not use this endpoint or object.
403read_only_keyAn analyst key tried to change something.
403workspace_inactive, affiliate_inactive, advertiser_inactive, not_approvedThe account is not active, or the partner is not approved for the offer.
404not_foundThe object does not exist or is not visible to this key.
409conflictA unique value is taken (details.field), e.g. an offer code or an adv_txid.
409in_useThe object is still referenced (e.g. an event with conversions).
409no_tracking_domainThe workspace has no active tracking domain to build links with.
413payload_too_largeBody over 512 KB.
415unsupported_media_typeBody not sent as application/json.
429rate_limitedToo many requests — see below.
500internal_errorUnexpected error; the message carries a request id for support.

Every response carries an X-Request-Id header — include it when you contact support.

Rate limits#

Each key may make 600 requests per minute (fixed one-minute windows). Every response reports the budget:

HeaderMeaning
X-RateLimit-LimitRequests allowed per window (600).
X-RateLimit-RemainingRequests left in the current window.
X-RateLimit-ResetSeconds until the window resets.

Over the limit you get 429 rate_limited with a Retry-After header (seconds). Back off until then. For bulk reads prefer one report over many small requests, and the CSV export over paging through very large reports.

Account#

GET /me#

Who is calling: the key, its workspace, the partner/advertiser it belongs to, its permissions and the rate-limit state.

Shell
curl https://app.168-144-125-119.sslip.io/api/v1/me -H "Authorization: Bearer $ATK"
JSON
{
  "data": {
    "key": { "id": 7, "name": "BI export", "role": "analyst" },
    "workspace": { "id": 1, "slug": "demo", "name": "Demo Network", "timezone": "Asia/Kolkata", "currency": "USD" },
    "affiliate": null,
    "advertiser": null,
    "permissions": ["dashboard.read", "reports.read", "offers.read", "…"],
    "rate_limit": { "limit": 600, "remaining": 598, "reset_sec": 41 }
  }
}

Offers#

GET /offers#

ParameterDescription
statusComma-separated: active, paused, pending, archived.
visibilityComma-separated: public, approval, private.
advertiser_idStaff keys: offers of one advertiser.
accessPartner keys: approved, pending, rejected or none (never requested).
idsComma-separated offer ids.
qName contains / exact code.
limit, cursorPage size (default 50, max 200) and cursor.
Shell
curl "https://app.168-144-125-119.sslip.io/api/v1/offers?status=active&limit=2" -H "Authorization: Bearer $ATK"
JSON
{
  "data": [
    {
      "id": 12,
      "code": "Kx7Lm2Q",
      "name": "Summer Sale — US",
      "status": "active",
      "visibility": "approval",
      "advertiser_id": 3,
      "category": "Retail",
      "description": "",
      "restrictions": "No incent traffic.",
      "preview_url": "https://shop.example.com/summer",
      "destination_url": "https://shop.example.com/summer?cid={click_id}&s1={sub1}",
      "fallback_url": "",
      "allowed_domains": [],
      "currency": "USD",
      "revenue_type": "rpa",
      "revenue": "7.0000",
      "payout_type": "cpa",
      "payout": "5.0000",
      "caps": { "conversions": { "daily": 100 } },
      "targeting": { "countries_allow": ["US"] },
      "conversion_method": "any",
      "unique_per_click": true,
      "attribution_days": 30,
      "default_status": "approved",
      "thumbnail_url": "",
      "tags": ["retail"],
      "is_featured": false,
      "events": [
        { "id": 40, "code": "default", "name": "Conversion", "revenue_type": "rpa", "revenue": "7.0000",
          "payout_type": "cpa", "payout": "5.0000", "is_default": true, "is_private": false, "allow_multiple": false }
      ],
      "created_at": "2026-10-01T09:30:00.000Z",
      "updated_at": "2026-10-08T14:02:11.000Z"
    }
  ],
  "next_cursor": null
}

What each role sees:

  • Partner keys get offers they can run or request — public and approval-required offers, plus private offers they are approved for — with payout_type/payout after their custom payout (CPC offers always show the offer's own per-click rate: custom payouts do not apply to click-priced offers), non-private events with their payouts, and three extra fields: access_status (approved, pending, rejected or null), can_run and tracking_link (their ready-to-use link while can_run is true). Revenue, advertiser, destination URL, fallback, caps and allowed domains are never included.
  • Advertiser keys get their own offers with revenue fields (their cost) and every event, but no payout.

GET /offers/{id}#

{id} is the numeric id or the offer code. Returns {"data": {…offer…}} in the same shape as the list.

POST /offers#

Admin keys. Creates the offer and its default event (with the offer's revenue and payout); events adds more events (goals). Omitted fields take the defaults shown.

FieldDefaultNotes
name—Required, max 200 characters.
destination_url—Required. Absolute http(s) URL; may contain macros like {click_id}.
codegenerated6-10 letters/digits used in tracking links; 409 conflict when taken.
advertiser_idnullAn advertiser of your workspace.
statusactiveactive, paused, pending, archived.
visibilityapprovalpublic (any active partner may run it), approval, private.
revenue_type, revenuerpa, "0"rpa per action, rps percent of sale amount (0-100), rpc per unique click, dynamic (from the postback).
payout_type, payoutcpa, "0"cpa per action, cps percent of sale amount (0-100), cpc per unique click.
currencyworkspace currencyISO-4217.
caps{}{"clicks":{"daily":5000},"conversions":{"daily":100,"total":1000},"payout":{"monthly":"2500.00"}} — calendar periods in the workspace time zone.
targeting{}{"countries_allow":["US"],"countries_deny":[],"devices":["mobile"],"os":["android"]}.
conversion_methodanyany, s2s, pixel.
unique_per_clicktrueOne conversion per click per event.
attribution_days301-365.
default_statusapprovedStatus of new conversions: approved or pending.
fallback_url, preview_url, thumbnail_url""Absolute URLs or empty.
allowed_domains[]Extra hosts allowed as transparent url= targets (example.com, *.example.com).
category, description, restrictions, tags, is_featuredempty
events[][{code, name, revenue_type, revenue, payout_type, payout, is_private, allow_multiple}]
Shell
curl -X POST https://app.168-144-125-119.sslip.io/api/v1/offers \
  -H "Authorization: Bearer $ATK" -H "Content-Type: application/json" \
  -d '{
        "name": "Summer Sale — US",
        "destination_url": "https://shop.example.com/summer?cid={click_id}&s1={sub1}",
        "advertiser_id": 3,
        "revenue": "7.00",
        "payout": "5.00",
        "targeting": { "countries_allow": ["US"] },
        "events": [{ "code": "sale", "name": "Sale", "payout_type": "cps", "payout": "10", "revenue_type": "rps", "revenue": "15" }]
      }'

Returns 201 with {"data": {…offer…}}.

PATCH /offers/{id}#

Admin keys. Send only the fields to change (same fields as create, except code and events). Changing revenue_type, revenue, payout_type or payout also updates the default event so every conversion path uses the new terms.

Click-priced offers (payout_type: "cpc", revenue_type: "rpc") are credited per unique click at the offer's own rate in reports and billing, so they do not support per-partner overrides on that side: switching an offer to cpc (or rpc) while it has partner payout (or revenue) overrides returns 400 validation_failed with the field in details.fields — remove the overrides in the panel first.

Shell
curl -X PATCH https://app.168-144-125-119.sslip.io/api/v1/offers/Kx7Lm2Q \
  -H "Authorization: Bearer $ATK" -H "Content-Type: application/json" \
  -d '{"status": "paused"}'

Events#

Method & pathDescription
GET /offers/{id}/eventsEvents as the key may see them (partner keys: non-private events with payout only).
POST /offers/{id}/eventsAdmin keys. Body {code, name, revenue_type, revenue, payout_type, payout, is_private, allow_multiple}. code is 1-32 letters, digits, _ . -; default is reserved.
PATCH /offers/{id}/events/{event}Admin keys. {event} is the event id or code. The default event's code cannot change.
DELETE /offers/{id}/events/{event}Admin keys. Only events without conversions (409 in_use otherwise — mark them private instead). The default event cannot be deleted. Returns 204.
Shell
curl -X POST https://app.168-144-125-119.sslip.io/api/v1/offers/12/events \
  -H "Authorization: Bearer $ATK" -H "Content-Type: application/json" \
  -d '{"code": "deposit", "name": "First deposit", "revenue": "40", "payout": "30"}'
JSON
{ "data": { "id": 41, "code": "deposit", "name": "First deposit", "revenue_type": "rpa", "revenue": "40.0000",
            "payout_type": "cpa", "payout": "30.0000", "is_default": false, "is_private": false, "allow_multiple": false } }

Private events are hidden from partners and never fire their postbacks.

Partner access#

Method & pathDescription
GET /offers/{id}/affiliates?status=pendingStaff keys. Access requests and decisions for the offer.
PUT /offers/{id}/affiliates/{affiliate}Admin keys. {affiliate} is an affiliate id or code. Body {"status": "approved" | "pending" | "rejected" | "blocked", "note": "…"}. Creates the access row when the partner never asked. The partner is notified of approvals and rejections.
Shell
curl -X PUT https://app.168-144-125-119.sslip.io/api/v1/offers/12/affiliates/Pq3Rt8z \
  -H "Authorization: Bearer $ATK" -H "Content-Type: application/json" \
  -d '{"status": "approved", "note": "Welcome aboard"}'
JSON
{ "data": { "affiliate_id": 7, "affiliate_code": "Pq3Rt8z", "affiliate_name": "Acme Media", "status": "approved",
            "note": "Welcome aboard", "requested_at": "2026-10-09T10:00:00.000Z", "decided_at": "2026-10-10T08:15:30.123Z" } }

Affiliates#

Staff keys only (admin: read/write, analyst: read).

GET /affiliates#

Filters: status (active, pending, blocked, rejected), manager_id, tag, ids, q (name, company or e-mail contains / exact code), limit (default 50, max 200), cursor.

Shell
curl "https://app.168-144-125-119.sslip.io/api/v1/affiliates?status=active&q=acme" -H "Authorization: Bearer $ATK"
JSON
{
  "data": [
    {
      "id": 7,
      "code": "Pq3Rt8z",
      "name": "Acme Media",
      "status": "active",
      "company": "Acme Media Ltd",
      "contact_name": "Jane Doe",
      "email": "ops@acme.example",
      "phone": "",
      "website": "https://acme.example",
      "country": "US",
      "messenger": "",
      "traffic_sources": "Search, native",
      "tier": "A",
      "manager_id": 4,
      "payment_method": "paypal",
      "payment_details": { "paypal": "billing@acme.example" },
      "payment_terms": "net30",
      "min_payout": "50.0000",
      "currency": "USD",
      "notes": "",
      "tags": ["search"],
      "approved_at": "2026-09-30T12:00:00.000Z",
      "created_at": "2026-09-29T08:00:00.000Z",
      "updated_at": "2026-10-02T16:45:00.000Z"
    }
  ],
  "next_cursor": null
}

payment_details is only included for admin keys.

GET /affiliates/{id}#

{id} is the numeric id or the affiliate code.

POST /affiliates#

Admin keys. name is required; every other field is optional: code (6-10 letters/digits, generated when omitted), status (default active), company, contact_name, email, phone, website, country (ISO-2), messenger, traffic_sources, tier, manager_id (a staff user of the workspace), payment_method (paypal, wire, payoneer, crypto, other), payment_details (object of strings), payment_terms (default net30), min_payout, currency (default: workspace currency), notes, tags.

Shell
curl -X POST https://app.168-144-125-119.sslip.io/api/v1/affiliates \
  -H "Authorization: Bearer $ATK" -H "Content-Type: application/json" \
  -d '{"name": "Acme Media", "email": "ops@acme.example", "country": "US", "payment_method": "paypal", "payment_details": {"paypal": "billing@acme.example"}}'

Returns 201 with the affiliate. To let the partner sign in, invite a user for them from the panel.

PATCH /affiliates/{id}#

Admin keys. Send only the fields to change, e.g. {"status": "blocked"}. Setting status to active for the first time records approved_at.

Advertisers#

Staff keys only.

Method & pathDescription
GET /advertisersFilters status (active, paused, inactive), ids, q; limit (default 50, max 200), cursor.
GET /advertisers/{id}One advertiser.
POST /advertisersAdmin keys. name required; optional status, company, contact_name, email, phone, website, country, currency, account_manager_id, billing_terms, notes, tags.
PATCH /advertisers/{id}Admin keys. Partial update.
Shell
curl -X POST https://app.168-144-125-119.sslip.io/api/v1/advertisers \
  -H "Authorization: Bearer $ATK" -H "Content-Type: application/json" \
  -d '{"name": "Shop Inc.", "website": "https://shop.example.com", "currency": "USD"}'
JSON
{
  "data": {
    "id": 3,
    "name": "Shop Inc.",
    "status": "active",
    "company": "",
    "contact_name": "",
    "email": "",
    "phone": "",
    "website": "https://shop.example.com",
    "country": "",
    "currency": "USD",
    "account_manager_id": null,
    "billing_terms": "net30",
    "notes": "",
    "tags": [],
    "postback_token": "9f0c…",
    "created_at": "2026-10-10T08:15:30.123Z",
    "updated_at": "2026-10-10T08:15:30.123Z"
  }
}

postback_token (the advertiser's S2S token, see postbacks) is only included for admin keys.

Reports#

GET /reports#

Runs the same engine as the panel's report builder: one aggregate over clicks, impressions and conversions, grouped by up to three dimensions, with totals computed by the database (never by adding up rows).

ParameterDescription
from, to, tz, presetDate range (default preset=last7). Max 400 days.
group_byUp to 3 comma-separated dimensions, outermost first (none = totals only).
metricsComma-separated metrics (default: the role's default set).
filtersComma-separated dimension:in:v1|v2 / dimension:not_in:v1|v2 (repeat filter= instead when a value contains a comma).
offer_id, affiliate_id, advertiser_idShortcuts for id filters (comma-separated ids).
date_basisconversion (default: conversions counted at conversion time) or click (at the time of their click).
currencyISO 4217 code of the money columns (default: the workspace currency).
sort-clicks (descending), clicks (ascending) or clicks:desc; any grouped dimension or requested metric.
limit, offsetRows per page (default 1000, max 10000) and offset; the response has next_offset (null on the last page).
formatjson (default) or csv — streams every row (max 200,000) plus a totals line as a download.

Dimensions: date, hour, week (weeks start Monday), month — bucketed in tz; offer, affiliate, advertiser, smart_link, event (numeric ids; rows include <dim>_label); sub1 … sub5, source_id, traffic_source, ext_campaign_id, ext_adgroup_id, ext_ad_id, ext_keyword, ext_placement, country, region, city, device_type, os, browser, domain, link_type. Filter-only: click_status (redirect, fallback, blocked, error) and conversion_status (approved, pending, rejected).

Metrics: impressions, clicks, unique_clicks, bot_clicks, ctr, conversions (approved), pending_conversions, rejected_conversions, cvr (approved ÷ unique clicks), revenue, payout, profit, margin, epc, rpc, cpc, avg_sale_amount, sale_amount. Counts are numbers, money is a 4-decimal string, ratios are fractions (0.0315 = 3.15 %) or null when the denominator is 0.

Partner keys only see their own traffic and never revenue, profit, margin, rpc, cpc, bot_clicks or the advertiser dimension; payout is their earnings and epc is earnings per click. Advertiser keys only see their own offers and never payout, profit, margin, rpc, cpc, bot_clicks or partner names; revenue is their cost. Requesting a metric or dimension the role cannot see is a 400 invalid_parameter.

Currencies: every offer has its own currency and money is never added across currencies. Money columns (revenue, payout, profit, epc, …) count only amounts in the report's currency (the currency parameter, else the workspace currency); counts (clicks, conversions) cover every currency. The response's other_currencies lists the other currencies that have money in the same range and scope — run the report again with currency=EUR (for example) to see those amounts. The CSV export names the currency in every money header and adds a note line when other currencies were left out.

Shell
curl "https://app.168-144-125-119.sslip.io/api/v1/reports?preset=last7&group_by=offer,date&metrics=clicks,conversions,revenue,payout,profit&sort=-clicks&filters=country:in:US|CA" \
  -H "Authorization: Bearer $ATK"
JSON
{
  "data": [
    { "offer": "12", "offer_label": "Summer Sale — US", "offer_sub": "Kx7Lm2Q", "date": "2026-10-09", "date_label": "Oct 9, 2026",
      "clicks": 1520, "conversions": 41, "revenue": "287.0000", "payout": "205.0000", "profit": "82.0000" }
  ],
  "totals": { "clicks": 1520, "conversions": 41, "revenue": "287.0000", "payout": "205.0000", "profit": "82.0000" },
  "columns": [
    { "key": "offer", "label": "Offer", "kind": "dimension", "format": "entity" },
    { "key": "date", "label": "Date", "kind": "dimension", "format": "date" },
    { "key": "clicks", "label": "Clicks", "kind": "metric", "format": "number" },
    { "key": "conversions", "label": "Conversions", "kind": "metric", "format": "number" },
    { "key": "revenue", "label": "Revenue", "kind": "metric", "format": "money" },
    { "key": "payout", "label": "Payout", "kind": "metric", "format": "money" },
    { "key": "profit", "label": "Profit", "kind": "metric", "format": "money" }
  ],
  "total_rows": 1,
  "next_offset": null,
  "currency": "USD",
  "other_currencies": ["EUR"],
  "range": { "from": "2026-10-03T18:30:00.000Z", "to": "2026-10-10T18:30:00.000Z", "tz": "Asia/Kolkata" },
  "group_by": ["offer", "date"],
  "metrics": ["clicks", "conversions", "revenue", "payout", "profit"],
  "date_basis": "conversion"
}

CSV export of the same report:

Shell
curl -o report.csv "https://app.168-144-125-119.sslip.io/api/v1/reports?preset=last_month&group_by=affiliate&format=csv" \
  -H "Authorization: Bearer $ATK"

GET /reports/timeseries#

A gap-filled series — one point per day (interval=day, default) or hour (interval=hour, ranges up to 31 days) in tz — for charts. Accepts from, to, tz, preset (default last30, or today for hours), metrics, filters, offer_id, affiliate_id.

Shell
curl "https://app.168-144-125-119.sslip.io/api/v1/reports/timeseries?preset=last7&metrics=clicks,payout" -H "Authorization: Bearer $ATK"
JSON
{
  "data": [
    { "bucket": "2026-10-04", "clicks": 1288, "payout": "160.0000" },
    { "bucket": "2026-10-05", "clicks": 0, "payout": "0.0000" }
  ],
  "interval": "day",
  "metrics": ["clicks", "payout"],
  "range": { "from": "2026-10-03T18:30:00.000Z", "to": "2026-10-10T18:30:00.000Z", "tz": "Asia/Kolkata" }
}

Clicks#

GET /clicks#

The raw click log, newest first. Default range: today in tz; at most 93 days per request (use reports for longer periods).

ParameterDescription
from, to, tz, presetDate range.
limit, cursorPage size (default 100, max 1000) and cursor.
filters, offer_id, affiliate_idSame filter syntax as reports.
botyes = only bot clicks, no = exclude bots.
qExact match on click id, gclid/gbraid/wbraid, fbclid, msclkid, sub1 or source id.
Shell
curl "https://app.168-144-125-119.sslip.io/api/v1/clicks?preset=today&limit=1000&offer_id=12" -H "Authorization: Bearer $ATK"
JSON
{
  "data": [
    {
      "click_id": "0192a6c3f1e04b7e9c2d8a1f3b5c7d9e",
      "ts": "2026-10-10T08:15:30.123Z",
      "link_type": "transparent",
      "status": "redirect",
      "status_reason": "",
      "offer_id": 12,
      "offer_label": "Summer Sale — US",
      "affiliate_id": 7,
      "affiliate_label": "Acme Media",
      "smart_link_id": 0,
      "is_unique": true,
      "is_bot": false,
      "is_datacenter": false,
      "country": "US",
      "city": "San Jose",
      "device_type": "mobile",
      "os": "Android",
      "browser": "Chrome",
      "traffic_source": "google",
      "sub1": "campaign-a",
      "sub2": "",
      "source_id": "",
      "gclid": "Cj0KCQjw…",
      "ext_campaign_id": "123456789",
      "ext_keyword": "running shoes",
      "domain": "trk.example.com",
      "destination": "https://shop.example.com/summer?cid=0192a6c3f1e04b7e9c2d8a1f3b5c7d9e"
    }
  ],
  "next_cursor": "MTc5MTYyNDUzMDEyMzowMTkyYTZjM2YxZTA0YjdlOWMyZDhhMWYzYjVjN2Q5ZQ",
  "range": { "from": "2026-10-09T18:30:00.000Z", "to": "2026-10-10T18:30:00.000Z", "tz": "Asia/Kolkata" }
}

Partner keys never receive destination (the offer's landing page). Advertiser keys never receive partner sub IDs, source_id, smart_link_id or partner names.

GET /clicks/{click_id}#

Every stored column of one click (IP address and fraud flags for staff keys only), the extra parameters that were not mapped to a column, and the click's conversions.

Shell
curl https://app.168-144-125-119.sslip.io/api/v1/clicks/0192a6c3f1e04b7e9c2d8a1f3b5c7d9e -H "Authorization: Bearer $ATK"
JSON
{
  "data": {
    "click": { "click_id": "0192a6c3f1e04b7e9c2d8a1f3b5c7d9e", "ts": "2026-10-10T08:15:30.123Z", "offer_id": 12, "affiliate_id": 7,
               "country": "US", "device_type": "mobile", "gclid": "Cj0KCQjw…", "…": "…" },
    "extra": { "utm_source": "google" },
    "offer": { "id": 12, "label": "Summer Sale — US", "sub": "Kx7Lm2Q" },
    "affiliate": { "id": 7, "label": "Acme Media", "sub": "Pq3Rt8z" },
    "conversions": [
      { "id": "8a6a2d0e-3c55-4c7e-9a43-0c1c2d8f0b11", "conv_ts": "2026-10-10T08:31:02.000Z", "event_code": "default",
        "status": "approved", "status_reason": "", "payout": "5.0000", "revenue": "7.0000", "sale_amount": "0.0000",
        "currency": "USD", "adv_txid": "ORDER-1001", "method": "s2s", "is_duplicate": false }
    ]
  }
}

Conversions#

Conversions are read from the system of record, so status changes are visible immediately (reports catch up within a few seconds).

GET /conversions#

By conversion time, newest first. Default range: last 7 days in tz.

ParameterDescription
from, to, tz, presetDate range on the conversion time.
statusComma-separated: approved, pending, rejected.
offer_id, affiliate_idComma-separated ids (affiliate_id: staff and advertiser keys).
eventComma-separated event codes.
methodComma-separated: s2s, pixel, js, manual, api, import.
click_idConversions of one click.
adv_txidAdvertiser order id (staff and advertiser keys).
duplicatesinclude (default), exclude or only.
limit, cursorPage size (default 100, max 500) and cursor.
Shell
curl "https://app.168-144-125-119.sslip.io/api/v1/conversions?preset=yesterday&status=pending" -H "Authorization: Bearer $ATK"
JSON
{
  "data": [
    {
      "id": "8a6a2d0e-3c55-4c7e-9a43-0c1c2d8f0b11",
      "click_id": "0192a6c3f1e04b7e9c2d8a1f3b5c7d9e",
      "offer_id": 12,
      "offer_name": "Summer Sale — US",
      "affiliate_id": 7,
      "affiliate_code": "Pq3Rt8z",
      "affiliate_name": "Acme Media",
      "advertiser_id": 3,
      "smart_link_id": null,
      "event_id": 40,
      "event_code": "default",
      "status": "pending",
      "status_reason": "",
      "is_duplicate": false,
      "revenue": "7.0000",
      "payout": "5.0000",
      "sale_amount": "0.0000",
      "currency": "USD",
      "adv_txid": "ORDER-1001",
      "method": "s2s",
      "click_ts": "2026-10-09T08:15:30.123Z",
      "conv_ts": "2026-10-09T08:31:02.000Z",
      "domain": "trk.example.com",
      "link_type": "transparent",
      "sub1": "campaign-a", "sub2": "", "sub3": "", "sub4": "", "sub5": "",
      "source_id": "",
      "traffic_source": "google",
      "gclid": "Cj0KCQjw…", "gbraid": "", "wbraid": "", "fbclid": "",
      "ext_campaign_id": "123456789", "ext_adgroup_id": "", "ext_ad_id": "", "ext_keyword": "running shoes", "ext_placement": "",
      "country": "US", "region": "CA", "city": "San Jose",
      "device_type": "mobile", "os": "Android", "browser": "Chrome",
      "conv_ip": "198.51.100.20",
      "params": { "adv1": "blue" },
      "notes": "",
      "created_at": "2026-10-09T08:31:02.000Z",
      "updated_at": "2026-10-09T08:31:02.000Z"
    }
  ],
  "next_cursor": null,
  "range": { "from": "2026-10-08T18:30:00.000Z", "to": "2026-10-09T18:30:00.000Z", "tz": "Asia/Kolkata" }
}

Partner keys only see their own conversions of non-private events, without revenue, advertiser_id, adv_txid, params, notes or conv_ip. Advertiser keys only see conversions of their offers, without payout, partner names, sub IDs, source_id, conv_ip or notes.

GET /conversions/{id}#

One conversion by UUID, same shape and visibility rules.

POST /conversions#

Admin keys. Records a conversion by hand (method api). Payout and revenue are resolved exactly like an S2S postback (custom payouts, event terms, percentages of sale_amount) unless you override them; an approved conversion enqueues the partner's postbacks.

FieldDescription
offer_idRequired.
affiliate_idThe partner (taken from the click when click_id is given).
eventEvent code, default default.
click_idAttribute to a click (32 hex). The click must belong to the offer.
statusapproved, pending or rejected; default: the offer's default_status.
payout, revenueOverride the resolved amounts.
sale_amountSale amount for percentage payouts/revenue.
adv_txidAdvertiser order id; 409 conflict when the offer already has a conversion with it.
conv_tsConversion time (ISO-8601, default now, never in the future).
notesInternal note.
Shell
curl -X POST https://app.168-144-125-119.sslip.io/api/v1/conversions \
  -H "Authorization: Bearer $ATK" -H "Content-Type: application/json" \
  -d '{"offer_id": 12, "affiliate_id": 7, "event": "sale", "sale_amount": "200", "adv_txid": "ORDER-1001", "notes": "Phone order"}'

Returns 201 with the conversion.

PATCH /conversions/{id}#

Admin keys. Approve, reject or pend one conversion: {"status": "rejected", "reason": "fraud"}. Approving enqueues the partner postbacks that have not fired for this conversion yet; duplicates can only be rejected.

Shell
curl -X PATCH https://app.168-144-125-119.sslip.io/api/v1/conversions/8a6a2d0e-3c55-4c7e-9a43-0c1c2d8f0b11 \
  -H "Authorization: Bearer $ATK" -H "Content-Type: application/json" \
  -d '{"status": "approved"}'

PATCH /conversions#

Admin keys. The same for up to 500 conversions at once:

Shell
curl -X PATCH https://app.168-144-125-119.sslip.io/api/v1/conversions \
  -H "Authorization: Bearer $ATK" -H "Content-Type: application/json" \
  -d '{"ids": ["8a6a2d0e-3c55-4c7e-9a43-0c1c2d8f0b11", "1f0e…"], "status": "rejected", "reason": "fraud"}'
JSON
{
  "data": {
    "updated": [ { "id": "8a6a2d0e-3c55-4c7e-9a43-0c1c2d8f0b11", "status": "rejected", "status_reason": "fraud" } ],
    "not_found": ["1f0e…"],
    "skipped_duplicates": 0
  }
}

Postbacks#

Outgoing partner postbacks and pixels (see macros for {payout}, {sub1}, …).

  • Partner keys manage their own postbacks: affiliate_id is always their account (omit it), offers must be ones they can run, and private events are not available.
  • Admin keys manage every postback of the workspace; affiliate_id: null fires for every partner's conversions. Analyst keys can read them. Advertiser keys have no access.
Method & pathDescription
GET /postbacksFilters affiliate_id (staff: an id or global), offer_id, status, kind; limit (default 50, max 200), cursor.
GET /postbacks/{id}One postback.
POST /postbacksCreate.
PATCH /postbacks/{id}Partial update.
DELETE /postbacks/{id}Delete (204). Already-sent postbacks stay in the log.
FieldDefaultDescription
affiliate_idnullStaff keys only.
offer_idnullnull = all offers.
event_codenullnull = every non-private event.
kinds2ss2s (server-to-server request), image (pixel URL), html (snippet in body).
methodGETPOST sends body (s2s only).
url—Required for s2s and image.
body""POST body or HTML snippet; macros expanded (JSON-escaped when it starts with {).
fire_onapprovedapproved or any (also pending/rejected).
statusactiveactive or paused.
Shell
curl -X POST https://app.168-144-125-119.sslip.io/api/v1/postbacks \
  -H "Authorization: Bearer $ATK" -H "Content-Type: application/json" \
  -d '{"offer_id": 12, "url": "https://tracker.example.net/postback?cid={sub1}&payout={payout}&status={status}"}'
JSON
{
  "data": {
    "id": 31,
    "affiliate_id": 7,
    "offer_id": 12,
    "event_code": null,
    "kind": "s2s",
    "method": "GET",
    "url": "https://tracker.example.net/postback?cid={sub1}&payout={payout}&status={status}",
    "body": "",
    "fire_on": "approved",
    "status": "active",
    "created_by_affiliate": true,
    "created_at": "2026-10-10T08:15:30.123Z",
    "updated_at": "2026-10-10T08:15:30.123Z"
  }
}

Builds a link on the workspace's default tracking domain (or host, which must be one of its active tracking domains).

ParameterDescription
offerRequired. Offer id or code.
affiliateAffiliate id or code. Staff keys: required for type=affiliate. Partner keys: always their own account.
typeaffiliate (default): /r/{offer}/{affiliate}?…. transparent: a Google-Ads-certifiable /c/{offer}-{affiliate}?…&url={lpurl} tracking template.
sub1 … sub5, source_idValues to embed.
urltransparent: the url= value (default {lpurl}, which Google Ads replaces with the final URL).
googletransparent: include the Google Ads ValueTrack parameters (gclid={gclid}, campaignid={campaignid}, …). Default true.
hostA specific active tracking domain.
Shell
curl "https://app.168-144-125-119.sslip.io/api/v1/tracking-link?offer=Kx7Lm2Q&affiliate=Pq3Rt8z&sub1=campaign-a" -H "Authorization: Bearer $ATK"
JSON
{
  "data": {
    "type": "affiliate",
    "url": "https://track.example.com/r/Kx7Lm2Q/Pq3Rt8z?sub1=campaign-a",
    "host": "track.example.com",
    "offer": { "id": 12, "code": "Kx7Lm2Q", "name": "Summer Sale — US" },
    "affiliate": { "id": 7, "code": "Pq3Rt8z" },
    "warnings": []
  }
}

A transparent tracking template for Google Ads:

Shell
curl "https://app.168-144-125-119.sslip.io/api/v1/tracking-link?offer=Kx7Lm2Q&affiliate=Pq3Rt8z&type=transparent" -H "Authorization: Bearer $ATK"
JSON
{
  "data": {
    "type": "transparent",
    "url": "https://track.example.com/c/Kx7Lm2Q-Pq3Rt8z?gclid={gclid}&gbraid={gbraid}&wbraid={wbraid}&campaignid={campaignid}&adgroupid={adgroupid}&creative={creative}&keyword={keyword}&matchtype={matchtype}&network={network}&device={device}&placement={placement}&loc_physical_ms={loc_physical_ms}&targetid={targetid}&url={lpurl}",
    "host": "track.example.com",
    "offer": { "id": 12, "code": "Kx7Lm2Q", "name": "Summer Sale — US" },
    "affiliate": { "id": 7, "code": "Pq3Rt8z" },
    "link_key": "Kx7Lm2Q-Pq3Rt8z",
    "google_ads": { "tracking_template": "https://track.example.com/c/Kx7Lm2Q-Pq3Rt8z?gclid={gclid}&…&url={lpurl}" },
    "warnings": []
  }
}

For staff keys warnings explains why clicks would currently go to the fallback (offer paused, partner not active or not approved). Partner keys get 403 not_approved until they may run the offer. Advertiser keys cannot build links.

Example: nightly sync#

Shell
#!/usr/bin/env bash
# Pull yesterday's approved conversions page by page.
ATK="atk_…"
URL="https://app.168-144-125-119.sslip.io/api/v1/conversions?preset=yesterday&status=approved&limit=500"
CURSOR=""
while :; do
  PAGE=$(curl -sf "$URL${CURSOR:+&cursor=$CURSOR}" -H "Authorization: Bearer $ATK") || exit 1
  echo "$PAGE" | jq -c '.data[]'
  CURSOR=$(echo "$PAGE" | jq -r '.next_cursor // empty')
  [ -z "$CURSOR" ] && break
done
Something unclear or missing on this page?Tell us

Ready to send your first click?

Create a workspace, add a tracking domain and copy your first link — the guides above walk you through every step.