Integrate Chat33
with your stack.
Tracking pixel, webhook events, MMP install postbacks, and revenue tracking. Everything you need to connect Chat33 to your website or app.
Getting Started
To use the Chat33 developer integrations, you need an MMP Token. Find it in your dashboard at Settings → Integrations.
1. Get your token
Go to Settings → Integrations → MMP Token. Click "Generate Token" if you don't have one.
2. Choose your integration
Website / Web App
Use the Tracking Pixel to track page views, signups, and purchases.
Mobile App
Use MMP Postbacks (AppsFlyer, Adjust, Branch, Tenjin, Singular) to track installs.
Tracking Pixel
Track website events and attribute them to DM conversations
Installation
Add this script tag to your website's <head> or before </body>. Replace YOUR_PIXEL_KEY with the pixel key from Settings, Integrations. Never use your MMP token here: it is a secret.
<script src="https://chat33.io/pixel.js" data-token="YOUR_PIXEL_KEY"></script>Track Events
The pixel auto-tracks page views. To track custom events (purchases, signups, etc.), use the chat33.track() method:
// Track a purchase with value
chat33.track("purchase", {
value: 49.99,
currency: "USD",
product: "Pro Plan"
});
// Track a signup
chat33.track("signup");
// Track a lead
chat33.track("lead", { source: "landing_page" });
// Track any custom event
chat33.track("tutorial_complete", { step: 5 });How it works
Smart Links
Chat33 appends ?cid=xxx to tracked links in DMs
Auto-Attribution
The pixel reads the contact ID from the URL and persists it across pages
Auto-Tagging
Events like 'purchase' automatically tag the contact in your CRM
API Reference
| Method | Parameters | Description |
|---|---|---|
| chat33.track(event, metadata?) | event: string, metadata: object | Send a custom event |
| chat33.identify(contactId) | contactId: string | Manually set the contact ID |
| chat33.getContactId() | — | Returns the current contact ID |
Webhook Events API
Send custom events from your server to Chat33
Endpoint
POST https://chat33.io/api/webhooks/events?token=YOUR_TOKEN
Content-Type: application/json
{
"event": "purchase",
"contactId": "cm1abc123...",
"value": 49.99,
"currency": "USD",
"metadata": {
"product": "Pro Plan",
"source": "checkout"
}
}Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| event | string | Yes | Event name (purchase, signup, lead, etc.) |
| contactId | string | No | Chat33 contact ID for attribution |
| value | number | No | Monetary value of the event |
| currency | string | No | Currency code (USD, EUR, etc.) |
| metadata | object | No | Any additional data |
Examples
# cURL
curl -X POST "https://chat33.io/api/webhooks/events?token=YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"event":"purchase","value":29.99,"currency":"USD"}'// Node.js / fetch
await fetch("https://chat33.io/api/webhooks/events?token=YOUR_TOKEN", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
event: "purchase",
contactId: "cm1abc123...",
value: 29.99,
currency: "USD",
}),
});Rate limit: 100 events per minute per token. Events are stored as analytics events and contacts are automatically tagged based on event names (e.g., "purchase" → "Purchased" tag).
Social Posting API
Schedule and publish content to Instagram, Facebook, TikTok, YouTube, X (Twitter), LinkedIn, Bluesky, and Threads
Authentication
Generate an API key in Settings → API Keys. Include it as the x-api-key header in all requests.
# All API requests require this header:
x-api-key: sk_live_your_api_key_hereSupported Platforms
Photos, reels, carousels (via IG Graph API)
Text, photos, videos with native scheduling
TikTok
Videos and photo posts via Content Posting API
YouTube
Video uploads with native scheduled publishing
X (Twitter)
Text, photos, video, and threads via X API v2 (uses Credits)
Text, link cards, up to 9 images, or video, posted to the member's own profile
Bluesky
Text, link cards, up to 4 images, or video, via the AT Protocol
Threads
Text, images, video, carousels up to 20, polls and topic tags
Create a Post
curl -X POST "https://chat33.io/api/v1/posts" \
-H "x-api-key: sk_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"accountIds": ["channel_id_here"],
"text": "Check out our new product! #launch",
"mediaUrls": ["https://your-blob-url.com/image.jpg"],
"mediaType": "image"
}'Platform options (platformConfig)
Per-platform settings go under a key named after the platform. Only the keys for platforms in accountIds are read, so one request can carry options for several at once. Every platform takes a text override, so a single post can read differently on each network.
Two behaviours worth knowing before your first call: youtube.madeForKids is rejected if missing rather than defaulted, because YouTube requires the declaration on every upload. And a TikTok post with no consent object lands in the account's drafts instead of publishing.
{
"instagram": {
"text": "...", // caption for this platform only
"firstComment": "#tags #here" // posted right after publish
},
"facebook": {
"text": "..."
},
"linkedin": {
"text": "...",
"visibility": "PUBLIC", // or "CONNECTIONS" (1st-degree only)
"articleUrl": "https://...", // link card, ignored when the post has media
"mediaTitle": "...",
"mediaDescription": "..."
},
"youtube": {
"madeForKids": false, // REQUIRED for any YouTube channel
"privacyStatus": "public", // or "unlisted" / "private"
"title": "...",
"description": "...",
"tags": ["one", "two"], // up to 30
"categoryId": "22", // default: People & Blogs
"notifySubscribers": true
},
"x": {
"text": "...",
"thread": true, // split text into a thread
"splitOn": "---",
"replyControl": "everyone" // or "following" / "mentionedUsers"
},
"tiktok": {
"text": "...",
"consent": {
"publishMode": "DIRECT_POST", // or "MEDIA_UPLOAD" (drafts)
"privacyLevel": "SELF_ONLY", // required for DIRECT_POST
"disableComment": false,
"disableDuet": false,
"disableStitch": false,
"aiGenerated": false
}
}
}LinkedIn specifics
- Posts publish to the authenticated member's own profile. Company Pages need LinkedIn's Community Management API, which is a separate review, not supported yet.
- One post carries up to 9 images or 1 video, never both. Over the limit is rejected rather than silently trimmed.
- Caption caps at 3,000 characters. Video 200MB, images 10MB each.
articleUrlonly applies to text posts. Media takes precedence and the link is ignored.- Access expires after 60 days and LinkedIn issues no refresh token, so the account must be reconnected manually. Publishing returns a clear reconnect error once it lapses.
- LinkedIn throttles 150 API requests per member per day, and each image costs an extra two calls, so the cap is on requests, not post count.
Schedule a Post
curl -X POST "https://chat33.io/api/v1/posts" \
-H "x-api-key: sk_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"accountIds": ["ig_channel_id", "fb_channel_id"],
"text": "Coming soon!",
"mediaUrls": ["https://your-blob-url.com/video.mp4"],
"mediaType": "video",
"scheduledAt": "2026-04-15T14:00:00Z",
"callbackUrl": "https://your-server.com/webhook"
}'Upload Media
Multipart upload is hard-capped at 4.5MB. Larger bodies are rejected with 413 FUNCTION_PAYLOAD_TOO_LARGE before reaching the API. For anything bigger, ingest from a public URL, or get a presigned URL and upload straight to storage.
# Small files (< 4.5MB): multipart upload
curl -X POST "https://chat33.io/api/v1/media/upload" \
-H "x-api-key: sk_live_your_key" \
-F "file=@photo.jpg"
# Large files, already on a public URL: URL ingestion (up to 4GB)
curl -X POST "https://chat33.io/api/v1/media/upload" \
-H "x-api-key: sk_live_your_key" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/large-video.mp4", "filename": "video.mp4"}'
# Large LOCAL file: presigned direct upload (up to 500MB per PUT)
# 1. ask for an upload URL
curl -X POST "https://chat33.io/api/v1/media/upload-url" \
-H "x-api-key: sk_live_your_key" \
-H "Content-Type: application/json" \
-d '{"filename": "video.mp4", "contentType": "video/mp4", "size": 16352652}'
# -> {"uploadUrl": "https://...", "headers": {"content-type": "video/mp4"}, ...}
# 2. PUT the bytes straight to storage (never touches the API)
curl -X PUT "<uploadUrl>" \
-H "Content-Type: video/mp4" \
--data-binary @video.mp4
# -> {"url": "https://....public.blob.vercel-storage.com/...", ...}
# pass that url in mediaUrls when creating the postAPI Reference
| Method | Endpoint | Description |
|---|---|---|
POST | /api/v1/posts | Create & queue post(s) |
GET | /api/v1/posts | List posts (paginated, filterable) |
GET | /api/v1/posts/:id | Get post details |
PATCH | /api/v1/posts/:id | Edit draft/queued post or cancel |
DELETE | /api/v1/posts/:id | Delete draft or cancel post |
POST | /api/v1/posts/:id/duplicate | Clone a post |
POST | /api/v1/media/upload | Upload media (file under 4.5MB, or public URL) |
POST | /api/v1/media/upload-url | Presigned URL for direct upload of a large local file |
Request Body: Create Post
| Field | Type | Required | Description |
|---|---|---|---|
| accountIds | string[] | Yes | Channel IDs to post to (max 10) |
| text | string | No | Post caption / description |
| mediaUrls | string[] | No | URLs to media files (uploaded via /media/upload) |
| mediaType | string | No | image, video, reel, or carousel |
| scheduledAt | string | No | ISO 8601 datetime for scheduling |
| callbackUrl | string | No | Webhook URL for publish notifications |
| platformConfig | object | No | Platform-specific options (YouTube title, privacy, etc.) |
| coverImageUrl | string | No | Cover/thumbnail image for videos |
Rate limits: 50 posts per minute per organization. Per-channel daily limits: Instagram (25), Facebook (50), TikTok (20), YouTube (100), X (300).
Callbacks: When a post is published or fails, a POST request is sent to your callbackUrl with the post ID, status, and external post URL.
OpenAPI 3.1 Spec
Interactive endpoint explorer + machine-readable JSON for codegen, n8n, MCP.
AI Docs · llms.txt
Markdown docs for Claude, ChatGPT, Cursor. Copy + paste so AI generates correct API calls.
n8n Node · community node
Official n8n community node. List accounts, create & schedule posts, upload media from any n8n workflow. Install n8n-nodes-chat33 from Settings → Community Nodes.
MCP Server
Connect Claude, Cursor, or any MCP client to Chat33. Post, pull analytics, and build DM flows with natural language.
Connect your users' accounts
Let your own users connect their Instagram, Facebook, TikTok, YouTube, X, LinkedIn, Bluesky, or Threads accounts through Chat33, then publish for them via the API
The developer connect API issues a one-time link that sends one of your users to the platform's own OAuth consent screen, on your behalf, without ever touching their password. Once they approve, the connected account behaves like any other in the Social Posting API above — you post to it, read its analytics where the platform provides them, and disconnect it — scoped to the end user ("profile") it belongs to.
How it works
- Create a profile for the user (
POST /v1/profiles) — or reuse one, keyed on your ownexternalId. - Request a connect link for that profile and a platform (
POST /v1/connect). It's valid once, for 15 minutes. - Send the user to the returned
url. They approve on the platform's own screen, which names Chat33 as the app requesting access — that's expected, and nobody sees their password. - They land back on your
redirectUrlwithstatus,profileId, andaccountIds(orstatus=error&code=…if they cancelled or something went wrong — a cancelled consent does not spend the link).
Create a profile
curl -X POST "https://chat33.io/api/v1/profiles" \
-H "x-api-key: sk_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Fitness",
"externalId": "user_482"
}'Create a connect link
curl -X POST "https://chat33.io/api/v1/connect" \
-H "x-api-key: sk_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"profileId": "prof_abc123",
"platform": "instagram",
"redirectUrl": "https://yourapp.com/oauth/done",
"skipIntro": false
}'
# -> {"url": "https://chat33.io/connect/...", "expiresAt": "...", "platform": "instagram", "profileId": "prof_abc123"}
# send your user to that url
# skipIntro: true returns a url that goes straight to the platform's consent screenWhat your user sees
A Chat33-hosted page naming your app, followed by the platform's own login and consent screen — which names Chat33 as the app asking for access, not your app. That's how the underlying OAuth works and is expected. They can revoke access from the platform itself at any time.
To skip the intro page and send your user straight to the platform's consent screen, pass "skipIntro": true when creating the link. The link is then an immediate redirect: the same token, the same 15-minute expiry, the same return to your redirectUrl. If you skip it, tell your user on your own screen that the consent page will name Chat33, and prefer sending them by a button click rather than a link in an email — mail clients and chat apps prefetch links, and a prefetch of a skipIntro link opens the platform's dialog in the scanner (it does not spend the token).
$1 per connection slot per 30 days. A slot is bought from your prepaid Credits the moment an account connects (or ahead of time with Buy slots) and renewed every 30 days while the account stays connected. No pro-rata and no refunds, but a slot freed by a disconnect stays yours until it expires and the next account you connect takes it. If your balance cannot cover a renewal the account keeps working for 7 days while the amount shows as outstanding; after that it pauses until you top up. Paused accounts are not billed.
Rules
- One real account lives once across all of Chat33 — it cannot be connected a second time, by you or anyone else, whether through the API or the Chat33 dashboard.
- API-connected accounts never count toward your organization's plan channel limit.
- Inbound messages and comments on these accounts are not stored or automated by Chat33 (v1 is publish-only).
- Facebook connects every Page the user grants access to; each Page is its own account and its own slot.
- Instagram connects through Instagram Login, for business or creator accounts.
- X posts through connected accounts also draw from your Credits balance, same as posts from your own dashboard accounts.
Paused accounts
An account whose slot stayed unpaid for 7 days is paused: publishing returns 402 account_paused with the affected accountIds; it resumes automatically when you top up. POST /v1/posts, POST /v1/posts/validate, and POST /v1/posts/:id/duplicate all check for this up front, before queuing anything.
// 402
{
"error": "ACCOUNT_PAUSED",
"code": "account_paused",
"message": "These accounts are paused until outstanding connection charges are paid. Top up credits.",
"accountIds": ["chan_abc123"]
}API Reference
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1/profiles | List your end users |
POST | /api/v1/profiles | Create (or find, by externalId) an end user |
GET | /api/v1/profiles/:id | Get a profile and its accounts |
PATCH | /api/v1/profiles/:id | Update a profile's name or externalId |
DELETE | /api/v1/profiles/:id | Disconnect its accounts and delete the profile |
POST | /api/v1/connect | Create a one-time connect link for a profile |
GET | /api/v1/accounts?source=api | List every account your users connected, across all profiles |
GET | /api/v1/accounts?profileId= | List a profile's connected accounts |
DELETE | /api/v1/accounts/:id | Disconnect an account |
GET | /api/v1/posts?profileId= | List a profile's posts |
GET | /api/v1/analytics/posts?profileId= | Aggregate analytics for a profile's posts |
GET | /api/v1/billing/connections | Credits balance, outstanding, and slot history |
POST | /api/v1/billing/slots | Pre-buy connection slots |
You are responsible for the content you publish through your users' accounts and for having their consent. Chat33 may disable API keys that put its platform apps at risk.
Enable the scope
Get the connect:write scope: create a key in Settings → API Keys and tick Connect your users' accounts. It is off by default on every key.
MMP Install Postbacks
Track app installs from AppsFlyer, Adjust, Branch, Tenjin, or Singular
Postback URL
Configure this URL in your MMP dashboard as a postback endpoint. Replace YOUR_SECRET with your MMP Webhook Secret from Settings.
https://chat33.io/api/webhooks/mmp?secret=YOUR_SECRETSupported MMPs
AppsFlyer
Reads event_type, app_id, appsflyer_id, af_sub1/af_sub2 for attribution
Adjust
Reads activity_kind (install, rejected_install, event), label and adid from the callback, GET or POST. For purchase revenue add the revenue_float placeholder (not revenue, which is in cents) and currency
Branch
Reads webhook events (INSTALL, PURCHASE), the device ids in user_data and the creator and post Chat33 adds to the link data
Tenjin
Reads campaign_id, site_id for attribution
Singular
Reads network, source for attribution
Smart Link Setup
Use the Send an app download link step in your flows. It sends iPhone users to the App Store link and Android users to the Google Play link, and counts every tap on your dashboard, per flow. Paste your MMP tracking link (OneLink, Adjust, Branch, Singular or Tenjin) with one campaign name per post, and your MMP reports installs by post. For Chat33 influencer campaigns, the contact and influencer IDs are added to the link automatically.
Add the download step
In your flow, add Send an app download link with your MMP tracking links
Configure MMP
Set up the postback URL in AppsFlyer, Adjust, Branch (a webhook for the Install event), Tenjin or Singular
Track installs
Taps per flow in Chat33, installs per campaign in your MMP, install totals on your dashboard
No MMP? Use the stores' campaign links
Apple and Google count installs per campaign for free. Put a campaign name in each store link, one per post, and paste the links into the download step. Installs show up in App Store Connect (App Analytics, Sources, Campaigns) and Play Console (acquisition reports by UTM campaign), about a day later and aggregated.
App Store: generate it in App Store Connect, or build it
https://apps.apple.com/app/apple-store/id<APP_ID>?pt=<PROVIDER_TOKEN>&ct=<campaign_name>&mt=8Google Play: the UTM tags go inside referrer, URL-encoded
https://play.google.com/store/apps/details?id=<PACKAGE>&referrer=utm_source%3Dchat33%26utm_medium%3Ddm%26utm_campaign%3D<campaign_name>Step by step: free install tracking without an MMP.
Link Tracking
Track clicks on links sent through DM automations
How it works
When you use tracked links in your flows, Chat33 wraps them in a redirect URL that records the click before forwarding to the destination. The contact ID is appended as a query parameter so downstream pages can identify the visitor.
# Original URL in your flow:
https://yoursite.com/signup
# What gets sent to the user:
https://chat33.io/api/t?url=https://yoursite.com/signup&cid=cm1abc...&fid=flow123
# User is redirected to:
https://yoursite.com/signup?cid=cm1abc...&fid=flow123URL Parameters Appended
| Parameter | Description |
|---|---|
| cid | Chat33 contact ID |
| fid | Flow ID that generated the link |
| ch | Channel ID |
Tip: Combine link tracking with the tracking pixel. When a user clicks a tracked link and lands on your site, the pixel automatically reads the cid parameter and attributes all subsequent events to that contact.
UTM Campaign Link Builder
Generate tracked URLs with UTM parameters for Google Analytics
Tip: Use UTM links in your Chat33 flow messages. When combined with the tracking pixel, you'll see the full journey from DM to conversion in Google Analytics.
App Store Campaign Links
Generate tracked download links for Apple App Store and Google Play
Found in your App Store Connect URL: apps.apple.com/app/id[THIS_NUMBER]
For Chat33 Campaigns: Use these links in your DM flows with the Smart Link node. Chat33 will automatically append the contact ID and influencer ID so installs are attributed back to your campaign.
MMP Integration: If you use AppsFlyer, Adjust, Branch, Tenjin, or Singular, use their OneLink/tracking URLs instead, and configure the Chat33 postback URL in your MMP dashboard. See the MMP Postbacks section.
Need help integrating?
Our team can help you set up tracking and get the most out of Chat33's developer tools.