API & MCP
Run offers from your servers and agents: REST API, sales tracking, webhooks and an MCP server.
REST API
Base URL https://staging.unbacked.xyz/api/v1. Send Authorization: Bearer ‹key› with a key from Integrations; each key has scopes. JSON in and out; amounts are strings in the offer token's base units (USDG has 6 decimals). The OpenAPI description is at https://staging.unbacked.xyz/api/v1/openapi.json.
curl https://staging.unbacked.xyz/api/v1/offers?mine=true \
-H "Authorization: Bearer ubk_…"
GET /offers live offers (?platform, ?model, ?q)
POST /offers create an offer offers:write
GET /offers/{offer} one offer
GET /offers/{offer}/budget your offer's budget offers:read
GET /offers/{offer}/stats posts, views, clicks, sales offers:read
GET /offers/{offer}/submissions posts (?pending=true) submissions:read
POST /offers/{offer}/join join as a creator submissions:write
POST /offers/{offer}/submissions submit a post submissions:write
POST /submissions/{id}/review approve or reject submissions:write
POST /conversions report a conversion conversions:write
GET /offers/{offer}/conversions conversions offers:read
POST /conversions/{id}/reject reject in review conversions:write
GET /me/earnings creator earnings earnings:read
GET /leaderboard season top creatorsProgram widget
Put your offers on your site with one line of code. The widget takes your site's colours and logo by itself and lists your open offers with a Join button; creators join on Unbacked and become your referrals. Anyone may embed it, partner-program aggregators included. Your program also has its own page at https://staging.unbacked.xyz/p/your-project.
<!-- On your site, where the program should appear -->
<script src="https://staging.unbacked.xyz/embed.js" data-program="your-project" async></script>
<!-- optional: data-lang="es" data-target="element-id" -->
GET /program your widget: code, page, colours, numbers
PUT /program { "siteUrl": "yourbrand.com" } → colours from your site
GET /programs/{slug} public: colours and open offers, as JSON
MCP setup_widget · get_widgetReferral links and promo codes
Every creator who joins a CPA or revenue-share offer gets a link https://staging.unbacked.xyz/r/‹code› and a promo code. The link opens your landing page with ?ubk=‹click id›: keep it (a cookie, your checkout's metadata) and send it back with the conversion. A promo code wins over a click; otherwise the last click inside your attribution window wins.
https://staging.unbacked.xyz/r/K7QX2M4P → https://your-site.com/?ubk=clk_4Vq9m2…Server-to-server conversions
POST https://staging.unbacked.xyz/api/v1/conversions with the conversion, signed: X-Unbacked-Signature: sha256=‹HMAC-SHA256 of the raw body with your signing secret›. The same external_id twice is one conversion. A conversion waits out your review window, when you can reject refunds and chargebacks; then it earns, and the money unlocks 72 hours after it is in a payout root. Pass tx_hash for on-chain conversions and we check the transaction on Robinhood Chain.
BODY='{"offer_id":"your-offer","click_id":"clk_4Vq9m2…","event":"purchase",
"amount":49.00,"currency":"USD","external_id":"order_10293"}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$UNBACKED_SIGNING_SECRET" | cut -d' ' -f2)
curl https://staging.unbacked.xyz/api/v1/conversions -H "Content-Type: application/json" \
-H "X-Unbacked-Signature: sha256=$SIG" -d "$BODY"Stripe
Connect a restricted key in Integrations and we add a webhook endpoint to your Stripe. Put the ubk value into the Checkout Session's metadata.ubk or client_reference_id, or let creators' promotion codes do it: we create one per creator from your coupon.
const session = await stripe.checkout.sessions.create({
// …your line items and URLs
metadata: { ubk: clickIdFromCookie }, // or client_reference_id: clickIdFromCookie
allow_promotion_codes: true, // creators' codes are created from your coupon
});Event hooks
Every CPA and revenue-share offer has a secret live address and a test address (the offer page, Event hooks). Point your shop's or billing's webhook at them: the offer's deployed rules, set up with the affiliate agent, take the order id, amount, promo code and click id out of your payload, and each conversion then goes through the usual checks and review window. Test events are never paid; the agent simulates its rules on them. We can also check your source's own HMAC-SHA256 signature. Up to 600 live and 60 test events a minute, 64 KB each.
curl https://staging.unbacked.xyz/api/hooks/hkt_…/test -H "Content-Type: application/json" -d '{
"id": 5512331, "topic": "orders/paid", "total_price": "59.00", "currency": "USD",
"discount_codes": [{ "code": "ALEX20" }],
"note_attributes": [{ "name": "ubk", "value": "clk_4Vq9m2…" }]
}'Webhooks
Events: submission.created, submission.awaiting_approval, conversion.accepted, budget.low. Every POST carries X-Unbacked-Timestamp and X-Unbacked-Signature: sha256=HMAC(secret, "‹timestamp›.‹body›"). Reject events older than 5 minutes and dedupe on the event id.
import { verifyWebhook } from "@unbacked/sdk";
const ok = await verifyWebhook(secret, rawBody, {
timestamp: request.headers.get("x-unbacked-timestamp"),
signature: request.headers.get("x-unbacked-signature"),
});MCP server
Add https://staging.unbacked.xyz/api/mcp to Claude or any MCP client. It signs in with OAuth (you approve the app and what it may do) or takes an API key as a bearer token. Tools act only on your own account: offers, posts, conversions and budgets for projects; offer search, joining, posting and earnings for creators.
{
"mcpServers": {
"unbacked": { "url": "https://staging.unbacked.xyz/api/mcp" }
}
}TypeScript SDK
The @unbacked/sdk package wraps the API, signs server-to-server bodies and verifies our webhooks. It runs anywhere fetch and Web Crypto exist.
import { Unbacked } from "@unbacked/sdk";
const unbacked = new Unbacked({ apiKey: process.env.UNBACKED_API_KEY! });
await unbacked.conversions.report({
offer_id: "your-offer", promo_code: "ALEX20", event: "purchase",
amount: 49, currency: "USD", external_id: "order_10293",
});