# Clickwise — complete guide for developers and AI agents > Clickwise is an affiliate meta-network: one publisher account, one tracking link format and one payout > across many merchant programs. Sites, apps, bots, creators and AI agents find programs and deals, create > tracked links, read their conversions and call a product search endpoint (by text or GTIN) through a REST > API or a remote MCP server. This file is self-contained; the short index is https://partners.clickwise.net/llms.txt and the human page is https://partners.clickwise.net/developers/. ## First successful call 1. Get a sandbox key, no account needed: `POST https://partners.clickwise.net/api/v1/developers/sandbox-keys/` with JSON `{"email": "you@example.com", "use_case": "price_comparison"}` (use_case: price_comparison, woocommerce, mcp_agent, deals_bot, ai_store, other), or the form at https://partners.clickwise.net/developers/#sandbox. Open the emailed link once to reveal the key (`cw_sbx_...`). 2. Call the API: `curl "https://partners.clickwise.net/api/v1/publisher/products/?q=sander&country=GB&limit=3" -H "X-API-Key: $CLICKWISE_API_KEY"` 3. Pick a quickstart below. To earn, create a free publisher account (https://partners.clickwise.net/en/signup_affiliate/) and use your own key: the code does not change. Without any key: public MCP tools (https://partners.clickwise.net/api/v1/mcp), the deals API and the public catalog sample. ## Credentials | Credential | Works on | Limits | |---|---|---| | Sandbox key `cw_sbx_...` (self-service by email) | Publisher Products API only (`products/` and `products/lookup/`) plus the MCP tool `search_products`; example tracked links, and results can be empty | 10 requests/min, 500/day, 14 days; a test identity that never earns commission | | Publisher API key (your account) | The whole Publisher API v1, the MCP account tools, the HasOffers/TUNE-compatible endpoint | 120 requests/min per account; 60/min for products | | OAuth 2.1 on the MCP server | https://partners.clickwise.net/api/v1/mcp/account: read your programs, reports and conversions; create tracked links; product search | Access tokens 1 h, rotating refresh tokens 30 days; revoke at https://partners.clickwise.net/oauth/connections/ | | None | Public MCP tools, deals and news APIs, catalog statistics and sample | Per-address limits | A sandbox key or an OAuth connection never applies to programs. No key or token changes payout details or postback settings, or approves applications, commissions or payments. Send keys only in the `X-API-Key` or `Authorization: Bearer` header, never in URLs, page source or browser code. ## Quickstarts by use case ### Price comparison or GTIN shop Search products by name in one market, then compare every offer for a GTIN (EAN/UPC), cheapest first. Each product carries a ready tracked link in url. 1. Search: GET /api/v1/publisher/products/?q=<3-200 chars>&country=. Follow next_offset for more. 2. Compare: GET /api/v1/publisher/products/lookup/?gtins=&country= (1-100 ids and GTINs). 3. Publish url exactly as returned (rel="sponsored nofollow"); refresh prices with lookup at least daily and hide items that are no longer returned. curl: ```bash # 1. Products whose title contains "sander" in the United Kingdom curl --get "https://partners.clickwise.net/api/v1/publisher/products/" \ -H "X-API-Key: $CLICKWISE_API_KEY" \ --data-urlencode "q=sander" --data-urlencode "country=GB" --data-urlencode "limit=20" # 2. Every offer for the first product's GTIN (needs jq; keep GTINs as strings) GTIN=$(curl -s --get "https://partners.clickwise.net/api/v1/publisher/products/" -H "X-API-Key: $CLICKWISE_API_KEY" \ --data-urlencode "q=sander" --data-urlencode "country=GB" --data-urlencode "limit=1" | jq -r '.products[0].gtin') curl --get "https://partners.clickwise.net/api/v1/publisher/products/lookup/" \ -H "X-API-Key: $CLICKWISE_API_KEY" \ --data-urlencode "gtins=$GTIN" --data-urlencode "country=GB" ``` JavaScript: ```javascript // Node 18+ (built-in fetch). Run: CLICKWISE_API_KEY=... node compare.mjs const BASE = "https://partners.clickwise.net/api/v1/publisher"; const headers = { "X-API-Key": process.env.CLICKWISE_API_KEY }; async function get(path) { const res = await fetch(`${BASE}${path}`, { headers }); const body = await res.json(); if (!res.ok) throw new Error(`${res.status} ${body.error?.code}: ${body.error?.message}`); return body; } export async function compare(gtin, country = "GB") { const { products } = await get(`/products/lookup/?gtins=${encodeURIComponent(gtin)}&country=${country}`); return products .filter((p) => p.availability === "in_stock") .sort((a, b) => Number(a.price) - Number(b.price)) .map((p) => ({ merchant: p.merchant.name, price: `${p.price} ${p.currency}`, link: p.url })); } const { products } = await get("/products/?q=sander&country=GB&limit=5"); console.table(products.map((p) => ({ title: p.title, gtin: p.gtin, price: p.price }))); if (products.length) console.table(await compare(products[0].gtin)); ``` Python: ```python # pip install requests. Run: CLICKWISE_API_KEY=... python compare.py import os import requests BASE = "https://partners.clickwise.net/api/v1/publisher" HEADERS = {"X-API-Key": os.environ["CLICKWISE_API_KEY"]} def get(path: str, **params) -> dict: r = requests.get(f"{BASE}{path}", params=params, headers=HEADERS, timeout=20) r.raise_for_status() return r.json() def compare(gtin: str, country: str = "GB") -> list[dict]: offers = [p for p in get("/products/lookup/", gtins=gtin, country=country)["products"] if p["availability"] == "in_stock"] return sorted(offers, key=lambda p: float(p["price"])) found = get("/products/", q="sander", country="GB", limit=5)["products"] for p in found: print(p["title"], p["gtin"], p["price"], p["currency"]) if found: for p in compare(found[0]["gtin"]): print(p["merchant"]["name"], p["price"], p["currency"], p["url"]) ``` ### WooCommerce / WordPress import (WP All Import) Turn API results into a CSV that WP All Import maps to WooCommerce external/affiliate products, then refresh it on a schedule. 1. Try the columns first with the public sample (no key, no links): https://partners.clickwise.net/api/v1/public/catalog-sample/?country=ES&format=csv 2. Generate your import file with the script below (JavaScript or Python) from your searches; each row keeps its tracked link in product_url. 3. WP All Import: New Items → WooCommerce Products → Product type "External/Affiliate product". Map title → Title, price → Regular Price, product_url → Product URL, button_text → Button text, image → Images (download from URL), sku → SKU, gtin → "GTIN, UPC, EAN or ISBN" (WooCommerce 9.2+). Unique identifier: {sku}. 4. Schedule the import daily with "Delete products that are no longer present in your file" on, so expired offers disappear. curl: ```bash # Sample columns (no key, no tracked links): map them before you have a key curl -o clickwise-sample.csv "https://partners.clickwise.net/api/v1/public/catalog-sample/?country=ES&format=csv" # Products with your tracked links, as JSON, one page of 100 curl --get "https://partners.clickwise.net/api/v1/publisher/products/" -H "X-API-Key: $CLICKWISE_API_KEY" \ --data-urlencode "q=drill" --data-urlencode "country=GB" --data-urlencode "limit=100" ``` JavaScript: ```javascript // Node 18+: writes clickwise-woocommerce.csv for WP All Import import { writeFileSync } from "node:fs"; const BASE = "https://partners.clickwise.net/api/v1/publisher"; const headers = { "X-API-Key": process.env.CLICKWISE_API_KEY }; const queries = ["drill", "bosch"]; const country = "GB"; const cols = ["sku", "gtin", "title", "price", "currency", "image", "product_url", "button_text", "merchant", "availability", "country", "updated_at"]; const csv = (v) => `"${String(v ?? "").replaceAll('"', '""')}"`; const rows = new Map(); for (const q of queries) { let offset = 0; while (offset !== null && offset < 1000) { const url = `${BASE}/products/?q=${encodeURIComponent(q)}&country=${country}&limit=100&offset=${offset}`; const res = await fetch(url, { headers }); if (res.status === 429) { await new Promise((r) => setTimeout(r, 1000 * Number(res.headers.get("Retry-After") || 60))); continue; } const body = await res.json(); if (!res.ok) throw new Error(`${res.status} ${body.error?.code}`); for (const p of body.products) { rows.set(`${p.id}-${p.country}`, [`${p.id}-${p.country}`, p.gtin, p.title, p.price, p.currency, p.image, p.url, `Buy at ${p.merchant.name}`, p.merchant.name, p.availability, p.country, p.updated_at]); } offset = body.next_offset; } } writeFileSync("clickwise-woocommerce.csv", [cols, ...rows.values()].map((r) => r.map(csv).join(",")).join("\n")); console.log(`${rows.size} products written`); ``` Python: ```python # pip install requests. Writes clickwise-woocommerce.csv for WP All Import import csv import os import time import requests BASE = "https://partners.clickwise.net/api/v1/publisher" HEADERS = {"X-API-Key": os.environ["CLICKWISE_API_KEY"]} QUERIES, COUNTRY = ["drill", "bosch"], "GB" COLS = ["sku", "gtin", "title", "price", "currency", "image", "product_url", "button_text", "merchant", "availability", "country", "updated_at"] rows = {} for q in QUERIES: offset = 0 while offset is not None and offset < 1000: r = requests.get(f"{BASE}/products/", headers=HEADERS, timeout=30, params={"q": q, "country": COUNTRY, "limit": 100, "offset": offset}) if r.status_code == 429: time.sleep(int(r.headers.get("Retry-After", 60))) continue r.raise_for_status() body = r.json() for p in body["products"]: sku = f'{p["id"]}-{p["country"]}' rows[sku] = [sku, p["gtin"], p["title"], p["price"], p["currency"], p["image"], p["url"], f'Buy at {p["merchant"]["name"]}', p["merchant"]["name"], p["availability"], p["country"], p["updated_at"]] offset = body["next_offset"] with open("clickwise-woocommerce.csv", "w", newline="", encoding="utf-8") as fh: writer = csv.writer(fh) writer.writerow(COLS) writer.writerows(rows.values()) print(len(rows), "products written") ``` PHP (WordPress): ```php 100, 'type' => 'external', 'return' => 'objects']); $by_gtin = []; foreach ($products as $product) { $gtin = $product->get_global_unique_id(); if ($gtin) { $by_gtin[$gtin] = $product; } } if (!$by_gtin) { return; } $url = add_query_arg(['gtins' => implode(',', array_keys($by_gtin)), 'country' => 'DE'], 'https://partners.clickwise.net/api/v1/publisher/products/lookup/'); $response = wp_remote_get($url, ['timeout' => 20, 'headers' => ['X-API-Key' => CLICKWISE_API_KEY]]); if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) { return; } $seen = []; foreach (json_decode(wp_remote_retrieve_body($response), true)['products'] as $p) { $product = $by_gtin[$p['gtin']] ?? null; if (!$product || isset($seen[$p['gtin']])) { continue; } $seen[$p['gtin']] = true; $product->set_regular_price($p['price']); $product->set_product_url($p['url']); $product->set_status($p['availability'] === 'in_stock' ? 'publish' : 'draft'); $product->save(); } foreach ($by_gtin as $gtin => $product) { // no longer offered: hide it if (!isset($seen[$gtin])) { $product->set_status('draft'); $product->save(); } } }); if (!wp_next_scheduled('clickwise_refresh_prices')) { wp_schedule_event(time(), 'daily', 'clickwise_refresh_prices'); } ``` ### AI agent over MCP (Claude, Cursor, ChatGPT) Two remote MCP endpoints over Streamable HTTP. Public, no sign-in: programs, deals and catalog statistics. Your account, OAuth sign-in: your programs, reports, conversions, product search and tracked links (read and links only). 1. Public tools, no key: https://partners.clickwise.net/api/v1/mcp 2. Your account: https://partners.clickwise.net/api/v1/mcp/account (the client opens Clickwise sign-in and asks you to approve read and links access; revoke it any time at https://partners.clickwise.net/oauth/connections/). 3. Claude (claude.ai, Desktop): Settings → Connectors → Add custom connector → paste the URL. 4. Claude Code: claude mcp add --transport http clickwise https://partners.clickwise.net/api/v1/mcp/account, then run /mcp to sign in. 5. Cursor: add the server to ~/.cursor/mcp.json (below); Cursor starts the sign-in on first use. 6. ChatGPT: enable developer mode, create a connector with the /api/v1/mcp/account URL and OAuth authentication. 7. Headless agents can send a key instead: header X-API-Key on https://partners.clickwise.net/api/v1/mcp. A sandbox key covers search_products (by text or GTIN); every other account tool needs your publisher key. curl: ```bash # Public tool call over plain JSON-RPC (no key) curl -s "https://partners.clickwise.net/api/v1/mcp" \ -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \ -H "MCP-Protocol-Version: 2025-06-18" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"find_affiliate_programs","arguments":{"topic":"headphones","country":"DE","limit":5}}}' # Cursor: ~/.cursor/mcp.json # {"mcpServers": {"clickwise": {"url": "https://partners.clickwise.net/api/v1/mcp/account"}}} # Claude Code with a key instead of sign-in claude mcp add --transport http clickwise "https://partners.clickwise.net/api/v1/mcp" --header "X-API-Key: $CLICKWISE_API_KEY" ``` JavaScript: ```javascript // npm install @modelcontextprotocol/sdk import { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; const client = new Client({ name: "my-shopping-agent", version: "1.0.0" }); await client.connect(new StreamableHTTPClientTransport(new URL("https://partners.clickwise.net/api/v1/mcp"))); const { tools } = await client.listTools(); console.log(tools.map((t) => t.name)); const stats = await client.callTool({ name: "get_catalog_stats", arguments: {} }); console.log(stats.content[0].text); await client.close(); ``` Python: ```python # pip install mcp import asyncio from mcp import ClientSession from mcp.client.streamable_http import streamablehttp_client async def main(): async with streamablehttp_client("https://partners.clickwise.net/api/v1/mcp") as (read, write, _): async with ClientSession(read, write) as session: await session.initialize() tools = await session.list_tools() print([t.name for t in tools.tools]) result = await session.call_tool("find_affiliate_programs", {"topic": "running shoes", "country": "ES", "limit": 5}) print(result.content[0].text) asyncio.run(main()) ``` ### Coupon and deals bot Post new merchant deals and coupon codes to Telegram, Discord or a newsletter. The deals API is public (no key); link each post to the deal page, or mint your own tracked link with your publisher key. 1. Poll GET /api/v1/deals/?lang=en&page_size=50 (lang: en, es, pt, fr, de, it); results are newest first. 2. Remember the ids you already posted; skip deals whose valid_until has passed. 3. Link to https://partners.clickwise.net + url (the public deal page). With a publisher key, POST /api/v1/publisher/links/ with subid "telegram" to earn and see clicks per channel in /reports/?group_by=subid. 4. RSS alternative: https://partners.clickwise.net/api/v1/deals/feed.en.xml curl: ```bash curl "https://partners.clickwise.net/api/v1/deals/?lang=en&page_size=20" # Your own tracked link for a deal's merchant (publisher key, earns commission) curl -X POST "https://partners.clickwise.net/api/v1/publisher/links/" -H "X-API-Key: $CLICKWISE_API_KEY" \ -H "Content-Type: application/json" -H "Idempotency-Key: deal-123-telegram" \ -d '{"campaign_id": 123, "subid": "telegram"}' ``` JavaScript: ```javascript // Node 18+: post new deals to a Discord channel webhook every run (cron it) import { existsSync, readFileSync, writeFileSync } from "node:fs"; const SEEN = "seen-deals.json"; const seen = new Set(existsSync(SEEN) ? JSON.parse(readFileSync(SEEN, "utf8")) : []); const { results } = await (await fetch("https://partners.clickwise.net/api/v1/deals/?lang=en&page_size=50")).json(); for (const d of results.reverse()) { if (seen.has(d.id) || (d.valid_until && new Date(d.valid_until) < new Date())) continue; const off = d.discount_pct ? `${d.discount_pct}% off` : d.discount_amount ? `${d.discount_amount} ${d.discount_currency} off` : ""; const code = d.code ? ` · code **${d.code}**` : ""; await fetch(process.env.DISCORD_WEBHOOK_URL, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ content: `**${d.merchant_name}**: ${d.title} ${off}${code}\nhttps://partners.clickwise.net${d.url}` }), }); seen.add(d.id); } writeFileSync(SEEN, JSON.stringify([...seen])); ``` Python: ```python # pip install requests. Post new deals to a Telegram channel (cron it) import json import os from datetime import datetime, timezone from pathlib import Path import requests SEEN = Path("seen-deals.json") seen = set(json.loads(SEEN.read_text())) if SEEN.exists() else set() deals = requests.get("https://partners.clickwise.net/api/v1/deals/", params={"lang": "en", "page_size": 50}, timeout=20).json()["results"] now = datetime.now(timezone.utc) for d in reversed(deals): if d["id"] in seen or (d["valid_until"] and datetime.fromisoformat(d["valid_until"]) < now): continue off = f'{d["discount_pct"]}% off' if d["discount_pct"] else "" code = f' · code {d["code"]}' if d["code"] else "" text = f'{d["merchant_name"]}: {d["title"]} {off}{code}\nhttps://partners.clickwise.net{d["url"]}' requests.post(f'https://api.telegram.org/bot{os.environ["TELEGRAM_TOKEN"]}/sendMessage', json={"chat_id": os.environ["TELEGRAM_CHAT_ID"], "text": text}, timeout=20) seen.add(d["id"]) SEEN.write_text(json.dumps(sorted(seen))) ``` ### Affiliate store built with Lovable, Bolt or v0 Paste the prompt into Lovable, Bolt or v0, add your key as a server-side secret named CLICKWISE_API_KEY, and publish. The store calls the API from a server function only. 1. Get a sandbox key (above) and add it as a secret: Lovable → Supabase secret, Bolt → .env on the server, v0/Vercel → Environment Variable. Never in client code. 2. Paste the prompt below. It asks for a server function (Supabase Edge Function, Next.js Route Handler or a server route) that proxies the products API and caches for 10 minutes. 3. Publish, then swap the sandbox key for your own publisher key so purchases pay you. Prompt to paste into Lovable, Bolt or v0: ```text Build a fast, mobile-first affiliate product store called "My Deals Store" that lists products from the Clickwise Publisher Products API. Data source (server side only): - Base URL: https://partners.clickwise.net/api/v1/publisher/ - Send the header X-API-Key with the value of the secret CLICKWISE_API_KEY. Never expose the key in browser code, URLs or logs: call the API only from a server function (Supabase Edge Function, Next.js Route Handler or server route) and have the frontend call that function. - Search: GET products/?q=&country=&limit=24&offset=. q needs 3-200 characters. Response: {products: [{id, gtin, title, price, currency, image, url, merchant: {id, name}, availability, country, updated_at}], count, next_offset}. Use next_offset for "Load more" (null means no more). - Refresh: GET products/lookup/?gtins=&country= (up to 100) returns current price and stock; hide products missing from the answer. - Errors are JSON {"error": {"code", "message", "status"}}. On 429 wait the Retry-After seconds; on 503 retry with backoff. Cache successful answers server side for 10 minutes. Pages and UI: - Home: a search box, a country selector and category chips (drills, saws, sanders, garden tools) that run searches. - Product grid: image, title, merchant name, price formatted with its currency, an availability badge and a "View deal" button. - The button opens product.url exactly as returned (it is the tracked link) in a new tab with rel="sponsored nofollow noopener". Never rewrite, shorten or strip that URL. - Product detail drawer with the GTIN and "last updated" time. - Footer on every page: "We earn a commission from purchases made through links on this site, at no extra cost to you." - Loading, empty and error states; responsive, accessible, lazy-loaded images. Show only prices the API returns; never invent or store prices. ``` curl: ```bash # Check your key before you build (expects a JSON list of products) curl --get "https://partners.clickwise.net/api/v1/publisher/products/" -H "X-API-Key: $CLICKWISE_API_KEY" \ --data-urlencode "q=sander" --data-urlencode "country=GB" --data-urlencode "limit=3" ``` JavaScript: ```javascript // Next.js Route Handler: app/api/products/route.js (v0 / Vercel). The key stays on the server. export const revalidate = 600; export async function GET(request) { const { searchParams } = new URL(request.url); const q = (searchParams.get("q") || "sander").slice(0, 200); const country = /^[A-Z]{2}$/.test(searchParams.get("country") || "") ? searchParams.get("country") : "GB"; const offset = Number(searchParams.get("offset") || 0); const upstream = await fetch( `https://partners.clickwise.net/api/v1/publisher/products/?q=${encodeURIComponent(q)}&country=${country}&limit=24&offset=${offset}`, { headers: { "X-API-Key": process.env.CLICKWISE_API_KEY }, next: { revalidate: 600 } }, ); return new Response(await upstream.text(), { status: upstream.status, headers: { "Content-Type": "application/json", "Cache-Control": "s-maxage=600" }, }); } ``` Python: ```python # pip install fastapi uvicorn httpx. Minimal proxy for any frontend: uvicorn store:app import os import httpx from fastapi import FastAPI, Query app = FastAPI() BASE = "https://partners.clickwise.net/api/v1/publisher" @app.get("/api/products") async def products(q: str = Query("sander", min_length=3, max_length=200), country: str = Query("GB", pattern="^[A-Z]{2}$"), offset: int = 0): async with httpx.AsyncClient(timeout=20) as client: r = await client.get(f"{BASE}/products/", headers={"X-API-Key": os.environ["CLICKWISE_API_KEY"]}, params={"q": q, "country": country, "limit": 24, "offset": offset}) return r.json() ``` ## Publisher API v1 reference Base `https://partners.clickwise.net/api/v1/publisher/`. OpenAPI 3.1: https://partners.clickwise.net/api/v1/publisher/openapi.json · Postman: https://partners.clickwise.net/api/v1/publisher/postman.json - `GET /api/v1/publisher/me/` — Who am I: account, access and limits. - `GET /api/v1/publisher/products/` — Search GTIN products with your tracked links. Query: q, brand, category, country, merchant, group, min_price, max_price, currency, limit, offset. - `GET /api/v1/publisher/products/lookup/` — Refresh price and stock by id and/or GTIN (1-100). Query: ids, gtins, country, merchant, limit, offset. - `GET /api/v1/publisher/trending-opportunities/` — Current Trend Radar opportunities for your opted-in account. Query: format, country, channel, limit. - `GET /api/v1/publisher/offers/` — Authorized coupons and deals for one country (pilot). Query: country, limit, page. - `GET /api/v1/publisher/programs/` — Programs you may promote (and, optionally, can apply to). Query: search, include_available, page, page_size. - `GET /api/v1/publisher/programs/joinable/` — Programs open for application. Query: search, page, page_size. - `POST /api/v1/publisher/programs/apply/` — Apply to a program (approved or pending review). - `GET /api/v1/publisher/links/` — Your tracking links. Query: subid, campaign_id, page, page_size. - `POST /api/v1/publisher/links/` — Create or return one tracking link. - `POST /api/v1/publisher/links/batch/` — Create up to 50 links; each item reports its own status. - `GET /api/v1/publisher/conversions/` — Your conversions with a sub-ID rollup. Query: date_from, date_to, subid, status, source, page, page_size. - `GET /api/v1/publisher/reports/` — Conversions and commission grouped (time series or dimension). Query: group_by, date_from, date_to, subid, status. - `GET /api/v1/publisher/payment-status/` — Your payment status: balance, next payment, method type. - `GET /api/v1/publisher/clicks/` — Your clicks on Clickwise redirects. Query: sub_id, campaign_id, deeplink_id, include_bots, page, page_size. - `GET /api/v1/publisher/postback/` — Conversion postback (webhook) configuration. - `PUT /api/v1/publisher/postback/` — Not available with an API key: set the postback URL in the portal. - `POST /api/v1/publisher/postback/secret/` — Not available with an API key: rotate the secret in the portal. - `POST /api/v1/publisher/postback/test/` — Send a signed test event. - `GET /api/v1/publisher/postback/deliveries/` — Recent postback delivery attempts. Conventions: every response carries `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`, `RateLimit-Policy` and `X-Request-Id`; a 429 adds `Retry-After`, and so does a 503 for a query over its time budget: on 503, retry once after `Retry-After`. Errors have one shape: `{"detail": "...", "error": {"code": "...", "message": "...", "status": 403, "request_id": "..."}}`. POST requests accept `Idempotency-Key` (24 h replay). Products paginate with `limit`/`offset` and `next_offset`; lists with `page`/`page_size`. Changes inside v1 are additive. Sandbox responses carry `X-Clickwise-Sandbox: 1`. Product `url` values are Clickwise tracked links: publish them unchanged. ## MCP server - Public endpoint (no sign-in): https://partners.clickwise.net/api/v1/mcp - Account endpoint (OAuth 2.1): https://partners.clickwise.net/api/v1/mcp/account — answers 401 with `WWW-Authenticate: Bearer resource_metadata=...` until the client signs the publisher in. - Transport: Streamable HTTP, JSON-RPC 2.0. Protocol versions: 2026-07-28, 2025-11-25, 2025-06-18, 2025-03-26. - OAuth metadata: https://partners.clickwise.net/.well-known/oauth-protected-resource/api/v1/mcp/account (RFC 9728) and https://partners.clickwise.net/.well-known/oauth-authorization-server (RFC 8414). Dynamic client registration (RFC 7591, public clients), authorization code with PKCE S256, resource indicators (RFC 8707). Scopes: `affiliate:read` (programs, reports, conversions) and `affiliate:links` (tracked links and product search). `get_product_feed` needs an API key: its signed feed URL outlives an OAuth connection. - Server card (SEP-2127 draft): https://partners.clickwise.net/.well-known/mcp-server-card · manifest: https://partners.clickwise.net/.well-known/mcp.json Tools: - `list_trending_opportunities` (your account: publisher key or OAuth scope affiliate:read): Read current Trend Radar opportunities. With your own affiliate API key, returns your existing placements and tracked links; no links or accounts are created by this tool. Without authentication, returns only public topics, evidence, geography, expiry and merchants. Requires feature flag CW_TREND_FEED_ENABLED. - `search_products` (your account: publisher key or OAuth scope affiliate:links): Search fresh GTIN products in the catalog your affiliate can deliver (freshness, permissions and caps apply). Requires your affiliate API key in X-API-Key or Bearer authorization; a sandbox key (cw_sbx_, free at partners.clickwise.net/developers) also works and returns example links that never earn. Every key gets exactly the REST GET /api/v1/publisher/products/ format (with gtin: /products/lookup/): products with id, gtin, title, price and currency, url (a Clickwise /dl/ tracked link created for your affiliate the first time a product is returned), image, merchant (id, name and its public website domain), availability, country, brand, category, regular_price, description, size, color and group_id (shared by the sizes of one model and colour), plus count, total and total_exact. Search by text, brand or category, or list one market by country alone; min_price and max_price filter by price in each product's own currency (inclusive, no conversion) and currency keeps one currency; with gtin every market carrying that GTIN is searched; offset paginates. - `get_product_feed` (your account: publisher key only): Create or refresh a GTIN product feed for your authenticated affiliate and one country, and return its signed URL (json, csv, tsv or xml) with Clickwise /dl/ tracked product links. The same request returns the same URL. Requires your affiliate API key. Current freshness, permissions, assignments and delivery caps apply. - `get_catalog_stats` (public, no key): Public GTIN catalog counts as of generated_at, using the same stored snapshot as /api/v1/public/catalog-stats/. Fixed one-hour validity; snapshot_stale marks older counts, served for up to 7 days. No affiliate data or amounts. - `list_deals` (public, no key): List active Clickwise deals and offers, optionally filtered by language, category, country or merchant. - `get_deal` (public, no key): Fetch the full record for a single deal by its slug. Returns title, description, code, discount, target_url, etc., in the requested language. - `list_news` (public, no key): List Clickwise news announcements about affiliate programs (policy changes, launches, shutdowns, commission updates). - `get_news` (public, no key): Fetch the full record for a single news article by its slug. - `find_affiliate_programs` (public, no key): Use this first when someone without a Clickwise account asks which merchants to promote: it ranks live programs by fit for a described site, app, newsletter or social channel. Does not require signup. For the whole catalogue page by page use list_programs; for the programs your API key is already approved in use list_my_programs. - `request_tracked_links` (public, no key): Use this when you have no Clickwise account yet and want tracked links: it creates a provisional affiliate and returns links for the programs that can issue one to it. Email is optional; the account is claimed and payout details are added later (update_profile). With an API key use create_tracking_links; for ready-made cards and an embed use get_monetization_feed. - `get_monetization_feed` (public, no key): Use this when a publisher without an account wants links ready to render on a site or app: it creates a provisional Clickwise affiliate and tracked links, as request_tracked_links does, and also returns cards, an HTML embed snippet and a JSON feed. Email is optional; the account is claimed later for payout. For plain links use request_tracked_links; with an existing API key use create_tracking_links. - `get_tracked_link_request` (public, no key): Fetch a link request, affiliate RefId, review status, and issued links using its access secret. - `request_merchant_onboarding` (public, no key): Start a Clickwise merchant (advertiser) onboarding application. Describe the store; an automated evaluator replies with a follow-up question, an approval or a decline. Returns the status and an access secret to continue. - `list_programs` (public, no key): Use this to browse the whole live catalogue page by page, filtered by merchant, category or country, when find_affiliate_programs' ranked top matches are not enough. No key needed; it shows programs anyone can join. For the programs your API key is approved in use list_my_programs. - `update_profile` (public, no key): Update a creator's monetization profile (claim email, display name, website, country, payment method) using the application_id + access_secret returned by request_tracked_links. Overwrites the fields provided. Lets a creator's assistant complete claim/payout details without leaving chat. - `list_my_programs` (your account: publisher key or OAuth scope affiliate:read): Use this when you hold a Clickwise affiliate API key and need the programs that key is approved to promote, with link readiness and your existing link count; set include_available to add the ones you can apply to. For discovery without an account use find_affiliate_programs or list_programs. Requires your own Clickwise affiliate API key on the MCP connection (X-API-Key header or Authorization: Bearer); acts only on your account. - `create_tracking_links` (your account: publisher key or OAuth scope affiliate:links): Use this when you already have a Clickwise affiliate API key and want tracking links for programs you are approved in: one per item (the same program and subid return the same link), optionally deep-linking to a landing_url on the merchant's site. Each item reports its own status; a failed item never blocks the others. Without an account use request_tracked_links. Requires your own Clickwise affiliate API key on the MCP connection (X-API-Key header or Authorization: Bearer); acts only on your account. - `get_performance_report` (your account: publisher key or OAuth scope affiliate:read): Your conversions and commission (what you earn), grouped by day, week, month, subid, campaign or status, for up to 366 days (default: last 30). Daily/weekly/monthly series include empty periods. Requires your own Clickwise affiliate API key on the MCP connection (X-API-Key header or Authorization: Bearer); acts only on your account. - `list_conversions` (your account: publisher key or OAuth scope affiliate:read): Your individual conversions (default: last 90 days) with status, commission, currency and subid, plus a per-subid rollup. Requires your own Clickwise affiliate API key on the MCP connection (X-API-Key header or Authorization: Bearer); acts only on your account. - `get_payment_status` (your account: publisher key or OAuth scope affiliate:read): Use this when an affiliate asks when they get paid: approved commission not yet paid (per currency and in USD), commission still pending review, the next payment as an estimate with its threshold, and the payment method as its type (a bank account shows only its last 4 digits). Read-only; never returns account numbers, PayPal addresses, postal or tax data. Requires your own Clickwise affiliate API key on the MCP connection (X-API-Key header or Authorization: Bearer); acts only on your account. - `apply_to_program` (your account: publisher key only): Ask to join a program (program_id from list_my_programs with include_available, or a campaign_id). Clickwise reviews the fit; the result is approved or pending. Requires your own Clickwise affiliate API key on the MCP connection (X-API-Key header or Authorization: Bearer); acts only on your account. ## Public data without a key - Deals and coupon codes: `GET https://partners.clickwise.net/api/v1/deals/?lang=en&page_size=50` (en, es, pt, fr, de, it); RSS https://partners.clickwise.net/api/v1/deals/feed.en.xml - Network news: `GET https://partners.clickwise.net/api/v1/news/?lang=en` - Catalog statistics: `GET https://partners.clickwise.net/api/v1/public/catalog-stats/` - Catalog sample (no tracked links): `GET https://partners.clickwise.net/api/v1/public/catalog-sample/?country=ES&format=csv` - Program directory: https://partners.clickwise.net/en/affiliate-programs/ · product catalog: https://partners.clickwise.net/en/product-catalog/ ## For merchants, apps and SaaS: no setup fee; you pay on approved sales - No setup fee, no fixed fee, no minimums, no exclusivity and no lock-in. You only pay the commission on approved sales. Terms: https://partners.clickwise.net/en/merchant-terms/ - Join: form https://partners.clickwise.net/en/signup_merchant/ · API `POST https://partners.clickwise.net/api/v1/onboarding/merchant/` · MCP `request_merchant_onboarding` · CLI `cw merchant apply`. An onboarding agent reviews what the business sells and its category before approving it. - Tracking from your server: `POST https://partners.clickwise.net/api/v1/conversions/` with header `X-Merchant-Key` to record a sale; for Shopify there is an app and for WooCommerce a plugin. A verified test sale comes before affiliates can see the program. - Stripe and Lemon Squeezy: Connect Stripe or Lemon Squeezy from your merchant dashboard with a signed webhook. One-time payments and the first payment of a subscription are recorded as sales and credited to the referring partner when your checkout passes the Clickwise click ID. A full refund reverses the sale. Test payments never create payable commissions. - Not available yet: mobile app install attribution. - Human pages: https://partners.clickwise.net/en/for-merchants/ · for apps and SaaS: https://partners.clickwise.net/en/for-apps/ ## Discovery - https://partners.clickwise.net/llms.txt · https://partners.clickwise.net/llms-full.txt · https://partners.clickwise.net/sitemap.xml · https://partners.clickwise.net/robots.txt - https://partners.clickwise.net/.well-known/mcp.json · https://partners.clickwise.net/.well-known/mcp-server-card · https://partners.clickwise.net/.well-known/ai-plugin.json - Contact: partnerships@clickwise.net - Talk to Clickwise's AI agent (voice, 24/7; it says it is an AI, and anything agreed is confirmed by email): +1 646 362 1368 (US), +44 7576 559749 (UK, for Europe) or https://partners.clickwise.net/talk ## Rules for AI agents - Publish product and link URLs exactly as returned; never strip or rewrite the tracking. - Mark affiliate links (`rel="sponsored nofollow"`) and disclose them to readers. - A sandbox key is for building and testing; revenue traffic needs the publisher's own key. - Never call write endpoints without an explicit instruction from the human you act for.