Transformer
Agent-run influencer marketplace on X: find creators, send offers, pay for confirmed posts.
Hosted MCP Server
npx add-mcp 'https://transformer.gg/mcp'Installs into Claude Code, Codex, Cursor and more
Documentation
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 login
curl 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 the check.
{
"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 when their window ends (the brand picks 3, 7, 10 or 14 days; default 7). The status field below is the raw state; every offer also carries stageInfo, the one status shown everywhere (sent, viewed, countered, accepted, payment needed, funded, in review, confirmed, paid; not delivered, sent back; declined, expired, cancelled), worded for your side, with stages (history) and timeline (timestamps).
stageInfo.key (the status to show)
sent → viewed → countered_by_creator / countered_by_brand → accepted → funded → in_review → approved → paid_out
problems: not_delivered · payment_needed · rejected · endings: declined · expired · cancelled
raw status (legacy, for older clients)
sent → accepted → submitted → verified → paid
sent → declined · sent / accepted → cancelled · sent → expired
sent: the brand created the offer; the creator gets an invite.accepted: the creator accepted; a tracked link now exists. The brand's saved card is charged at that moment. If the charge fails,stageisawaiting_payment(payment.failuresays why,funding.duehow much and by when, and submitting a post returns 409payment_pending); unpaid by then, the offer is cancelled.submitted: the creator submitted a post; the Transformer team is reviewing it (or some, but not yet all, deliverables are confirmed).verified: the Transformer team confirmed every deliverable: live on X, from the right account, with the tracked link and the Paid partnership label.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 the post check looks for (that link and the disclosure). The post must carry X's Paid partnership label (that's the disclosure; no hashtag is needed). 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). The check 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
Sending needs a card on file (409 card_required with a setupUrl otherwise); the card is charged when the creator accepts, and the payout is held. A failed charge can be paid with POST /offers/:id/pay or POST /pay-due. When the Transformer team confirms the creator's deliverables, 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:
Add to Cursor Opens Cursor with Transformer's MCP server filled in.
Connect from Claude, ChatGPT or VS Code
Add a remote (custom) MCP server with the URL https://transformer.gg/mcp. In Claude: Settings → Connectors → Add custom connector. In ChatGPT: Settings → Apps & Connectors → Create (developer mode). In VS Code: MCP: Add Server → HTTP.
Sign-in is OAuth: the first time, your client opens Transformer in the browser. Sign in, choose the brand or creator workspace it may act for, and approve. No key to copy; disconnect it any time in Settings → Agents. Clients without OAuth use a workspace API key as a bearer token, as in the config below.
{
"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
| 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 the Transformer team's check. |
| 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 accepted offers whose card charge didn't go through: balance first, then a checkout link. |
| list_deals | creator | Accepted offers with their next step (post, submit, confirmed, paid) and due date. |
| submit_deal_post | creator | Submit the post URL for a deal, for the Transformer team's review. |
| 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.
GET /api/v1/me brand or creator key
The workspace this key acts for, its connected X account (creators) and totals. Use it to check whether you're on the brand or creator side.
curl "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
}
}
GET /api/v1/creators?q=fitness brand key
Search creators with a connected X account, by handle or name. Up to 50 results, largest audience first.
| 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/…"
}
]
}
POST /api/v1/offers brand key
Send a creator an offer. Returns 201. 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's saved card is charged when the creator accepts; it's held and released once the Transformer team confirms the post. |
| commissionPercent | number | Optional commission on reported sales, 0–50. Default 0. |
| expiresInDays | integer | Offer is good for (days): 3, 7, 10 or 14; 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",
"X's Paid partnership label"
],
"submissions": [],
"expiresAt": "2026-11-08T14:02:11Z",
"acceptedAt": null,
"verifiedAt": null,
"createdAt": "2026-10-25T14:02:11Z"
}
}
GET /api/v1/offers?status=sent brand or creator key
Offers this workspace sent (brand), or that were sent to this creator or its X handle, newest first.
| 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",
"X's Paid partnership label"
],
"submissions": [],
"expiresAt": "2026-11-08T14:02:11Z",
"acceptedAt": null,
"verifiedAt": null,
"createdAt": "2026-10-25T14:02:11Z"
}
]
}
GET /api/v1/offers/{id} brand or creator key
One offer, including its tracked link once accepted, its submissions and its stats.
curl "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",
"X's Paid partnership label"
],
"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
}
}
}
POST /api/v1/offers/{id}/accept creator key
Accept an offer that's still sent. 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",
"X's Paid partnership label"
],
"submissions": [],
"expiresAt": "2026-11-08T14:02:11Z",
"acceptedAt": "2026-10-25T15:40:03Z",
"verifiedAt": null,
"createdAt": "2026-10-25T14:02:11Z"
}
}
POST /api/v1/offers/{id}/decline creator key
Decline an offer that's still sent.
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",
"X's Paid partnership label"
],
"submissions": [],
"expiresAt": "2026-11-08T14:02:11Z",
"acceptedAt": null,
"verifiedAt": null,
"createdAt": "2026-10-25T14:02:11Z"
}
}
POST /api/v1/offers/{id}/cancel brand key
Cancel an offer that's sent 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",
"X's Paid partnership label"
],
"submissions": [],
"expiresAt": "2026-11-08T14:02:11Z",
"acceptedAt": null,
"verifiedAt": null,
"createdAt": "2026-10-25T14:02:11Z"
}
}
POST /api/v1/offers/{id}/submissions creator key
Submit a post you published on X yourself (Transformer never posts for you), for the Transformer team's review (automated checks assist): it must be public, from the creator's own account, contain everything in the offer's mustInclude (its postLink), carry X's Paid partnership label, plus a video for video offers. The submission is pending until the team confirms it or sends it back with a reason (rejected; you can resubmit). A URL that isn't an X post returns 400.
| 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",
"X's Paid partnership label"
],
"submissions": [
{
"id": "e19b3c7d-2a4f-4b8e-9d1c-6f0a5e2b7c84",
"url": "https://x.com/jane/status/123",
"status": "verified",
"note": "Confirmed by the Transformer team.",
"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"
}
}
POST /api/v1/visits brand key
Record a visit to a brand's own per-creator landing page, server-side. It counts as a click on the offer. Returns 201 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"
}
POST /api/v1/conversions brand key
Report a sale or signup (send amountUsd: 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
}
}
GET /api/v1/offers/{id}/stats brand or creator key
Clicks on the tracked link, reported conversions, revenue, commission and confirmed posts for an offer.
curl "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);
}
Agents can do the same with the 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.