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: falsemeans 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 withdeduplicated: true.dispatched: truedoes NOT guarantee the event was accepted by Meta/TikTok/etc. — it means a send was scheduled. If a downstream check (e.g., missinguser_agentfor Meta) rejects the send, the underlyingconversion_sendsrow will be markedskipped. Poll the dashboard or use Axiom logs to confirm final delivery.
Warm vs cold leads
- Warm: AdTarget has prior history for the
telegramUserIdon the account. With an account-scoped key, the API resolves the latest matching site/channel/funnel context and inherits itssessionId/visitId. - Cold: no prior conversion. Supply
siteIdorwebsiteIdpluschannelId; the suppliedfbc/fbp/clientIp/userAgentare written toconversion.apiVisitOverrideand 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: trueand 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
}]
}