Skip to Content
¡Rastrea tus conversiones de Telegram con Meta Ads — comienza en minutos!

Endpoints

All requests require Authorization: Bearer atk_live_.... All responses are JSON.

Forwarding the real client

If you call from a reverse proxy, forward the end user’s IP and User-Agent via either:

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

Precedence: headers win over body fields.


POST /api/v1/events/visit

Mirror of the tracker’s POST /track/init. Creates a tracking_session + visit row.

Body (all fields optional unless noted):

{ tempId?: string, // UUID; generated if absent clientSessionId?: string, // generated if absent externalUserId?: string, href: string, // REQUIRED — full URL of the page landingPage?: string, // derived from href if missing referrer?: string, clientIp?: string, userAgent?: string, country?: string, // override Vercel geo city?: string, region?: string, fbc?: string, fbp?: string, gclid?: string, ttclid?: string, sccid?: string, utmSource?: string, utmMedium?: string, utmCampaign?: string, utmContent?: string, utmTerm?: string, }

Response:

{ visitId: string, sessionId: string, tempId: string, clientSessionId: string, cookies: { tempId: { name: "adtarget_temp_id", value, maxAge: 31536000, sameSite: "Lax" }, session: { name: "adtarget_session_id", value, maxAge: 1800, sameSite: "Lax" } } }

The cookies block is a hint for proxies: echo them as Set-Cookie headers on the upstream response so the end user keeps the same tempId/sessionId on subsequent requests.

curl -X POST https://adtarget.io/api/v1/events/visit \ -H "Authorization: Bearer atk_live_..." \ -H "Content-Type: application/json" \ -d '{ "href": "https://yoursite.com/landing?fbclid=AbCd", "fbc": "fb.1.1700000000.AbCd", "clientIp": "203.0.113.42", "userAgent": "Mozilla/5.0 ..." }'

POST /api/v1/events/update-fbp

Patch the latest visit’s fbp value. Use when Meta’s fbevents.js sets _fbp after your initial /events/visit call.

Body:

{ tempId: string, fbp: string }

Response: { success: boolean }


POST /api/v1/events/invite

Generate a Telegram invite link for a visitor. Returns JSON (no redirect, unlike the tracker’s GET endpoint).

Body:

{ tempId: string, clientSessionId?: string, channelId: string // Convex ID or Telegram channel ID (-100...) }

Response: { inviteLink: string }


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, // Convex ID or Telegram -100... ID eventType: "Lead" | "Purchase" | "CompleteRegistration" | "Subscribe" | "Custom", customEventName?: string, // required when eventType === "Custom" value?: number, currency?: string, // 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=[] (no platform matched attribution) warning?: string, // present when dispatched=false; explains why sends: [{ sendId: string, platform: string }] }
  • dispatched: false means no platform CAPI was scheduled — the request body lacked attribution signals (fbc/fbp/gclid/ttclid/sccid) that match any configured pixel, or no pixel is configured for this site. The conversion is still stored (you’ll see it in the dashboard) but no CAPI event was fired. capiStatus is "skipped" in this case.
  • 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 a prior conversion for (siteId, telegramUserId, channelId). The new conversion inherits its sessionId/visitId and reuses the original attribution.
  • Cold: no prior conversion. 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.


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

Fire an additional event on an existing lead. Equivalent to POST /events/conversion but auto-resolves channelId from the lead’s most recent conversion.

Body: same as /events/conversion minus telegramUserId and (optionally) channelId.


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 this site. 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 this site.


GET /api/v1/channels

List the active channels for this site, with both the internal Convex ID and the Telegram channel ID. Useful when you only know one and need the other.

Response:

{ channels: [{ channelId: string, // Convex ID telegramChannelId: string, // "-100..." title: string, username?: string, funnelOrder: number, eventType?: string }] }
Last updated on