Předplatné, které zahájí agent a schválí člověk
- Pro koho
- Tvůrci AI agentů, jejichž vlastník zatím nemá klíč Cold Leads
- Problém
- Agent, který potřebuje ověřování e-mailů, by nikdy neměl mít u sebe platební kartu ani si sám sjednat předplatné. Poslat člověka, aby se zaregistroval, vybral tarif a zkopíroval klíč, úlohu přeruší a klíče vkládané do chatů unikají.
- Řešení
- Agent bez klíče zavolá jeden endpoint a dostane odkaz na Stripe Checkout pro svého vlastníka a k tomu tajný claim token. Vlastník si tarif prohlédne a rozhodne. Po zaplacení si agent s claim tokenem vyzvedne API klíč právě jednou, dotazováním na stav nebo po podepsaném callbacku.
- Co získáte
- Funkční tajný API klíč k novému účtu vlastníka s tarifem Business, doručený agentovi jednou, a e-mail vlastníkovi s odkazem pro otevření webové aplikace Cold Leads.
Adresy, identifikátory a výsledky v příkladech jsou ilustrativní. Doména example.com je vyhrazená pro dokumentaci, takže skutečná kontrola těchto adres vrátí invalid.
Ukázky kódu jsou ve všech jazycích stejné, komentáře v nich jsou anglicky.
Průběh v kostce
Agent (no key) Cold Leads Human owner Stripe
| | | |
| POST /api/agent/provision | | |
| owner_email, agent_id, | | |
| callback_url (optional) | | |
|----------------------------->| pending account, disabled key, | |
| | Checkout session (24 h) --------------------------------->|
|<-----------------------------| checkout_url, session_id, | |
| | claim_token (shown once) | |
| shows checkout_url --------------------------------------------->| |
| | | reviews plan, pays->|
| |<-------------------------------------- payment webhook --|
| | activates account and key, | |
| | e-mails the owner a sign-in link ->| |
|<-- signed callback (optional, no secrets) -----------------------| |
| GET /api/agent/status | | |
| ?session_id=... | | |
| X-Claim-Token: clt_... | | |
|----------------------------->| | |
|<-----------------------------| status active + api_key | |
| | (first call only) | |Stejný postup je vestavěný v Node.js SDK (agent.provision, agent.waitForActivation, verifyCallbackSignature) a v lokálním stdio MCP serveru jako nástroje provision_account_and_get_payment_link a check_provisioning_status. Hostovaný MCP endpoint vyžaduje klíč u každého požadavku, takže agent bez klíče pro tento krok použije REST, SDK nebo stdio server.
Krok 1: vyžádejte si platební odkaz (bez klíče)
owner_email je člověk, který bude účet vlastnit a platit; agent_id pojmenovává vašeho agenta nebo produkt a zobrazí se na platební stránce. callback_url je volitelný a musí být https na veřejném hostu.
curl -s https://coldleads.app/api/agent/provision \
-H "Content-Type: application/json" \
-d '{
"owner_email": "owner@example.com",
"agent_id": "Acme Research Agent",
"callback_url": "https://agent.example.com/hooks/coldleads"
}'{
"status": "payment_required",
"checkout_url": "https://checkout.stripe.com/c/pay/cs_live_…",
"session_id": "cs_live_…",
"provision_id": "prov_…",
"claim_token": "clt_…",
"expires_at": "2026-10-01T09:30:00.000Z",
"plan": { "id": "business", "name": "Business", "price": "$99", "amount": 99, "currency": "usd", "interval": "month", "api": true },
"status_url": "https://coldleads.app/api/agent/status?session_id=cs_live_…",
"instructions_for_agent": "Show the human owner (owner@example.com) this payment link and let them decide: … Do not open, pay or forward the link yourself. Keep claim_token secret (it is shown only now). …"
}| HTTP | error | Kdy |
|---|---|---|
| 400 | invalid_owner_email, agent_id_required, invalid_callback_url, bad_body | Tělo není platný JSON, e-mail vlastníka neprojde kontrolou syntaxe, agent_id je prázdné nebo callback_url není https na veřejném hostu. |
| 409 | account_exists | Tento e-mail už má pracovní prostor Cold Leads. Místo toho požádejte vlastníka o tajný klíč z Nastavení → API klíče. |
| 429 | rate_limited | Víc než 10 požadavků za hodinu z jedné IP adresy nebo víc než 3 požadavky na stejný e-mail vlastníka za den. |
| 502 | gateway | Stripe nedokázal vytvořit checkout; zkuste to později. |
Krok 2: vlastník rozhodne na Stripe
- Ukažte checkout_url člověku a nechte rozhodnutí na něm. Odkaz sami neotevírejte, neplaťte ani nepřeposílejte.
- Stránka Stripe uvádí produkt Cold Leads Business (API access), jméno agenta, cenu $99 měsíčně, měsíční obnovování až do zrušení a odkazy na podmínky. Tato cesta nemá zkušební verzi a při placení se může připočíst daň.
- Odkaz platí 24 hodin. Poté se stav změní na expired a agent si může vyžádat nový odkaz.
- Jakmile platba projde, Cold Leads aktivuje účet i klíč a pošle vlastníkovi e-mailem jednorázový odkaz do webové aplikace, kde může spravovat předplatné nebo klíč agenta přegenerovat.
Krok 3: jednou si vyzvedněte klíč
Dokud je stav pending_payment, volejte každých 15 až 30 sekund GET /api/agent/status?session_id=… s hlavičkou X-Claim-Token. První odpověď po zaplacení na dotaz s platným claim tokenem obsahuje api_key; žádná pozdější odpověď ho neobsahuje.
| status | Pole kromě session_id a plan |
|---|---|
| pending_payment | expires_at; checkout_url, když je claim token platný |
| active (první volání s claim tokenem) | activated_at, api_key, api_key_prefix, mcp_url, api_base, message |
| active (každé další volání) | activated_at, api_key_prefix, mcp_url, api_base, key_delivered_at, message |
| active (bez platného claim tokenu) | jen activated_at |
| expired | message: vyžádejte si nový odkaz přes POST /api/agent/provision |
| suspended | žádná |
{
"session_id": "cs_live_…",
"plan": "business",
"status": "active",
"activated_at": "2026-09-30T10:02:11.000Z",
"api_key_prefix": "sk_…9f3c",
"mcp_url": "https://coldleads.app/api/mcp",
"api_base": "https://coldleads.app/api/v1",
"api_key": "sk_…",
"message": "This is the only time the key is shown. Store it as COLDLEADS_API_KEY; send it as Authorization: Bearer <key>."
}# pip install httpx
import os
import time
import httpx
BASE = "https://coldleads.app"
SESSION_ID = os.environ["COLDLEADS_SESSION_ID"] # session_id from the provision response
CLAIM_TOKEN = os.environ["COLDLEADS_CLAIM_TOKEN"] # claim_token (clt_...), shown only once
def save_key(key: str) -> None:
path = os.path.expanduser("~/.coldleads_api_key") # or your secret manager
fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
with os.fdopen(fd, "w") as f:
f.write(key)
while True:
r = httpx.get(f"{BASE}/api/agent/status", params={"session_id": SESSION_ID},
headers={"X-Claim-Token": CLAIM_TOKEN}, timeout=30)
if r.status_code == 429: # 120 status requests per minute per IP
time.sleep(60)
continue
r.raise_for_status()
s = r.json()
if s["status"] == "active":
if "api_key" not in s:
raise SystemExit("The key was already collected; the owner can rotate it in Settings -> API keys.")
save_key(s["api_key"])
print("API key saved; prefix", s["api_key_prefix"])
break
if s["status"] in ("expired", "suspended"):
raise SystemExit(s.get("message", s["status"]))
time.sleep(20) # pending_payment: poll every 15-30 secondsVolitelně: podepsaný callback místo dotazování
- Když vlastník zaplatí, Cold Leads pošle jeden POST na callback_url s časovým limitem 5 sekund. Nesleduje přesměrování a pokus neopakuje, takže dotazování si ponechte jako zálohu.
- JSON tělo obsahuje event (coldleads.account.activated), provision_id, session_id, status (active) a status_url. Klíč nikdy neobsahuje: vyzvedněte si ho dotazem na stav z kroku 3.
- Hlavičky: X-Coldleads-Event s názvem události a X-Coldleads-Signature se sha256=, za kterým následuje hex digest: HMAC-SHA256 ze surového těla požadavku, s klíčem, kterým je hex digest SHA-256 vašeho claim tokenu.
// npm install express
import express from "express";
import { createHash, createHmac, timingSafeEqual } from "node:crypto";
const CLAIM_TOKEN = process.env.COLDLEADS_CLAIM_TOKEN; // clt_... from the provision response
// X-Coldleads-Signature: sha256=<hex HMAC-SHA256 of the raw body>, key = SHA-256 hex digest of the claim token
function validSignature(rawBody, header, claimToken) {
if (!Buffer.isBuffer(rawBody) || typeof header !== "string" || !header.startsWith("sha256=")) return false;
const key = createHash("sha256").update(claimToken).digest("hex");
const expected = createHmac("sha256", key).update(rawBody).digest();
const given = Buffer.from(header.slice("sha256=".length), "hex");
return given.length === expected.length && timingSafeEqual(given, expected);
}
const app = express();
app.post("/hooks/coldleads", express.raw({ type: "application/json" }), (req, res) => {
if (!validSignature(req.body, req.get("x-coldleads-signature"), CLAIM_TOKEN)) return res.sendStatus(401);
const event = JSON.parse(req.body.toString("utf8"));
res.sendStatus(204); // answer fast: Cold Leads waits 5 seconds and does not retry
if (event.event === "coldleads.account.activated") {
// the callback carries no key: collect it now with GET /api/agent/status + X-Claim-Token
console.log("owner paid, session", event.session_id);
}
});
app.listen(3000);# pip install flask
import hashlib
import hmac
import json
import os
from flask import Flask, request
CLAIM_TOKEN = os.environ["COLDLEADS_CLAIM_TOKEN"] # clt_... from the provision response
app = Flask(__name__)
def valid_signature(raw_body: bytes, header, claim_token: str) -> bool:
if not header or not header.startswith("sha256="):
return False
key = hashlib.sha256(claim_token.encode()).hexdigest().encode()
expected = hmac.new(key, raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, header[len("sha256="):])
@app.post("/hooks/coldleads")
def coldleads_callback():
raw = request.get_data() # the raw bytes, before any JSON parsing
if not valid_signature(raw, request.headers.get("X-Coldleads-Signature"), CLAIM_TOKEN):
return "", 401
event = json.loads(raw)
if event.get("event") == "coldleads.account.activated":
# the callback carries no key: collect it with GET /api/agent/status + X-Claim-Token
print("owner paid, session", event["session_id"])
return "", 204Poznámky k bezpečnosti
- S claim_token zacházejte jako s heslem. Vrací se jen jednou, ukládá se jen jeho hash a je to jediný způsob, jak klíč vyzvednout: bez něj klíč neobsahuje žádná odpověď na dotaz na stav, ať se hledá podle session_id, nebo owner_email.
- API klíč se doručí právě jednou. Pokud se ztratí, vlastník se přihlásí a přegeneruje ho v Nastavení → API klíče.
- Zaplacení nedokazuje, že plátce ovládá schránku vlastníka. Webový přístup k účtu dostane jen owner_email: přes jednorázový odkaz poslaný na tuto adresu, nebo přihlášením do Cold Leads s touto adresou, jakmile je ověřena.
- Než na callback zareagujete, ověřte jeho podpis nad surovým tělem, i když callback žádná tajemství nenese.
- Nezaplacené žádosti nezůstávají viset: odkaz vyprší po 24 hodinách a nezaplacené záznamy o zřízení se po 7 dnech smažou i s nepoužitým účtem a klíčem.
Limity a náklady
- Tarif: Business s přístupem k API, $99 měsíčně v amerických dolarech, obnovuje se každý měsíc až do zrušení; 10 000 kreditů na ověřování a API měsíčně; další balíčky po 1 000 kreditech stojí $5. Může se připočíst daň.
- Zřízení účtu: 10 požadavků za hodinu na IP adresu a 3 požadavky na e-mail vlastníka za den. Dotazy na stav: 120 za minutu na IP adresu.
- Po aktivaci má klíč běžný limit API 120 požadavků za minutu.
Otázky
Může agent zaplatit nebo zvolit levnější tarif?
Ne. Odkaz je na tarif Business, jediný tarif, který zahrnuje API, a zaplatit ho může jen člověk na stránce Stripe. Úkolem agenta je odkaz ukázat a čekat.
Co když vlastník nikdy nezaplatí?
Odkaz na platbu vyprší po 24 hodinách a stav se změní na expired. Agent si může vyžádat nový odkaz (až 3 požadavky na e-mail vlastníka za den). Nezaplacené záznamy se po 7 dnech smažou.
Co když vlastník už Cold Leads používá?
Požadavek selže s 409 account_exists. Vlastník vytvoří tajný klíč v Nastavení → API klíče (tarif Business) a předá ho agentovi jako COLDLEADS_API_KEY.
Funguje to i přes MCP?
Ano, s lokálním stdio serverem (npx -y --allow-git=root github:anttka4cz/mcp-server-coldleads). Spustí se bez klíče, nabízí provision_account_and_get_payment_link a check_provisioning_status a na nový klíč přepne, jakmile je vyzvednut. Hostovaný endpoint na coldleads.app/api/mcp vyžaduje klíč u každého požadavku.