Skip to Content
Отслеживайте конверсии Telegram из Meta Ads — начните за несколько минут!

Endpoints

Active /api/v1/* business endpoints require Authorization: Bearer atk_live_.... Responses are JSON.

New API keys are account-scoped. Lead/event endpoints can infer the site and channel from the Telegram user’s existing funnel history. For cold leads or ambiguous matches, supply siteId or websiteId plus channelId.

Do not use /api/v1 for pageviews or invite clicks. First-party reverse proxies should use the keyless tracker-compatible endpoints: /backend/track/init, /backend/track/update-fbp, and /backend/track/invite.

Forwarding the real client

For API conversion requests, forward the end user’s IP and User-Agent via either:

  • Headers: X-Adtarget-Client-IP, X-Adtarget-Client-User-Agent
  • Body fields: clientIp, userAgent

Precedence: headers win over body fields.

For /backend/track/* reverse-proxy requests, forward the real browser context with headers. Do not add an API key.


POST /api/v1/events/conversion

The killer endpoint. Creates a conversion row + dispatches Meta CAPI (and TikTok / Snapchat / Google if configured).

Body:

{ telegramUserId: number, channelId?: string, // optional when prior lead history can resolve it siteId?: string, // optional override; required for cold account-key leads websiteId?: string, // alternative to siteId eventType: "Lead" | "Purchase" | "CompleteRegistration" | "Subscribe" | "Custom", customEventName?: string, // required when eventType === "Custom" value?: number, // required for Purchase currency?: string, // required for Purchase; ISO 4217 (USD, EUR, ...) contentName?: string, pii?: { email?: string, phone?: string, firstName?: string, lastName?: string }, externalUserId?: string, eventTime?: number, // Unix millis; defaults to now // Cold-lead attribution overrides fbc?: string, fbp?: string, gclid?: string, ttclid?: string, sccid?: string, clientIp?: string, userAgent?: string, eventSourceUrl?: string, }

Response:

{ conversionId: string, capiStatus: "pending" | "skipped", coldLead: boolean, dispatched: boolean, // false when sends=[] or a sent duplicate was reused deduplicated?: boolean, // true when an existing matching event was reused/updated updatedExisting?: boolean, // true when a pending/failed/skipped event was completed warning?: string, // present when dispatched=false; explains why sends: [{ sendId: string, platform: string }] }
  • dispatched: false means no platform CAPI was scheduled. This can happen because the request lacked attribution signals, no pixel is configured for the resolved site, or a recent matching sent event was reused with deduplicated: true.
  • dispatched: true does NOT guarantee the event was accepted by Meta/TikTok/etc. — it means a send was scheduled. If a downstream check (e.g., missing user_agent for Meta) rejects the send, the underlying conversion_sends row will be marked skipped. Poll the dashboard or use Axiom logs to confirm final delivery.

Warm vs cold leads

  • Warm: AdTarget has prior history for the telegramUserId on the account. With an account-scoped key, the API resolves the latest matching site/channel/funnel context and inherits its sessionId/visitId.
  • Cold: no prior conversion. Supply siteId or websiteId plus channelId; the supplied fbc/fbp/clientIp/userAgent are written to conversion.apiVisitOverride and used for CAPI.

For cold leads, always pass fbc, fbp, clientIp, userAgent, eventSourceUrl to keep Meta’s Event Match Quality high.

Duplicate protection

For recent matching events on the same resolved site, Telegram user, channel, and event type:

  • pending/failed/skipped events are updated with the new value/PII and dispatched or retried;
  • sent events are returned with deduplicated: true and are not sent again.

Purchase events require value and currency.


POST /api/v1/leads/{telegramUserId}/events

Fire an additional event on an existing lead. Equivalent to POST /events/conversion, but the Telegram user ID comes from the URL and account-scoped keys can auto-resolve both siteId and channelId from the lead’s most recent conversion.

Body: same as /events/conversion minus telegramUserId; channelId, siteId, and websiteId are optional when lead history is unambiguous.


POST /api/v1/leads/{telegramUserId}/conversions/{conversionId}/value

Submit value + optional PII for a conversion that’s awaitingManualInput (Purchase event with no auto-fire). This is the API equivalent of clicking “Submit value” in the dashboard.

Body:

{ value: number, // > 0 currency: string, // ISO 4217 whitelist pii?: { email?, phone?, firstName?, lastName? } }

Response: { success: boolean }


GET /api/v1/leads/{telegramUserId}

Read everything we know about a lead on the resolved site. Account-scoped keys may pass ?siteId=... or ?websiteId=... when the lead exists on multiple sites. Useful for debugging.

Response:

{ telegramUserId: number, firstSeenAt: number, lastSeenAt: number, externalUserId?: string, conversions: [{ conversionId: string, channelId: string, eventType: string, capiStatus: string, attributedAt: number, eventValue?: number, eventCurrency?: string, source?: "tracker" | "webhook" | "api" }] }

Returns 404 LEAD_NOT_FOUND if no conversion exists for the lead on the resolved site.


GET /api/v1/channels

List active channels. Site-scoped keys return channels for their site. Account-scoped keys return channels across the account, or for one site when ?siteId=... / ?websiteId=... is supplied. Useful when you only know one ID and need the other.

Response:

{ channels: [{ siteId?: string, // present for account-wide channel lists siteDomain?: string, // present for account-wide channel lists channelId: string, // Convex ID telegramChannelId: string, // "-100..." title: string, username?: string, funnelOrder: number, eventType?: string }] }
Last updated on