Developer Guide · Integration
Developer Documentation
Everything you need to integrate Markt into your website, mobile app, or backend. From visitor tracking to transactional emails, push notifications, webhooks, and conversion tracking.
JavaScript SDK
Drop in a script tag to track page views, custom events, and identify visitors on your website.
REST API
Manage contacts, campaigns, and trigger transactional emails from any language or platform.
Webhooks
Receive real time notifications when emails are sent, opened, clicked, bounced, or unsubscribed.
Push Notifications
Send push notifications to iOS, Android, and Web browsers via APNs, FCM, and VAPID.
Conversion Tracking
Send server side conversion events to ad platforms like Meta via the Conversions API (CAPI).
Transactional Email
Trigger event driven emails with dynamic data, merge tags, and time limited expiring links.
Authentication
Markt provides two authentication methods depending on your integration type. Client side tracking uses a Site Token, while server side operations use an API Token.
Site Token
Site Tokens are used for client side tracking (JavaScript SDK, mobile tracking). They are safe to expose in browser code because they can only submit tracking events and identify visitors. They cannot read or modify your data.
Pass the token via the X-Site-Token header or the site_token query parameter:
// Header
X-Site-Token: YOUR_SITE_TOKEN
// Query parameter (used by sendBeacon)
POST /api/v1/t?site_token=YOUR_SITE_TOKEN
Find your Site Token in Team Settings. Each team has a unique token.
API Token (Sanctum)
API Tokens are used for server side operations: managing contacts, controlling campaigns, triggering transactional emails, sending conversion events, and registering push devices. Include the token in the Authorization header:
Authorization: Bearer YOUR_API_TOKEN
Generate API tokens from your team settings in the dashboard. Tokens are scoped to your team.
JavaScript SDK
The Markt JavaScript SDK is a lightweight tracking library you embed on your website. It automatically tracks page views, supports custom events and visitor identification, handles SPA navigation, and batches requests for optimal performance.
Installation
Add the following snippet to your website, ideally before the closing </body> tag. Replace YOUR_SITE_TOKEN with your actual Site Token.
<script>
window.markt = [];
window.markt.SITE_TOKEN = 'YOUR_SITE_TOKEN';
window.markt.API_HOST = 'https://your-domain.com';
</script>
<script src="https://your-domain.com/js/markt.js" defer></script>
The SDK initializes automatically. Before the script loads, you can queue calls using the array stub syntax:
// Queue calls before SDK loads
window.markt = window.markt || [];
window.markt.push(['track', 'signup_started', { plan: 'pro' }]);
window.markt.push(['identify', '[email protected]', { first_name: 'John' }]);
Methods
| Method | Description |
|---|---|
markt.page(url?, title?, referrer?) |
Track a page view. Called automatically on load and SPA navigation. |
markt.track(eventName, properties?) |
Track a custom event with optional properties. |
markt.identify(email, traits?) |
Identify the current visitor as a known contact. |
markt.getVisitorId() |
Returns the current visitor UUID. |
markt.flush() |
Manually flush the event buffer immediately. |
Page View
// Automatic (called on page load and navigation)
// No action needed unless you want to manually trigger
// Manual call with custom values
markt.page('https://example.com/products', 'Products Page', 'https://google.com');
Custom Event
// Track a custom event
markt.track('added_to_cart', {
product_id: 'SKU-123',
product_name: 'Blue T-Shirt',
price: 29.99,
currency: 'USD'
});
// Track without properties
markt.track('newsletter_popup_shown');
Identify
// After login or form submission
markt.identify('[email protected]', {
first_name: 'John',
last_name: 'Doe',
company: 'Acme Inc',
title: 'CTO',
phone: '+1234567890',
website: 'https://acme.com'
});
When identify() is called, all previous anonymous page views and events are retroactively linked to the contact. The SDK flushes any pending events before sending the identify call.
SPA Support
The SDK automatically detects single page application navigation by monkey patching history.pushState, history.replaceState, and listening to the popstate event. Each navigation triggers a page view event automatically. No additional configuration is required for frameworks like React, Vue, Next.js, or Nuxt.
Automatic Behaviors
- Auto page view on initial load
- Auto page view on
pushStateandreplaceStatenavigation - Event batching: flushes every 2 seconds or when 10 events are buffered
- Uses
sendBeaconwithfetchfallback for reliable delivery - Flushes pending events on page hide and visibility change
- Visitor ID persisted in cookie (
_markt_vid, 365 days) and localStorage - Session ID stored in sessionStorage (
_markt_sid)
Tracking API
The Tracking API is the underlying endpoint used by the JavaScript SDK. You can also call it directly from mobile apps, server side code, or any HTTP client. Authentication uses a Site Token.
Rate Limit (per IP)
100 requests per minute
Rate Limit (per token)
1,000 requests per minute
/api/v1/t
Batch track page views, custom events, and identify calls. Accepts 1 to 50 events per request.
Headers
Content-Type: application/json
X-Site-Token: YOUR_SITE_TOKEN
Request Body
| Field | Type | Description |
|---|---|---|
visitor_id required |
string (uuid) | Unique visitor identifier |
session_id |
string (uuid) | Session identifier |
events required |
array (1 to 50) | Array of event objects |
context |
object | Device context: user_agent, screen, platform, language |
Event Object
| Field | Type | Description |
|---|---|---|
type required |
string | One of: page_view, event, identify |
name |
string | Event name (for type event) |
url |
string | Page URL (max 2048 characters) |
title |
string | Page title |
referrer |
string | Referrer URL |
properties |
object | Custom key value properties |
timestamp |
integer | Unix timestamp in seconds |
Example Request
curl -X POST https://your-domain.com/api/v1/t \
-H "Content-Type: application/json" \
-H "X-Site-Token: YOUR_SITE_TOKEN" \
-d '{
"visitor_id": "550e8400-e29b-41d4-a716-446655440000",
"session_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"events": [
{
"type": "page_view",
"url": "https://example.com/pricing",
"title": "Pricing",
"referrer": "https://google.com",
"timestamp": 1705315200
},
{
"type": "event",
"name": "plan_selected",
"properties": { "plan": "pro", "billing": "annual" },
"timestamp": 1705315210
}
],
"context": {
"user_agent": "Mozilla/5.0...",
"screen": "1920x1080",
"platform": "web",
"language": "en-US"
}
}'
Response (202)
{
"message": "Events accepted."
}
/api/v1/t/identify
Link an anonymous visitor to a known contact by email address. All previous anonymous activities from this visitor will be retroactively associated with the contact.
Request Body
| Field | Type | Description |
|---|---|---|
visitor_id required |
string (uuid) | The anonymous visitor ID to resolve |
email required |
string | Contact email address |
traits |
object | Optional: first_name, last_name, company, title, phone, website |
Example Request
curl -X POST https://your-domain.com/api/v1/t/identify \
-H "Content-Type: application/json" \
-H "X-Site-Token: YOUR_SITE_TOKEN" \
-d '{
"visitor_id": "550e8400-e29b-41d4-a716-446655440000",
"email": "[email protected]",
"traits": {
"first_name": "John",
"last_name": "Doe",
"company": "Acme Inc"
}
}'
Response (200)
{
"message": "Contact identified.",
"contact_id": 42
}
REST API Overview
The Markt REST API is a JSON based API authenticated with Sanctum Bearer tokens. All server side integrations use this API.
Base URL
https://your-domain.com/api/v1
Rate Limit
60 requests per minute per token
Format
JSON request and response bodies
Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /contacts |
List contacts (paginated) |
| POST | /contacts |
Create a contact |
| GET | /contacts/{id} |
Get a contact |
| PUT | /contacts/{id} |
Update a contact |
| DELETE | /contacts/{id} |
Delete a contact |
| GET | /campaigns |
List campaigns |
| GET | /campaigns/{id} |
Get campaign details |
| POST | /campaigns/{id}/start |
Start a campaign |
| POST | /campaigns/{id}/pause |
Pause a campaign |
| GET | /campaigns/{id}/stats |
Get campaign statistics |
| GET | /events |
List transactional events |
| GET | /events/{slug} |
Get transactional event details |
| POST | /events/{slug}/trigger |
Trigger a transactional email |
| POST | /conversions |
Send a conversion event (CAPI) |
| POST | /push-devices |
Register a push device |
| PUT | /push-devices/{token} |
Update a push device |
| DELETE | /push-devices/{token} |
Unregister a push device |
Rate Limit Headers
X-RateLimit-Limit: Maximum requests allowed per windowX-RateLimit-Remaining: Remaining requests in the current windowX-RateLimit-Reset: Unix timestamp when the limit resets
Error Responses
Validation errors return a 422 status code with field level error messages:
{
"message": "The given data was invalid.",
"errors": {
"email": ["The email field is required."],
"first_name": ["The first name must be a string."]
}
}
For complete endpoint documentation with all request fields and response examples, see the API Reference.
Transactional Emails
Transactional emails are event driven messages triggered by your application via the API. First, define an event and email template in the Markt dashboard. Then, trigger the event from your backend with dynamic data that gets merged into the template.
1. Define Event
Create a transactional event in the dashboard and link it to an email template.
2. Design Template
Use {{key}} merge tags in your template for dynamic content.
3. Trigger via API
Call the trigger endpoint with the recipient and data payload.
/api/v1/events/{slug}/trigger
Trigger a transactional email to a recipient with dynamic data. The event must be active.
Request Body
| Field | Type | Description |
|---|---|---|
to required |
string (email) | Recipient email address |
to_name |
string | Recipient display name |
data required |
object | Key value pairs merged into the template |
send_at |
string (datetime) | Schedule for future delivery (must be in the future) |
Example: Order Confirmation
curl -X POST https://your-domain.com/api/v1/events/order-confirmation/trigger \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"to": "[email protected]",
"to_name": "Jane Smith",
"data": {
"customer_name": "Jane",
"order_id": "ORD-2024-001",
"order_total": "$149.99",
"tracking_url": "https://shipping.example.com/track/ABC123"
}
}'
Response (202)
{
"message": "Event accepted.",
"log_id": 156
}
Example: Scheduled Password Reset
{
"to": "[email protected]",
"data": {
"reset_url": "https://app.example.com/reset?token=abc123",
"user_name": "John"
},
"send_at": "2026-03-01T09:00:00Z"
}
Expiring Links
Transactional events support expiring links: time limited URLs that become invalid after a specified number of hours. Configure expiring link fields in the event's payload schema in the dashboard. When the email is rendered, URLs marked as expiring are wrapped in signed URLs that automatically expire.
This is useful for password reset links, magic login links, and download URLs that should not remain valid permanently.
Webhooks
Markt sends outgoing webhooks as HTTP POST requests to your configured endpoints whenever email and campaign events occur. Use webhooks to keep your CRM, database, or other systems in sync with Markt in real time.
HTTP POST
All webhooks are sent as POST requests with JSON payloads
HMAC SHA256
Every webhook includes a signature header for verification
3 Retries
Automatic retry with 30 second backoff on failure
Available Events
| Event | Description |
|---|---|
email.sent |
Email was successfully sent from Markt |
email.delivered |
Email was delivered to the recipient's mailbox |
email.opened |
Recipient opened the email |
email.clicked |
Recipient clicked a tracked link in the email |
email.bounced |
Email bounced (hard or soft bounce) |
email.complained |
Recipient marked the email as spam |
email.unsubscribed |
Recipient unsubscribed from emails |
campaign.started |
A campaign was started |
Payload Format
All webhooks are sent as JSON with the following structure:
{
"event": "email.opened",
"payload": {
"contact_id": 123,
"campaign_id": 45,
"email": "[email protected]"
},
"timestamp": "2026-01-15T10:15:30+00:00"
}
Headers included with every webhook:
Content-Type: application/jsonX-Webhook-Signature: HMAC SHA256 signature of the request body
Signature Verification
Each webhook includes an X-Webhook-Signature header containing an HMAC SHA256 hash of the JSON request body, signed with your webhook secret. Always verify the signature to confirm the webhook came from Markt.
Node.js
const crypto = require('crypto');
function verifyWebhook(secret, signature, body) {
const expected = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(body))
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// Express.js example
app.post('/webhook', express.json(), (req, res) => {
const signature = req.headers['x-webhook-signature'];
if (!verifyWebhook(process.env.WEBHOOK_SECRET, signature, req.body)) {
return res.status(401).send('Invalid signature');
}
console.log('Event:', req.body.event);
console.log('Payload:', req.body.payload);
res.status(200).send('OK');
});
PHP
function verifyWebhook(string $secret, string $signature, string $rawBody): bool {
$expected = hash_hmac('sha256', $rawBody, $secret);
return hash_equals($expected, $signature);
}
// Laravel example
$signature = $request->header('X-Webhook-Signature');
$rawBody = $request->getContent();
if (!verifyWebhook(config('services.markt.webhook_secret'), $signature, $rawBody)) {
abort(401, 'Invalid signature');
}
$event = $request->input('event');
$payload = $request->input('payload');
Python
import hmac
import hashlib
import json
def verify_webhook(secret: str, signature: str, body: dict) -> bool:
expected = hmac.new(
secret.encode(),
json.dumps(body, separators=(',', ':')).encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
# Flask example
@app.route('/webhook', methods=['POST'])
def handle_webhook():
signature = request.headers.get('X-Webhook-Signature')
if not verify_webhook(WEBHOOK_SECRET, signature, request.json):
return 'Invalid signature', 401
event = request.json['event']
payload = request.json['payload']
return 'OK', 200
For detailed event payloads and more examples, see the Webhooks Reference.
Push Notifications
Send push notifications to iOS, Android, and Web browsers. Create a Push App in the Markt dashboard with your platform credentials, then use the API to register devices and link them to contacts.
iOS (APNs)
Apple Push Notification service with JWT authentication
Android (FCM)
Firebase Cloud Messaging v1 API
Web (VAPID)
Web Push protocol with VAPID key authentication
/api/v1/push-devices
Register a device token for push notifications. If a device with the same token already exists, it will be updated instead.
Request Body
| Field | Type | Description |
|---|---|---|
push_app_id required |
integer | ID of the Push App created in the dashboard |
device_token required |
string | Platform specific device token (APNs token, FCM token, or Web Push subscription JSON) |
contact_email |
string (email) | Link device to a contact by email |
device_name |
string | Device name (e.g. "John's iPhone") |
device_model |
string | Device model (e.g. "iPhone 15 Pro") |
os_version |
string | OS version (e.g. "17.2") |
app_version |
string | Your app version (e.g. "2.1.0") |
Additional Endpoints
| Method | Endpoint | Description |
|---|---|---|
| PUT | /push-devices/{token} |
Update device metadata or link to a contact |
| DELETE | /push-devices/{token}?push_app_id=1 |
Unregister a device (marks as inactive) |
iOS Integration (APNs)
To send push notifications to iOS devices:
- 1. Create a Push App in the Markt dashboard with your APNs credentials (Key ID, Team ID, Auth Key .p8 file, Bundle ID)
-
2.
In your iOS app, request notification permissions and obtain the device token from
didRegisterForRemoteNotificationsWithDeviceToken -
3.
Send the device token to the Markt API via
POST /api/v1/push-devices -
4.
Optionally link the device to a contact by passing
contact_email
// Swift example
func application(_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
var request = URLRequest(url: URL(string: "https://your-domain.com/api/v1/push-devices")!)
request.httpMethod = "POST"
request.setValue("Bearer \(apiToken)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try? JSONEncoder().encode([
"push_app_id": pushAppId,
"device_token": token,
"contact_email": userEmail,
"device_model": UIDevice.current.model,
"os_version": UIDevice.current.systemVersion
])
URLSession.shared.dataTask(with: request).resume()
}
Android Integration (FCM)
To send push notifications to Android devices:
- 1. Create a Push App in the Markt dashboard with your FCM credentials (Service Account JSON)
-
2.
In your Android app, integrate Firebase Cloud Messaging and obtain the device token from
FirebaseMessaging.getInstance().getToken() -
3.
Send the device token to the Markt API via
POST /api/v1/push-devices
// Kotlin example
FirebaseMessaging.getInstance().token.addOnSuccessListener { token ->
val json = JSONObject().apply {
put("push_app_id", pushAppId)
put("device_token", token)
put("contact_email", userEmail)
put("device_model", Build.MODEL)
put("os_version", Build.VERSION.RELEASE)
put("app_version", BuildConfig.VERSION_NAME)
}
val request = Request.Builder()
.url("https://your-domain.com/api/v1/push-devices")
.addHeader("Authorization", "Bearer $apiToken")
.post(json.toString().toRequestBody("application/json".toMediaType()))
.build()
OkHttpClient().newCall(request).enqueue(...)
}
Web Push Integration (VAPID)
To send push notifications to web browsers:
- 1. Create a Push App in the Markt dashboard with platform set to "Web" (VAPID public and private keys will be generated)
- 2. Register a service worker in your website and subscribe the user to push
-
3.
Send the subscription JSON as the
device_tokento the Markt API
// Browser JavaScript
async function subscribeToPush(pushAppId, vapidPublicKey) {
const registration = await navigator.serviceWorker.ready;
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(vapidPublicKey)
});
await fetch('https://your-domain.com/api/v1/push-devices', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + apiToken,
'Content-Type': 'application/json'
},
body: JSON.stringify({
push_app_id: pushAppId,
device_token: JSON.stringify(subscription),
contact_email: userEmail
})
});
}
Conversion Tracking (CAPI)
The Conversions API (CAPI) lets you send server side conversion events to ad platforms like Meta (Facebook/Instagram). Server side events are more reliable than browser pixels because they are not affected by ad blockers, cookie restrictions, or browser privacy settings.
/api/v1/conversions
Send a conversion event to the active ad integration. The event is queued and forwarded to the ad platform (e.g. Meta Conversions API).
Request Body
| Field | Type | Description |
|---|---|---|
event_name required |
string | Standard or custom event name (e.g. Purchase, Lead, AddToCart) |
event_id required |
string | Unique identifier for deduplication (must be globally unique) |
event_time required |
integer | Unix timestamp of when the event occurred |
event_source_url |
string (url) | URL where the event occurred |
user_data required |
object | User matching parameters (see below) |
custom_data |
object | Event specific data (see below) |
user_data Fields
| Field | Description |
|---|---|
em |
Email address (SHA256 hashed, lowercase, trimmed) |
ph |
Phone number (SHA256 hashed, digits only) |
fn |
First name (SHA256 hashed, lowercase) |
ln |
Last name (SHA256 hashed, lowercase) |
fbc |
Facebook click ID (from _fbc cookie, not hashed) |
fbp |
Facebook browser ID (from _fbp cookie, not hashed) |
client_ip_address |
Client IP address (not hashed) |
client_user_agent |
Client user agent string (not hashed) |
custom_data Fields
| Field | Description |
|---|---|
value |
Monetary value of the event (numeric) |
currency |
ISO 4217 currency code (e.g. USD, EUR) |
content_ids |
Array of product or content IDs |
content_type |
Type of content (e.g. "product", "product_group") |
Example: Purchase Event
curl -X POST https://your-domain.com/api/v1/conversions \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"event_name": "Purchase",
"event_id": "order-2024-001-abc123",
"event_time": 1705315200,
"event_source_url": "https://shop.example.com/checkout/success",
"user_data": {
"em": "a1b2c3d4e5f6...sha256hash",
"ph": "b2c3d4e5f6a1...sha256hash",
"fbc": "fb.1.1705315200.ABCdef",
"fbp": "fb.1.1705315200.XYZabc",
"client_ip_address": "203.0.113.50",
"client_user_agent": "Mozilla/5.0..."
},
"custom_data": {
"value": 149.99,
"currency": "USD",
"content_ids": ["SKU-001", "SKU-002"],
"content_type": "product"
}
}'
Response (202)
{
"message": "Event accepted.",
"event_id": "order-2024-001-abc123"
}
Standard Event Names
| Event Name | Use Case |
|---|---|
Purchase |
Completed purchase or transaction |
Lead |
Lead generation (form submission, sign up) |
AddToCart |
Item added to shopping cart |
ViewContent |
Product or content page view |
InitiateCheckout |
Checkout process started |
CompleteRegistration |
User registration completed |
Subscribe |
Subscription started |
Event Deduplication
The event_id field must be globally unique and is used for deduplication when the same event is sent from both the browser pixel and the server side CAPI. Use the same event_id in both the client side Meta Pixel call and the server side CAPI request so ad platforms can deduplicate correctly.
// Generate a unique event ID to use in both pixel and CAPI
const eventId = 'evt_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
// Client side: Meta Pixel
fbq('track', 'Purchase', {value: 49.99, currency: 'USD'}, {eventID: eventId});
// Server side: Markt CAPI (send from your backend)
// POST /api/v1/conversions with the same event_id
Ad Pixel Integration
For maximum conversion tracking accuracy, use both the browser side ad pixel and the server side CAPI together. The browser pixel captures events from the user's browser, while CAPI provides a reliable server side signal that is not affected by ad blockers or browser privacy restrictions.
Browser Pixel (Client Side)
JavaScript snippet that fires events in the user's browser. Set up via the Ads > Pixel section in the dashboard. Subject to ad blockers.
CAPI (Server Side)
REST API endpoint that sends events from your backend. Not affected by ad blockers. Requires event_id for deduplication.
Recommended Setup
- 1. Connect your Meta ad account in the Markt dashboard (Ads > Integrations)
- 2. Get your pixel snippet from the Ads > Pixel page and add it to your website
-
3.
Generate a unique
event_idfor each conversion and pass it to both the pixel and CAPI -
4.
From your backend, send the same event via
POST /api/v1/conversionswith the matchingevent_id
The ad platform will automatically deduplicate events that share the same event_id, so you will not be double counted.
Email Tracking
Markt automatically tracks email opens and link clicks. No integration is required from your side. This section explains how it works for transparency.
Open Tracking
A 1x1 transparent pixel is embedded in each email. When loaded, a page view is recorded.
Click Tracking
Links in emails are wrapped with tracking URLs. The click is recorded and the user is redirected to the original URL.
Expiring Links
Transactional emails can include time limited signed URLs that expire after a configured number of hours.
How It Works
| URL | Purpose |
|---|---|
GET /t/o/{hash} |
Open tracking pixel. Returns a 1x1 transparent GIF and records the open event. |
GET /t/c/{hash}?url=... |
Click tracking redirect. Records the click event and redirects to the original URL. |
GET /t/e?signature=... |
Expiring link redirect. Validates the signed URL and expiry, then redirects. |
These tracking URLs are automatically injected into campaign and transactional emails by Markt. Open and click events are also available via webhooks.
Platform Guides
Quick reference for integrating Markt based on your platform type.
Website
For traditional websites and SPAs (React, Vue, Next.js, Nuxt).
- Install the JavaScript SDK for visitor tracking, page views, and custom events
- Use
markt.identify()after login or form submission to link visitors to contacts - Add the ad pixel snippet from the dashboard for conversion tracking
- Set up webhooks to sync email events to your backend
Mobile App (iOS / Android)
For native mobile applications.
- Use the Tracking API (
POST /api/v1/t) to send page views and events from your app - Generate and persist a
visitor_id(UUID) on the device - Call the Identify endpoint after user login
- Register device tokens for push notifications (APNs or FCM)
- Send conversion events from your backend when purchases or signups occur
Backend (Any Language)
For server side integrations (Node.js, PHP, Python, Ruby, Go, etc.).
- Use the REST API to manage contacts and campaigns
- Trigger transactional emails for order confirmations, password resets, and notifications
- Listen to webhooks to update your database when email events occur
- Send conversion events to ad platforms via CAPI
Best Practices
Identify visitors early
Call markt.identify() as soon as you know the visitor's email (after login, registration, or form submission). This ensures all prior anonymous activity is linked to the contact for better attribution and segmentation.
Use event_id for deduplication
When sending both browser pixel events and server side CAPI events, use the same event_id for both. Ad platforms will automatically deduplicate, preventing double counting.
Handle webhooks idempotently
Webhooks may be retried up to 3 times. Design your webhook handler to be idempotent by checking for duplicate events using the event data. Always return a 2xx status code quickly and process the event asynchronously.
Store secrets securely
Never expose your API token or webhook secret in client side code. API tokens should only be used in server side environments. Site Tokens are the only credentials safe for browser use.
Use scheduled sending wisely
For time sensitive transactional emails (password resets, verification codes), send them immediately without send_at. Use scheduled sending for non urgent notifications that should arrive at a specific time.
Hash user data for CAPI
Always SHA256 hash personal data (email, phone, names) before sending to the CAPI endpoint. The fbc, fbp, client_ip_address, and client_user_agent fields should not be hashed.
Respect rate limits
Monitor the X-RateLimit-Remaining header and implement exponential backoff when you receive a 429 response. The tracking API allows 100 requests per minute per IP and 1,000 per minute per token.
Ready to integrate?
Explore the full API reference and webhooks documentation for detailed endpoint information.