XTRACKER

Integrate your app with XTRACKER in 10 minutes

Step-by-step: create a link, get your API keys, send canonical events from Python or curl, and fire idempotent postbacks. Includes a drop-in async client.

This is the hands-on guide to wiring your bot/app to XTRACKER. By the end you'll be sending subscribed, registration and deposit events that show up live on your dashboard.

You'll touch four screens, in this order:

Where to click, in order

In the dashboard, create a link for your funnel:

Creating a link, step by step

Then open API keys for the space and rotate to reveal your secrets:

API keys: rotate, copy, grab a postback URL

You'll see two secrets:

  • Bearer — authenticates the write API (POST /events, /bind, /clicks). Keep it server-side only.
  • Ingest token — for header-less GET /e calls (postbacks). It travels in URLs, so it's lower-trust — use it only where you can't set a header.

Both are shown once when you rotate them. Save them in your secrets manager.

The keys page also renders ready-to-paste code and postback URLs with your real tokens filled in. This article is the narrative version.

2. Understand the uid

Every event is tied to a uid — the click id the user arrived with. You don't generate it: the landing mints it and passes it in the deeplink t.me/<bot>?start=fb_<uid>. In your /start handler, read the payload and strip the fb_ prefix:

payload = message.text.partition(" ")[2]   # "fb_abc123"
uid = payload[3:] if payload.startswith("fb_") else None

Events without a uid still record and back-fill once the click is bound — but bind as early as you can.

3. Send events from Python

Here's a self-contained async client (only httpx):

import httpx, time

TRACKER = "https://xtracker.cc"
BEARER  = "<BEARER>"   # server-side only

async def send_event(event, *, uid=None, value=None, currency=None, eid=None):
    payload = {"event": event, "occurred_at_ms": int(time.time() * 1000)}
    if uid:               payload["uid"] = uid
    if value is not None: payload["value"] = str(value)
    if currency:          payload["currency"] = currency
    if eid:               payload["eid"] = eid
    async with httpx.AsyncClient(timeout=5) as c:
        r = await c.post(f"{TRACKER}/events", json=payload,
                         headers={"Authorization": f"Bearer {BEARER}"})
        return r.json()   # {"ok": true, "status": "queued" | "duplicate"}

async def bind(uid, tg_id):
    async with httpx.AsyncClient(timeout=5) as c:
        r = await c.post(f"{TRACKER}/bind", json={"uid": uid, "tg_id": tg_id},
                         headers={"Authorization": f"Bearer {BEARER}"})
        return r.json()

And the funnel, in order:

await bind(uid, message.from_user.id)              # once, after /start fb_<uid>
await send_event("subscribed",   uid=uid)
await send_event("registration", uid=uid, eid=str(trader_id))
await send_event("deposit", uid=uid, value="25.00", currency="USD",
                 eid=f"dep-{transaction_id}")

4. Or fire a postback (no code)

For systems that can only hit a URL (ad networks, a partner's postback field), use the header-less GET /e endpoint with the ingest token:

https://xtracker.cc/e?token=<INGEST_TOKEN>&event=registration&uid={uid}&eid={txid}

For a deposit, add the amount:

https://xtracker.cc/e?token=<INGEST_TOKEN>&event=deposit&uid={uid}&value={amount}&currency=USD&eid={txid}

The {...} parts are macros the upstream system substitutes with its own placeholders.

5. Parameter reference

This is the bit worth bookmarking.

Param Where What it is · how to generate
event required Closed set: contact / subscribed / registration / deposit. Pick the one matching what the user did.
uid optional The click id from the deeplink start=fb_<uid>. You don't generate it. Events without it back-fill once bound.
tg_id /bind Telegram user id — message.from_user.id.
eid recommended Your unique id for this exact conversion (e.g. the PO trade/transaction id). Makes re-sends idempotent — the same eid is recorded once.
value deposit only Numeric string, e.g. "25.00". From the payment/postback.
currency optional ISO-4217, defaults to USD.
occurred_at_ms / t optional Epoch milliseconds — int(time.time()*1000). Omit to let the server stamp receive-time.
Bearer POST auth Authorization: Bearer … for the write API. Rotate to generate. Server-side only.
token GET auth Ingest token in the query string for GET /e. Lower-trust — postbacks only.

6. Idempotency — don't double-count deposits

A retried call must never count a deposit twice. Two mechanisms:

  • occurred_at_ms is pinned per logical event; reusing the same value on a retry dedups.
  • eid dedups independent of time — a postback that re-fires seconds later is ignored. Strongly recommended for deposits.

The response tells you what happened: "status": "queued" (new), "duplicate" (deduped — also success), or "invalid_event" / "invalid_value" / "invalid_time" for a bad field.

You're done

Send a test deposit, watch it appear in the live feed and revenue widget on your dashboard. Next: wire a Pocket Option postback.