API: tracking links, domains, pixels and stats
A tracking link sends a visitor to a creator on Telegram and counts what follows: clicks, conversations, first messages, buyers and revenue. Links can live on your own domain, fire ad pixels (Meta, TikTok, Snap) and send server events. These endpoints match the Tracking page.
Endpoints #
| Method | Path | Description | Permission | Status |
|---|---|---|---|---|
| GET | /links | List links (creator_id=, archived=false) with their counters | can_tracking or can_sales_read | Planned |
| POST | /links | Create a link | can_tracking | Planned |
| GET | /links/{id} | One link | can_tracking or can_sales_read | Planned |
| PUT | /links/{id} | Replace a link (name, slug, domain, destination, UTM, pixels) | can_tracking | Planned |
| DELETE | /links/{id} | Archive a link (permanent=1 deletes it) | can_tracking | Planned |
| GET | /domains | Your domains (status=active,inactive, q=) | can_tracking | Planned |
| POST | /domains | Add a domain (status pending) | can_tracking | Planned |
| POST | /domains/{host}/verify | Check its DNS records now | can_tracking | Planned |
| PATCH | /domains/{host} | Make a verified domain the default, or change its label | can_tracking | Planned |
| DELETE | /domains/{host} | Remove a domain no active link uses | can_tracking | Planned |
| GET | /pixels | The Meta, TikTok and Snap connections and the consent mode | can_tracking | Planned |
| PUT | /pixels/{provider} | Connect or replace a pixel (meta, tiktok, snap) | owner | Planned |
| DELETE | /pixels/{provider} | Disconnect a pixel | owner | Planned |
| POST | /pixels/{provider}/test | Send a test event and return the platform's answer | can_tracking | Planned |
| PUT | /pixels/consent | Consent mode: page or implicit_by_country | owner | Planned |
| GET | /events | Built-in and custom events, with 7-day delivery counts | can_tracking | Planned |
| POST | /events | Create a custom event | owner | Planned |
| PATCH | /events/{id} | Turn an event on or off per network | owner | Planned |
| DELETE | /events/{id} | Delete a custom event | owner | Planned |
| GET | /stats | Daily totals, per link and per source (from=, to=, creator_id=) | can_sales_read | Planned |
| GET | /stats/export.csv | The same numbers as CSV | can_sales_read | Planned |
The link object #
{
"id": "lnk_3",
"name": "TikTok · Lea night",
"slug": "lea-tiktok-night",
"creator_id": "cr_lea",
"source": "tiktok",
"campaign": "Autumn",
"domain": "go.mia-links.com",
"public_url": "https://go.mia-links.com/r/lea-tiktok-night",
"destination": "pool",
"account_id": null,
"utm": { "source": "tiktok", "medium": "social", "campaign": "autumn" },
"pixels": { "meta": false, "tiktok": true, "snap": false },
"counters": { "clicks": 640, "conversations": 97, "first_messages": 76, "buyers": 14, "revenue_stars": 6200 },
"archived": false,
"created_at": "2026-09-12T08:00:00Z"
}
| Field | Type | Description |
|---|---|---|
id | string | Starts with lnk_ |
name | string | The name your team sees |
slug | string | The end of the public URL |
creator_id | string | The creator the link sends to |
source, campaign | string | Where the traffic comes from, and your campaign name |
domain | string | A verified domain of the workspace, or the LanaChat domain |
public_url | string | https://<domain>/r/<slug>: the URL to put in a bio or an ad |
destination | string | pool or account |
account_id | string or null | The fixed account when destination is account |
utm | object | source, medium, campaign |
pixels | object | Which connected pixels fire on this link |
counters | object | clicks, conversations, first_messages, buyers, revenue_stars |
archived | boolean | Archived links are left out of stats |
created_at | string | ISO 8601, UTC |
destinationispool(the account is chosen at click time among the creator's pool, see pools) oraccount(always the sameaccount_id).slug: lowercase letters, digits and dashes, 1 to 48 characters, starting and ending with a letter or a digit, unique among active links of the same domain.domainmust be a verified domain of the workspace; without it the LanaChat domain is used.utmvalues: up to 80 letters, digits, dots, dashes or underscores. They are stamped on every click and forwarded to pixel events.
The domain object #
{
"host": "go.mia-links.com",
"builtin": false,
"status": "pending",
"error": null,
"ssl": "none",
"is_default": false,
"label": "Lea TikTok",
"token": "8be03d5f19a2",
"checks": 0,
"added_at": "2026-10-05T10:00:00Z",
"verified_at": null,
"last_check_at": null
}
| Field | Type | Description |
|---|---|---|
host | string | A subdomain in lowercase (at least 3 labels), unique in the workspace |
builtin | boolean | true for the LanaChat domain, which can never be removed |
status | string | verified, pending or error |
error | string or null | A plain sentence when status is error |
ssl | string | none, issuing or active |
is_default | boolean | Exactly one verified domain is the default |
label | string or null | A name for your team, up to 60 characters |
token | string | The value of the TXT verification record |
checks | number | DNS checks made so far |
added_at, verified_at, last_check_at | string or null | ISO 8601, UTC |
The pixel connection object #
{ "connected": true, "pixel_id": "123456789012345", "token_last4": "x9Qa", "token_status": "valid", "token_error": null, "test_event_code": "TEST4821", "connected_at": "2026-10-06T14:30:00Z" }
| Field | Type | Description |
|---|---|---|
connected | boolean | The pixel is connected |
pixel_id | string | Meta: 15 or 16 digits. TikTok: 20 capital letters or digits. Snap: a UUID |
token_last4 | string | The last 4 characters of the access token, never the token itself |
token_status | string | valid, expired or invalid |
token_error | string or null | A plain sentence, for example "Access token expired on 3 Oct. Replace it to resume delivery." |
test_event_code | string or null | The test code of the platform's events manager |
connected_at | string | ISO 8601, UTC |
The event object #
{
"id": "evt_purchase",
"name": "Purchase",
"builtin": true,
"trigger": { "type": "purchase" },
"networks": { "meta": true, "tiktok": true, "snap": false }
}
| Field | Type | Description |
|---|---|---|
id | string | The event id |
name | string | Built-in name, or your custom name: a letter, then up to 39 letters, digits or underscores, unique in the workspace |
builtin | boolean | true for Lead, Contact, Purchase, Subscribe and InitiateCheckout |
trigger | object | type, and a value for the triggers that need one |
networks | object | On or off for meta, tiktok and snap |
The stats object #
{
"days": [ { "date": "2026-10-05", "clicks": 54, "conversations": 9, "first_messages": 7, "buyers": 2, "revenue_stars": 950 } ],
"links": [ { "link_id": "lnk_3", "clicks": 640, "conversations": 97, "first_messages": 76, "buyers": 14, "revenue_stars": 6200 } ],
"sources": [ { "source": "instagram", "clicks": 1210, "conversations": 160, "first_messages": 131, "buyers": 22, "revenue_stars": 9100 } ]
}
| Field | Type | Description |
|---|---|---|
days | array | One row per UTC day of the period |
links | array | One row per link, over the whole period |
sources | array | One row per source group: instagram, tiktok, snapchat, meta, x and other |
clicks | number | Redirects served |
conversations | number | Telegram conversations started with the link |
first_messages | number | Fans who wrote at least once |
buyers | number | Distinct fans with a verified Stars purchase in the period |
revenue_stars | number | Verified Stars purchases of those fans in the period |
Links #
List links #
GET /links · can_tracking or can_sales_read
| Parameter | Type | Required | Description |
|---|---|---|---|
creator_id | string | No | Only this creator's links |
archived | boolean | No | false (default view) hides archived links |
limit, cursor | number, string | No | Pagination |
curl "https://api.lanachat.ai/v1/links?creator_id=cr_lea&archived=false" \
-H "Authorization: Bearer $LANACHAT_TOKEN"
The answer is a paged list of link objects, each with its counters.
Create a link #
POST /links · can_tracking
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | The name your team sees |
slug | string | Yes | Lowercase letters, digits and dashes, 1 to 48 characters |
creator_id | string | Yes | The creator the link sends to |
source | string | No | instagram, tiktok, snapchat, meta, x or another source |
campaign | string | No | Your campaign name |
destination | string | No | pool or account |
account_id | string | When destination is account | The fixed Telegram account |
domain | string | No | A verified domain; without it the LanaChat domain |
utm | object | No | source, medium, campaign |
pixels | object | No | meta, tiktok, snap: true to fire that pixel |
curl -X POST https://api.lanachat.ai/v1/links \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"name": "Insta bio · Lea", "slug": "lea-bio", "creator_id": "cr_lea", "source": "instagram", "destination": "pool", "domain": "go.mia-links.com"}'
{
"id": "lnk_9",
"name": "Insta bio · Lea",
"slug": "lea-bio",
"creator_id": "cr_lea",
"source": "instagram",
"campaign": null,
"domain": "go.mia-links.com",
"public_url": "https://go.mia-links.com/r/lea-bio",
"destination": "pool",
"account_id": null,
"utm": { "source": null, "medium": null, "campaign": null },
"pixels": { "meta": false, "tiktok": false, "snap": false },
"counters": { "clicks": 0, "conversations": 0, "first_messages": 0, "buyers": 0, "revenue_stars": 0 },
"archived": false,
"created_at": "2026-10-06T14:40:00Z"
}
- A slug already used answers
409withThe slug /lea-bio is already used by "Insta bio · Mia". - A domain that is not verified answers
400("Pick a verified domain…").
Get a link #
GET /links/{id} · can_tracking or can_sales_read
curl https://api.lanachat.ai/v1/links/lnk_3 \
-H "Authorization: Bearer $LANACHAT_TOKEN"
The answer is the link object. An unknown id answers 404.
Replace a link #
PUT /links/{id} · can_tracking
Same body as Create a link; PUT replaces the whole link, so send every field you want to keep.
curl -X PUT https://api.lanachat.ai/v1/links/lnk_3 \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "TikTok · Lea night", "slug": "lea-tiktok-night", "creator_id": "cr_lea", "source": "tiktok", "campaign": "Autumn", "destination": "pool", "domain": "go.mia-links.com", "utm": {"source": "tiktok", "medium": "social", "campaign": "autumn"}, "pixels": {"meta": false, "tiktok": true, "snap": false}}'
The answer is the updated link. Same errors as when you create one.
Archive or delete a link #
DELETE /links/{id} · can_tracking
| Parameter | Type | Required | Description |
|---|---|---|---|
permanent | number | No | 1 deletes the link for good instead of archiving it |
curl -X DELETE https://api.lanachat.ai/v1/links/lnk_3 \
-H "Authorization: Bearer $LANACHAT_TOKEN"
An archived link is left out of stats.
Domains #
To use your own domain, add it, create two DNS records, then verify it:
CNAME go.mia-links.com → links.lanachat.appTXT _lanachat.go.mia-links.com = lanachat-verify=8be03d5f19a2
Pending domains are also checked every 10 minutes for 72 hours, then daily. status becomes verified and ssl goes issuing then active.
List domains #
GET /domains · can_tracking
| Parameter | Type | Required | Description |
|---|---|---|---|
status | string | No | active (verified) or inactive (pending or error) |
q | string | No | Part of the host or the label |
curl "https://api.lanachat.ai/v1/domains?status=active" \
-H "Authorization: Bearer $LANACHAT_TOKEN"
The answer lists domain objects, the LanaChat domain included (builtin: true).
Add a domain #
POST /domains · can_tracking
| Parameter | Type | Required | Description |
|---|---|---|---|
host | string | Yes | A subdomain, for example go.mia-links.com |
curl -X POST https://api.lanachat.ai/v1/domains \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"host": "go.mia-links.com"}'
The answer is the new domain with status: "pending" and its token for the TXT record. The host is cleaned (lowercase, no https://, no path). Root domains, *.lanachat.app and a domain already added answer 400 with a sentence.
Verify a domain #
POST /domains/{host}/verify · can_tracking
curl -X POST https://api.lanachat.ai/v1/domains/go.mia-links.com/verify \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Idempotency-Key: $(uuidgen)"
{ "host": "go.mia-links.com", "builtin": false, "status": "verified", "error": null, "ssl": "issuing", "is_default": false, "label": null, "token": "8be03d5f19a2", "checks": 3, "added_at": "2026-10-05T10:00:00Z", "verified_at": "2026-10-06T14:45:00Z", "last_check_at": "2026-10-06T14:45:00Z" }
When a record is wrong, status is error and error says which, for example "The CNAME record points to another service." The LanaChat domain cannot be verified (its DNS is managed by LanaChat).
Change a domain #
PATCH /domains/{host} · can_tracking
| Parameter | Type | Required | Description |
|---|---|---|---|
is_default | boolean | No | true makes it the default domain; the previous default stops being one |
label | string or null | No | A name for your team, up to 60 characters |
curl -X PATCH https://api.lanachat.ai/v1/domains/go.mia-links.com \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"is_default": true}'
Only a verified domain can be the default. The LanaChat domain refuses a label (403).
Remove a domain #
DELETE /domains/{host} · can_tracking
curl -X DELETE https://api.lanachat.ai/v1/domains/go.mia-links.com \
-H "Authorization: Bearer $LANACHAT_TOKEN"
Removing the LanaChat domain, the default domain ("Set another default domain first") or a domain used by active links ("Used by N active links across all creators") answers 409.
Pixels #
Events are sent server to server, deduplicated with the browser pixel through the same event_id, with the fan's Telegram id hashed (SHA-256), never raw. Without consent no event is sent, ever. A pixel fires only for links that enable it.
Read pixel connections #
GET /pixels · can_tracking
curl https://api.lanachat.ai/v1/pixels \
-H "Authorization: Bearer $LANACHAT_TOKEN"
The answer holds one pixel connection for each of meta, tiktok and snap, and the consent mode.
Connect a pixel #
PUT /pixels/{provider} · owner
| Parameter | Type | Required | Description |
|---|---|---|---|
pixel_id | string | Yes | Meta: 15 or 16 digits. TikTok: 20 capital letters or digits. Snap: a UUID |
access_token | string | Yes | The Conversions API token of the platform |
test_event_code | string | No | The platform's test code |
curl -X PUT https://api.lanachat.ai/v1/pixels/meta \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"pixel_id": "123456789012345", "access_token": "EAAG…", "test_event_code": "TEST4821"}'
{ "connected": true, "pixel_id": "123456789012345", "token_last4": "x9Qa", "token_status": "valid", "token_error": null, "test_event_code": "TEST4821", "connected_at": "2026-10-06T14:30:00Z" }
The access token is stored encrypted and never returned: only token_last4. A pixel id in the wrong format or an empty token answers 400.
Disconnect a pixel #
DELETE /pixels/{provider} · owner
curl -X DELETE https://api.lanachat.ai/v1/pixels/meta \
-H "Authorization: Bearer $LANACHAT_TOKEN"
Send a test event #
POST /pixels/{provider}/test · can_tracking
curl -X POST https://api.lanachat.ai/v1/pixels/meta/test \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Idempotency-Key: $(uuidgen)"
{ "ok": true, "status": 200, "body": "{\"events_received\":1}" }
status and body are the platform's own answer, so you see exactly what Meta, TikTok or Snap said.
Set the consent mode #
PUT /pixels/consent · owner
| Parameter | Type | Required | Description |
|---|---|---|---|
mode | string | Yes | page: a consent screen for every fan before Telegram opens. implicit_by_country: the screen only in the EU and EEA, the UK and Switzerland |
curl -X PUT https://api.lanachat.ai/v1/pixels/consent \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"mode": "implicit_by_country"}'
Events #
Built-in events: Lead (conversation started from a link), Contact (first fan message), Purchase (verified sale), Subscribe, InitiateCheckout. Custom events take a trigger: sale_over (a sale above an amount in US dollars), tag_added (a tag), nth_message (the Nth fan message, 2 or more) or offer_viewed.
List events #
GET /events · can_tracking
curl https://api.lanachat.ai/v1/events \
-H "Authorization: Bearer $LANACHAT_TOKEN"
The answer lists event objects with their deliveries accepted and failed over 7 days, per network.
Create a custom event #
POST /events · owner
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | A letter, then up to 39 letters, digits or underscores; unique in the workspace |
trigger | object | Yes | type: sale_over, tag_added, nth_message or offer_viewed; value for the first three |
networks | object | No | meta, tiktok, snap: true to send it there |
curl -X POST https://api.lanachat.ai/v1/events \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"name": "BigSpender", "trigger": {"type": "sale_over", "value": 50}, "networks": {"meta": true, "tiktok": true, "snap": false}}'
The answer is the new event (201). A name already used answers 409.
Turn an event on or off #
PATCH /events/{id} · owner
| Parameter | Type | Required | Description |
|---|---|---|---|
networks | object | Yes | meta, tiktok, snap: true or false |
curl -X PATCH https://api.lanachat.ai/v1/events/evt_purchase \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"networks": {"meta": true, "tiktok": true, "snap": true}}'
Delete a custom event #
DELETE /events/{id} · owner
curl -X DELETE https://api.lanachat.ai/v1/events/evt_bigspender \
-H "Authorization: Bearer $LANACHAT_TOKEN"
Built-in events cannot be deleted; turn them off instead.
Stats #
Read stats #
GET /stats · can_sales_read
| Parameter | Type | Required | Description |
|---|---|---|---|
from | string | Yes | First UTC day, YYYY-MM-DD, included |
to | string | Yes | Last UTC day, YYYY-MM-DD, included |
creator_id | string | No | Only this creator; without it, all creators |
curl "https://api.lanachat.ai/v1/stats?from=2026-09-06&to=2026-10-05&creator_id=cr_lea" \
-H "Authorization: Bearer $LANACHAT_TOKEN"
The answer is the stats object. Days are UTC; archived links are left out.
Export stats as CSV #
GET /stats/export.csv · can_sales_read
Same parameters as GET /stats.
curl "https://api.lanachat.ai/v1/stats/export.csv?from=2026-09-06&to=2026-10-05" \
-H "Authorization: Bearer $LANACHAT_TOKEN" -o stats.csv
date,link,slug,domain,creator,source,clicks,conversations,first_messages,buyers,revenue_stars
2026-10-05,TikTok · Lea night,lea-tiktok-night,go.mia-links.com,Lea,tiktok,54,9,7,2,950