Build on Transformer
Everything in the dashboard is an API call. Brands and creators, people and agents, all use the same REST API, MCP server and CLI with a workspace key.
Quickstart
- Create an account. Sign in with email, Google or GitHub.
- Create a workspace. Choose brand if you send offers, or creator if you receive them. Creators connect their X account from the workspace page.
- Create an API key. On the workspace page, under API keys, create a key. It's shown once and looks like
tf_live_…. Store it asTRANSFORMER_API_KEY. - Connect your agent. Pick one:
claude mcp add --transport http transformer https://transformer.gg/mcp --header "Authorization: Bearer $TRANSFORMER_API_KEY"curl -fsSL https://transformer.gg/install | sh
transformer logincurl https://transformer.gg/api/v1/me \
-H "Authorization: Bearer $TRANSFORMER_API_KEY"Authentication
Every agent-facing request carries a workspace API key as a bearer token:
Authorization: Bearer tf_live_…- One key = one workspace. A key acts for exactly one workspace and can only see and change that workspace's offers. Create separate keys per agent or environment and revoke any of them from the workspace page.
- Brand vs creator. The workspace's kind decides what the key can do. Brand keys search creators, create, fund and cancel offers, and report conversions. Creator keys accept or decline offers and submit the URLs of posts they published on X. Both can list offers, read an offer and read its stats.
- Keep keys server-side. Never ship a key to a browser or mobile app. Conversion reporting in particular belongs on your backend.
Errors are JSON with an error message and a matching HTTP status: 400 invalid input, 401 missing or revoked key, 403 wrong side (e.g. a creator key creating an offer), 404 not found or not yours, 409 the offer isn't in a state that allows this, 422 a post failed verification.
{
"error": "Invalid or revoked API key."
}Concepts
Workspaces
A workspace is a brand or a creator. People join workspaces as members; agents act for a workspace through its API keys. A creator workspace has one connected X account.
Offers and their lifecycle
An offer is what a brand sends a creator: a title and note (brief), a link to promote (url), a deliverable, a quantity, a payout and an optional commission on sales. Brands address creators by X handle (or by email, or creator workspace id); the creator who accepts must own that handle. Unanswered offers expire after expiresInDays (7 by default).
sent: the brand created the offer; the creator gets an invite.accepted: the creator accepted; a tracked link now exists. The brand then pays within 72 hours (until it does,stageisawaiting_payment,funding.duesays how much and by when, and submitting a post returns 409payment_pending); unpaid, the offer is cancelled.submitted: some, but not yet all, of the deliverables have been verified.verified: every deliverable is live on X, from the right account, with the tracked link.paid: the held payout was released to the creator.declinedby the creator,cancelledby the brand, orexpiredunanswered.
Deliverables
post (a single X post), thread (an X thread) or video (an X post with video). quantity (1–20) is how many of them the offer covers. X is the first platform; more will follow.
Tracked links and tf_click
When a creator accepts, the offer gets a tracked link like https://transformer.gg/r/k3v9x2. Each click is counted and redirected to the offer's url with a tf_click query parameter appended, e.g. https://acme.com/launch?tf_click=b2e7d4a1-5c3f-4e8a-9d61-7a0c2f9e8b14. That id identifies the click, and through it the offer and creator.
The link creators post: postLink
Every offer has a postLink, the exact link the creator puts in their post, and mustInclude, the list verification checks for (that link and the disclosure). The post must carry X's Paid partnership label; #ad in the text counts only where X doesn't report the label. If the brand has a landing page per creator, set a landingUrlTemplate such as https://acme.com/x/{handle} (on the brand's pricing settings, a template, or the offer itself): each offer then gets its own landingUrl, e.g. https://acme.com/x/jane, and that is the postLink. Otherwise postLink is the tracked /r/… link (which redirects to the creator's landing page, if there is one, with ?tf_click= appended). Verification accepts the link as X shows it (expanded from t.co), ignoring scheme, www., case, a trailing slash and the query string.
Conversions and commission
A conversion is a sale the brand reports with the tf_click id, an amount and its own order id. If the offer has a commissionPercent, commission is calculated on the amount and credited to the creator. Order ids are unique per offer, so reporting the same order twice is a no-op and retries are safe. If a sale can't be tied to a click, report it with the creator's handle (their latest live offer from you) or the offerId.
Payments
Offers go out unpaid; the brand pays when the creator accepts (POST /offers/:id/pay or POST /pay-due, within 72 hours), and the payout is held. When the creator's deliverables are verified on X, the held payout is released to the creator; the offer moves to paid once it's paid out. Each offer reports its paymentStatus: unfunded, funded, released, paid_out or refunded.
Webhooks
Not yet available. Poll GET /api/v1/offers and GET /api/v1/offers/{id}/stats for now.
MCP
Transformer runs an MCP server at https://transformer.gg/mcp over streamable HTTP, authenticated with the same workspace key. Add it to any MCP client:
{
"mcpServers": {
"transformer": {
"url": "https://transformer.gg/mcp",
"headers": { "Authorization": "Bearer tf_live_…" }
}
}
}Claude Code:
claude mcp add --transport http transformer https://transformer.gg/mcp --header "Authorization: Bearer $TRANSFORMER_API_KEY"Tools
| Tool | Key | What it does |
|---|---|---|
| whoami | either | The workspace this key acts for (brand or creator). |
| search_creators | brand | Find creators by topic or handle, with filters: followers, niche, engagement, price, verified, worked with you. |
| create_offer | brand | Send a creator an offer: what to post about, link, payout, commission. |
| list_offers | either | Offers sent (brand) or received (creator), optionally by status. |
| get_offer | either | One offer, with its tracked link, submissions and payment state. |
| accept_offer | creator | Accept an offer and get its tracked link. |
| decline_offer | creator | Decline an offer. |
| cancel_offer | brand | Cancel an offer that hasn't been completed. |
| submit_post | creator | Submit an existing X post URL for verification. |
| report_conversion | brand | Report a sale with the tf_click id (or an offerId). The same orderId twice is ignored. |
| offer_stats | either | Clicks, conversions, revenue and commission for an offer. |
| pay_offers | brand | Pay for offers creators accepted (you pay only on accept, within 72 hours): balance first, then a checkout link. |
| list_deals | creator | Accepted offers with their next step (post, submit, verified, paid) and due date. |
| submit_deal_post | creator | Submit the post URL for a deal so it can be verified. |
| list_accounts | creator | Your connected X accounts, with pending verifications. |
| start_account_verification | creator | Start verifying an account: returns a code to put in the bio. |
| verify_account | creator | Check the bio for the code and connect the account. |
Tool inputs mirror the REST request bodies below: offer tools take an offerId, search_creators takes a query. Results are the same JSON the REST API returns; failures come back as tool errors with the same message.
CLI
Install the transformer binary, then sign in with a workspace API key:
transformer login # paste a workspace API key
transformer me
transformer creators search fitness
transformer offers create --creator @jane --deliverable post --payout 250 --url https://acme.com/launch --brief "Share what you think of Acme"
transformer offers list --status sent
transformer offers accept <id>
transformer submit <id> https://x.com/jane/status/123
transformer conversions report --click <tf_click> --amount 49 --order A-1001
transformer stats <id>transformer login stores the key locally. In CI or agent sandboxes, set TRANSFORMER_API_KEY instead. Every command calls the REST endpoint of the same name.
REST reference
Base URL https://transformer.gg/api/v1. Requests and responses are JSON. Amounts are in US dollars. In the examples, $OFFER_ID is an offer's id.
/api/v1/mebrand or creator keycurl "https://transformer.gg/api/v1/me" \
-H "Authorization: Bearer $TRANSFORMER_API_KEY"{
"workspace": {
"id": "8d0e5b7a-1f2c-4d3e-8a9b-0c1d2e3f4a5b",
"kind": "brand",
"name": "Acme"
},
"x": null,
"stats": {
"offers": 4,
"active": 1,
"completed": 2,
"payoutsUsd": 500,
"clicks": 640,
"revenueUsd": 882,
"commissionUsd": 88.2
}
}/api/v1/creators?q=fitnessbrand key| Field | Type | Description |
|---|---|---|
| q | string | Part of an X handle or name. |
curl "https://transformer.gg/api/v1/creators?q=fitness" \
-H "Authorization: Bearer $TRANSFORMER_API_KEY"{
"creators": [
{
"workspaceId": "c41a9e2d-7b6f-4a1e-9c3d-5e8f7a6b2c10",
"name": "Jane Park",
"x": "@jane",
"xName": "Jane Park",
"followers": 48210,
"avatar": "https://pbs.twimg.com/…"
}
]
}/api/v1/offersbrand key201. Share inviteUrl with the creator if they're not on Transformer yet.| Field | Type | Description |
|---|---|---|
| creator | string | The creator's X handle ("@jane"), an email address, or a creator workspace id. |
| title | string | Short name for the offer. |
| brief | string | Your note to the creator: what to say, what to avoid. |
| url | string | The page to promote. The tracked link redirects here. |
| deliverable | "post" | "thread" | "video" | What the creator publishes on X. |
| quantity | integer | How many deliverables, 1–20. Default 1. |
| payoutUsd | number | Fixed payout. The brand pays it when the creator accepts; it's held and released on verification. |
| commissionPercent | number | Optional commission on reported sales, 0–50. Default 0. |
| expiresInDays | integer | Days the creator has to accept, 1–90. Default 7. |
curl https://transformer.gg/api/v1/offers \
-H "Authorization: Bearer $TRANSFORMER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"creator":"@jane","title":"Launch week","brief":"Share what you think of Acme","url":"https://acme.com/launch","deliverable":"post","quantity":1,"payoutUsd":250,"commissionPercent":10}'{
"offer": {
"id": "3f6c1a2e-8b1d-4c3e-9a57-2d0b6f1e4c90",
"status": "sent",
"paymentStatus": "unfunded",
"title": "Launch week",
"brief": "Share what you think of Acme",
"url": "https://acme.com/launch",
"deliverable": "post",
"quantity": 1,
"payoutUsd": 250,
"commissionPercent": 10,
"brand": {
"workspaceId": "8d0e5b7a-1f2c-4d3e-8a9b-0c1d2e3f4a5b",
"name": "Acme"
},
"creator": {
"x": "@jane",
"email": null
},
"inviteUrl": "https://transformer.gg/claim/9QxW2mLr7Tq",
"trackedUrl": null,
"landingUrl": "https://acme.com/x/jane",
"postLink": "https://acme.com/x/jane",
"mustInclude": [
"https://acme.com/x/jane",
"#ad"
],
"submissions": [],
"expiresAt": "2026-11-08T14:02:11Z",
"acceptedAt": null,
"verifiedAt": null,
"createdAt": "2026-10-25T14:02:11Z"
}
}/api/v1/offers?status=sentbrand or creator key| Field | Type | Description |
|---|---|---|
| status | string | Optional filter: sent, accepted, submitted, verified, paid, declined, cancelled, expired, or all. |
curl "https://transformer.gg/api/v1/offers?status=sent" \
-H "Authorization: Bearer $TRANSFORMER_API_KEY"{
"offers": [
{
"id": "3f6c1a2e-8b1d-4c3e-9a57-2d0b6f1e4c90",
"status": "sent",
"paymentStatus": "unfunded",
"title": "Launch week",
"brief": "Share what you think of Acme",
"url": "https://acme.com/launch",
"deliverable": "post",
"quantity": 1,
"payoutUsd": 250,
"commissionPercent": 10,
"brand": {
"workspaceId": "8d0e5b7a-1f2c-4d3e-8a9b-0c1d2e3f4a5b",
"name": "Acme"
},
"creator": {
"x": "@jane",
"email": null
},
"inviteUrl": "https://transformer.gg/claim/9QxW2mLr7Tq",
"trackedUrl": null,
"landingUrl": "https://acme.com/x/jane",
"postLink": "https://acme.com/x/jane",
"mustInclude": [
"https://acme.com/x/jane",
"#ad"
],
"submissions": [],
"expiresAt": "2026-11-08T14:02:11Z",
"acceptedAt": null,
"verifiedAt": null,
"createdAt": "2026-10-25T14:02:11Z"
}
]
}/api/v1/offers/{id}brand or creator keycurl "https://transformer.gg/api/v1/offers/$OFFER_ID" \
-H "Authorization: Bearer $TRANSFORMER_API_KEY"{
"offer": {
"id": "3f6c1a2e-8b1d-4c3e-9a57-2d0b6f1e4c90",
"status": "accepted",
"paymentStatus": "unfunded",
"title": "Launch week",
"brief": "Share what you think of Acme",
"url": "https://acme.com/launch",
"deliverable": "post",
"quantity": 1,
"payoutUsd": 250,
"commissionPercent": 10,
"brand": {
"workspaceId": "8d0e5b7a-1f2c-4d3e-8a9b-0c1d2e3f4a5b",
"name": "Acme"
},
"creator": {
"workspaceId": "c41a9e2d-7b6f-4a1e-9c3d-5e8f7a6b2c10",
"name": "Jane Park",
"x": "@jane"
},
"inviteUrl": "https://transformer.gg/claim/9QxW2mLr7Tq",
"trackedUrl": "https://transformer.gg/r/k3v9x2",
"landingUrl": "https://acme.com/x/jane",
"postLink": "https://acme.com/x/jane",
"mustInclude": [
"https://acme.com/x/jane",
"#ad"
],
"submissions": [],
"expiresAt": "2026-11-08T14:02:11Z",
"acceptedAt": "2026-10-25T15:40:03Z",
"verifiedAt": null,
"createdAt": "2026-10-25T14:02:11Z",
"stats": {
"clicks": 312,
"conversions": 9,
"revenueUsd": 441,
"commissionUsd": 44.1,
"verifiedPosts": 0
}
}
}/api/v1/offers/{id}/acceptcreator keysent. The response includes trackedUrl, the link to put in your post. Connect X first; your account must match the handle the offer was sent to.curl -X POST https://transformer.gg/api/v1/offers/$OFFER_ID/accept \
-H "Authorization: Bearer $TRANSFORMER_API_KEY"{
"offer": {
"id": "3f6c1a2e-8b1d-4c3e-9a57-2d0b6f1e4c90",
"status": "accepted",
"paymentStatus": "unfunded",
"title": "Launch week",
"brief": "Share what you think of Acme",
"url": "https://acme.com/launch",
"deliverable": "post",
"quantity": 1,
"payoutUsd": 250,
"commissionPercent": 10,
"brand": {
"workspaceId": "8d0e5b7a-1f2c-4d3e-8a9b-0c1d2e3f4a5b",
"name": "Acme"
},
"creator": {
"workspaceId": "c41a9e2d-7b6f-4a1e-9c3d-5e8f7a6b2c10",
"name": "Jane Park",
"x": "@jane"
},
"inviteUrl": "https://transformer.gg/claim/9QxW2mLr7Tq",
"trackedUrl": "https://transformer.gg/r/k3v9x2",
"landingUrl": "https://acme.com/x/jane",
"postLink": "https://acme.com/x/jane",
"mustInclude": [
"https://acme.com/x/jane",
"#ad"
],
"submissions": [],
"expiresAt": "2026-11-08T14:02:11Z",
"acceptedAt": "2026-10-25T15:40:03Z",
"verifiedAt": null,
"createdAt": "2026-10-25T14:02:11Z"
}
}/api/v1/offers/{id}/declinecreator keysent.curl -X POST https://transformer.gg/api/v1/offers/$OFFER_ID/decline \
-H "Authorization: Bearer $TRANSFORMER_API_KEY"{
"offer": {
"id": "3f6c1a2e-8b1d-4c3e-9a57-2d0b6f1e4c90",
"status": "declined",
"paymentStatus": "unfunded",
"title": "Launch week",
"brief": "Share what you think of Acme",
"url": "https://acme.com/launch",
"deliverable": "post",
"quantity": 1,
"payoutUsd": 250,
"commissionPercent": 10,
"brand": {
"workspaceId": "8d0e5b7a-1f2c-4d3e-8a9b-0c1d2e3f4a5b",
"name": "Acme"
},
"creator": {
"x": "@jane",
"email": null
},
"inviteUrl": "https://transformer.gg/claim/9QxW2mLr7Tq",
"trackedUrl": null,
"landingUrl": "https://acme.com/x/jane",
"postLink": "https://acme.com/x/jane",
"mustInclude": [
"https://acme.com/x/jane",
"#ad"
],
"submissions": [],
"expiresAt": "2026-11-08T14:02:11Z",
"acceptedAt": null,
"verifiedAt": null,
"createdAt": "2026-10-25T14:02:11Z"
}
}/api/v1/offers/{id}/cancelbrand keysent or accepted.curl -X POST https://transformer.gg/api/v1/offers/$OFFER_ID/cancel \
-H "Authorization: Bearer $TRANSFORMER_API_KEY"{
"offer": {
"id": "3f6c1a2e-8b1d-4c3e-9a57-2d0b6f1e4c90",
"status": "cancelled",
"paymentStatus": "unfunded",
"title": "Launch week",
"brief": "Share what you think of Acme",
"url": "https://acme.com/launch",
"deliverable": "post",
"quantity": 1,
"payoutUsd": 250,
"commissionPercent": 10,
"brand": {
"workspaceId": "8d0e5b7a-1f2c-4d3e-8a9b-0c1d2e3f4a5b",
"name": "Acme"
},
"creator": {
"x": "@jane",
"email": null
},
"inviteUrl": "https://transformer.gg/claim/9QxW2mLr7Tq",
"trackedUrl": null,
"landingUrl": "https://acme.com/x/jane",
"postLink": "https://acme.com/x/jane",
"mustInclude": [
"https://acme.com/x/jane",
"#ad"
],
"submissions": [],
"expiresAt": "2026-11-08T14:02:11Z",
"acceptedAt": null,
"verifiedAt": null,
"createdAt": "2026-10-25T14:02:11Z"
}
}/api/v1/offers/{id}/submissionscreator keymustInclude (its postLink and the disclosure), carry X's Paid partnership label, plus a video for video offers. If it can't be verified you get a 422 whose error says why, and the submission is kept as rejected.| Field | Type | Description |
|---|---|---|
| url | string | The URL of a post on X from your connected account. |
curl https://transformer.gg/api/v1/offers/$OFFER_ID/submissions \
-H "Authorization: Bearer $TRANSFORMER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://x.com/jane/status/123"}'{
"offer": {
"id": "3f6c1a2e-8b1d-4c3e-9a57-2d0b6f1e4c90",
"status": "verified",
"paymentStatus": "released",
"title": "Launch week",
"brief": "Share what you think of Acme",
"url": "https://acme.com/launch",
"deliverable": "post",
"quantity": 1,
"payoutUsd": 250,
"commissionPercent": 10,
"brand": {
"workspaceId": "8d0e5b7a-1f2c-4d3e-8a9b-0c1d2e3f4a5b",
"name": "Acme"
},
"creator": {
"workspaceId": "c41a9e2d-7b6f-4a1e-9c3d-5e8f7a6b2c10",
"name": "Jane Park",
"x": "@jane"
},
"inviteUrl": "https://transformer.gg/claim/9QxW2mLr7Tq",
"trackedUrl": "https://transformer.gg/r/k3v9x2",
"landingUrl": "https://acme.com/x/jane",
"postLink": "https://acme.com/x/jane",
"mustInclude": [
"https://acme.com/x/jane",
"#ad"
],
"submissions": [
{
"id": "e19b3c7d-2a4f-4b8e-9d1c-6f0a5e2b7c84",
"url": "https://x.com/jane/status/123",
"status": "verified",
"note": "Verified on X.",
"createdAt": "2026-10-25T16:05:40Z",
"verifiedAt": "2026-10-25T16:05:41Z"
}
],
"expiresAt": "2026-11-08T14:02:11Z",
"acceptedAt": "2026-10-25T15:40:03Z",
"verifiedAt": "2026-10-25T16:05:41Z",
"createdAt": "2026-10-25T14:02:11Z"
}
}/api/v1/visitsbrand key201 with a clickId: keep it in the visitor's session and send it to POST /conversions when they sign up or buy. 404 if there's no live offer to that handle. Repeat visits from the same visitor are deduplicated (duplicate: true).| Field | Type | Description |
|---|---|---|
| handle | string | The creator's X handle, e.g. from your /x/{handle} landing page (with or without @, any case). Uses your latest live offer to them. |
| offerId | string | Instead of handle, when you know the offer. |
| clickId | string | Optional. The tf_click query value if the visitor came through the tracked /r link: that click is reused, not counted twice. |
| url | string | Optional. The landing page URL. |
| ip | string | Optional. The visitor's IP, hashed for deduplication and never stored. |
| userAgent | string | Optional. Bots are not counted. |
| referrer | string | Optional. |
curl https://transformer.gg/api/v1/visits \
-H "Authorization: Bearer $TRANSFORMER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"handle":"jane","ip":"203.0.113.7","userAgent":"Mozilla/5.0 …","referrer":"https://t.co/abc"}'{
"recorded": true,
"duplicate": false,
"clickId": "b2e7d4a1-5c3f-4e8a-9d61-7a0c2f9e8b14",
"offerId": "3f6c1a2e-8b1d-4c3e-9a57-2d0b6f1e4c90"
}/api/v1/conversionsbrand keyamountUsd: 0 for a signup) that came from a creator's link. Returns 201; duplicate is true if that order was already reported. Call it from your backend; see conversion tracking.| Field | Type | Description |
|---|---|---|
| clickId | string | The tf_click value from the landing URL, or the clickId from POST /visits (preferred). |
| handle | string | The creator's X handle. Used when there's no clickId, or as a fallback if the clickId is unknown. Uses your latest live offer to them. |
| offerId | string | Instead of clickId, when the sale can't be tied to a click. |
| amountUsd | number | Order value the commission is calculated on. |
| orderId | string | Your order id. Reporting the same order twice is a no-op. |
curl https://transformer.gg/api/v1/conversions \
-H "Authorization: Bearer $TRANSFORMER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"clickId":"b2e7d4a1-5c3f-4e8a-9d61-7a0c2f9e8b14","amountUsd":49.00,"orderId":"A-1001"}'{
"recorded": true,
"duplicate": false,
"offerId": "3f6c1a2e-8b1d-4c3e-9a57-2d0b6f1e4c90",
"amountUsd": 49,
"commissionUsd": 4.9,
"stats": {
"clicks": 312,
"conversions": 9,
"revenueUsd": 441,
"commissionUsd": 44.1,
"verifiedPosts": 1
}
}/api/v1/offers/{id}/statsbrand or creator keycurl "https://transformer.gg/api/v1/offers/$OFFER_ID/stats" \
-H "Authorization: Bearer $TRANSFORMER_API_KEY"{
"stats": {
"clicks": 312,
"conversions": 9,
"revenueUsd": 441,
"commissionUsd": 44.1,
"verifiedPosts": 1
}
}Conversion tracking
With the tracked /r/… link, attribution takes three small pieces on the brand's side: the click id travels in the URL, and you report the sale from your server. With your own per-creator landing pages, see below.
1. Read tf_click on your site
Visitors from a tracked link land on your offer url with ?tf_click=… appended. Read it on any page they might land on and keep it (local storage, a first-party cookie or your session) until checkout.
// On every landing page: keep the click id from the tracked link.
const tfClick = new URLSearchParams(location.search).get("tf_click");
if (tfClick) localStorage.setItem("tf_click", tfClick);
// At checkout: send it with the order to your own backend.
await fetch("/checkout", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ cart, tfClick: localStorage.getItem("tf_click") }),
});2. Store it with the order
Save the tf_click value on the order record when it's created, the same way you'd keep a coupon code.
3. Report the conversion server-side
When the order is paid, call POST /api/v1/conversions with a brand key. Use your own order id so a retry never double-counts.
// On your server, once the order is paid (store tfClick with the order first).
async function reportConversion(order: { id: string; totalUsd: number; tfClick: string | null }) {
if (!order.tfClick) return; // not from a tracked link
const res = await fetch("https://transformer.gg/api/v1/conversions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TRANSFORMER_API_KEY}`, // brand workspace key
"Content-Type": "application/json",
},
body: JSON.stringify({ clickId: order.tfClick, amountUsd: order.totalUsd, orderId: order.id }),
});
if (!res.ok) throw new Error((await res.json()).error);
}report_conversion MCP tool or transformer conversions report --click <tf_click> --amount 49 --order A-1001.Per-creator landing pages
If creators post a link to your own page (e.g. https://acme.com/x/jane, from landingUrlTemplate), visitors don't pass through the tracked redirect, so tell Transformer about the visit yourself. On the server that renders /x/:handle:
// GET /x/:handle — record the visit, keep the clickId for the signup.
const res = await fetch("https://transformer.gg/api/v1/visits", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TRANSFORMER_API_KEY}`, // brand workspace key
"Content-Type": "application/json",
},
body: JSON.stringify({ handle: req.params.handle, clickId: req.query.tf_click, ip: req.ip, userAgent: req.get("user-agent"), referrer: req.get("referer") }),
});
if (res.ok) req.session.tfClick = (await res.json()).clickId;Then report the signup or sale with that clickId. If you didn't keep it, send the creator's handle instead:
curl https://transformer.gg/api/v1/conversions \
-H "Authorization: Bearer $TRANSFORMER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"clickId":"b2e7d4a1-5c3f-4e8a-9d61-7a0c2f9e8b14","amountUsd":0,"orderId":"signup-8812"}'
# or, without a clickId: -d '{"handle":"jane","amountUsd":0,"orderId":"signup-8812"}'Pixel
No server code? Put a 1×1 pixel on the landing page instead. Each load counts as a visit to that creator's live offer (bots and link previews aren't counted). Use your brand workspace's slug and the creator's handle:
<img src="https://transformer.gg/p/<brand-slug>/<handle>.gif" width="1" height="1" alt="" />The pixel only counts visits. Report signups and sales with POST /conversions and the creator's handle.