Adjoin
Veřejné API · v1 · MCP

Adjoin API dokumentace

REST API pro kampaně, landing stránky, statistiky, členy, platby a vlastní eventy — agent-friendly, autentizace API klíčem. Dostupné na plánu Business. Totéž lze ovládat z ChatGPT, Claude nebo jiného AI asistenta přes MCP server, který funguje na každém plánu.

Autentizace

Každý požadavek nese hlavičku Authorization: Bearer <klíč>. Klíč vygenerujete v adminu (Nastavení → API klíče) — formát adj_ + 32 hex znaků. Klíč se zobrazí jen jednou, při vytvoření; server ukládá pouze jeho SHA-256 otisk.

curl https://meta-pixel.cz/api/v1/campaigns \
  -H "Authorization: Bearer adj_<váš_klíč>"

Klíč patří konkrétnímu workspace — API vidí a mění jen data toho workspace, ke kterému klíč patří (stejná izolace jako v adminu). Žádný endpoint nepřijímá workspace_id v těle ani v URL; workspace se vždy odvozuje z klíče.

Základní adresa je https://meta-pixel.cz/api/v1. Těla požadavků i odpovědí jsou JSON (content-type: application/json), s výjimkou GET /landings/contract, který vrací prostý text.

Limity a chyby

Rate limit: 120 požadavků/min na klíč. Po překročení vrací API 429. Zápisy mají navíc vlastní strop 10 zápisů/min na workspace, společný pro REST API i MCP — podrobnosti v sekci Idempotence a zápisy. Chybové odpovědi mají vždy tvar:

{ "error": "rate_limited", "detail": "Limit 120 požadavků za minutu na klíč byl překročen." }
StaverrorVýznam
401unauthorizedchybějící, neplatný nebo zrušený klíč
403workspace_suspendedworkspace je pozastaven
404not_foundzdroj neexistuje nebo patří jinému workspace
429rate_limitedlimit 120/min překročen
400invalid_bodychybějící nebo neplatné pole v těle požadavku

Úplný slovník chyb včetně stavů, které vracejí jen zápisy (plan_limit, slug_taken, in_use, not_ready, write_limited), je v následující sekci.

Idempotence a zápisy

Zápisové endpointy (POST, PUT, DELETE u kampaní a landing stránek) sdílejí několik pravidel. Platí pro REST API i pro nástroje MCP serveru, které vnitřně volají stejnou servisní vrstvu.

Hlavička Idempotency-Key

U vytvářecích požadavků — POST /campaigns, POST /landings a POST /landings/from-template — je hlavička Idempotency-Key povinná. Bez ní server vrátí 400 invalid_body a nic nezaloží. Hodnota je libovolný řetězec do 128 bajtů (měřeno v UTF-8, ne ve znacích — diakritika a emoji zabírají víc; typicky UUID), unikátní pro každý zamýšlený zápis. Delší klíč vrátí 400 invalid_body.

  • Stejný klíč podruhé (do 24 hodin) vrátí uložený úspěšný výsledek — stejný stav i tělo — a akce se znovu neprovede. Přehrávku poznáte podle hlavičky Idempotency-Replayed: true.
  • Chybová odpověď se neukládá — ani 400, 404, 409 a 422, ani zamítnutí kvůli plánu nebo limitu (403, 429). Po opravě těla lze poslat stejný klíč znovu. Uložený a přehrávaný je jen úspěšný výsledek (2xx).
  • Stejný klíč s jiným tělem vrátí 409 idempotency_key_reused a uložený výsledek se nevrací — nedostanete cizí entitu pod svým klíčem. Pro nový požadavek použijte nový klíč.
  • Klíč je vázaný na workspace a operaci: stejný řetězec u POST /landings a u POST /campaigns jsou dva různé záznamy.

Úpravy, mazání a zapínání (PUT, DELETE, /activate, /deactivate) hlavičku nevyžadují — jsou přirozeně idempotentní.

Pořadí kontrol u zápisu: plán → přítomnost hlavičky Idempotency-Key → hledání uloženého výsledku (přehrání) → limit zápisů → validace těla → hledání záznamu. Přehrání tedy projde i s vyčerpaným limitem a limit nespotřebuje. Jedinou výjimkou je vypnutí kampaně, které žádnou z těchto kontrol nemá.

curl -X POST https://meta-pixel.cz/api/v1/landings/from-template \
  -H "Authorization: Bearer adj_<klíč>" \
  -H "Idempotency-Key: 6f1c2a9e-landing-signaly-leden" \
  -H "content-type: application/json" \
  -d '{"template":"signals","name":"Signály — leden"}'
HTTP/1.1 201 Created
content-type: application/json

{ "id": 9, "workspace_id": 4, "name": "Signály — leden", "kind": "custom", "…": "…" }

# stejný požadavek podruhé:
HTTP/1.1 201 Created
Idempotency-Replayed: true

Limit zápisů

Na workspace je povoleno 10 zápisů za minutu (klouzavé okno po minutách), společně pro REST API a MCP. Po vyčerpání vrací každý další zápis 429 write_limited; čtení běží dál. Limit se počítá nad rámec 120 požadavků/min na klíč. Vypnutí kampaně (/deactivate, v MCP set_campaign_active s active: false) se do limitu nepočítá a projde i s vyčerpaným limitem. Nepočítá se ani přehrání uloženého idempotentního výsledku (stejný Idempotency-Key podruhé) — opakovaný požadavek limit nespotřebovává.

{ "error": "write_limited", "detail": "Limit 10 zápisů za minutu na workspace. Zkuste to za 60 s." }

Plán Business a přechod na nižší plán

API klíč lze vytvořit jen na plánu Business. Pokud workspace později přejde na nižší plán, klíč dál funguje pro čtení (zpětná kompatibilita), ale každý zápis vrátí 403 plan_limit:

{ "error": "plan_limit", "detail": "Veřejné API nejsou dostupné v plánu Zdarma. Přejděte na vyšší plán." }

Kontrola plánu běží před vším ostatním a limit zápisů hned po ní (až po případném přehrání uloženého výsledku) — obojí dřív než validace těla nebo hledání záznamu. Zápis na neexistující slug proto na nižším plánu vrátí 403, ne 404. Výjimka: vypnutí kampaně kontrolu plánu nemá — i po přechodu na nižší plán musí jít kampaň vypnout.

Kampaň z API vzniká vypnutá

Kampaň založená přes POST /campaigns je vždy neaktivní (active: 0); pole active v těle se ignoruje, stejně jako u PUT /campaigns/:slug. Vypnutá kampaň nesbírá kliky a nezapočítává se do limitu aktivních kampaní. Jediná cesta k zapnutí je POST /campaigns/:slug/activate, který před zapnutím ověří připravenost:

  • 422 not_ready — kampaň s cílem Telegram nemá připojený chat; návštěvníci by skončili bez pozvánky.
  • 403 plan_limit — workspace je na limitu aktivních kampaní svého plánu.

Vypnutí (/deactivate) projde vždy — je to bezpečný směr bez kontroly připravenosti, plánu i limitu zápisů.

Audit

Každý zápis přes API se zapíše do protokolu workspace (v adminu Protokol) se zdrojem via: "api" a aktérem key:<id klíče>. Zápisy z MCP mají via: "mcp" (API klíč) nebo via: "mcp_oauth" (OAuth souhlas). Z protokolu tedy vždy poznáte, co udělal člověk v adminu, co skript a co AI asistent.

Slovník chyb

StaverrorKdy
400invalid_bodychybějící nebo neplatné pole, chybějící Idempotency-Key, HTML landingu porušující kontrakt, odkaz na cizí pixel/landing/chat/doménu (detail říká které)
401unauthorizedchybějící, neplatný nebo zrušený klíč
403plan_limitzápis na plánu bez veřejného API, nebo zapnutí kampaně nad limit aktivních kampaní
403workspace_suspendedworkspace je pozastaven
404not_foundzdroj neexistuje, patří jinému workspace, nebo id v cestě není kladné celé číslo
409slug_takenslug už používá jiná kampaň (sluggy jsou globálně unikátní kvůli veřejné URL /l/:slug)
409in_uselanding používá kampaň — přepis pod běžící kampaní vyžaduje allow_active: true; smazání blokuje jakákoli kampaň, běžící i vypnutá
409idempotency_key_reusedstejný Idempotency-Key už byl použit s jiným tělem požadavku; uložený výsledek se nevrací
422not_readykampaň nelze zapnout — Telegram cíl bez připojeného chatu
429rate_limited120 požadavků/min na klíč překročeno
429write_limited10 zápisů/min na workspace překročeno (REST + MCP dohromady)

GET /api/v1/campaigns

Seznam kampaní workspace se souhrnnou statistikou (návštěvy, kliky, vstupy, platby, revenue, CR).

curl https://meta-pixel.cz/api/v1/campaigns \
  -H "Authorization: Bearer adj_<klíč>"
{
  "items": [
    { "campaign_id": 12, "slug": "black-friday", "name": "Black Friday", "active": 1,
      "currency": "CZK", "visits": 4210, "clicks": 3190, "requests": 980,
      "joins": 812, "leaves": 40, "payments": 210, "revenue": 158900, "cr": 25.5 }
  ]
}

GET /api/v1/campaigns/:slug

Plný detail jedné kampaně — nastavení tak, jak je uložené (bez statistik; ty vrací /stats). Stejný tvar vrací i POST a PUT.

curl https://meta-pixel.cz/api/v1/campaigns/black-friday \
  -H "Authorization: Bearer adj_<klíč>"
{
  "id": 12, "workspace_id": 4, "slug": "black-friday", "name": "Black Friday",
  "pixel_id": 3, "chat_id": 5, "landing_id": 7, "domain_id": 1,
  "dest_type": "telegram", "dest_url": null,
  "approve_mode": "auto_tracked", "link_mode": "per_click",
  "event_click": "Lead", "event_join": "Subscribe",
  "welcome_msg": null, "currency": "CZK", "active": 1, "antibot": 1,
  "meta_campaign_id": null, "channel_id": null, "project_id": null,
  "created_at": 1761955200
}
PoleTyp
dest_typestringtelegram · whatsapp · messenger · instagram · url
approve_modestringauto_all · auto_tracked · manual · confirm_button
link_modestringper_click · static
active, antibot0 | 1číselné příznaky, ne boolean
pixel_id, landing_id, chat_id, domain_idnumber | nullinterní id z referencí, ne Meta pixel ID
created_atunix čas (s)

Chyby: 404 not_found — slug neexistuje nebo patří jinému workspace.

POST /api/v1/campaigns

Založí novou kampaň z pixelu, landing stránky a cíle. Kampaň vzniká vždy vypnutá — zapnete ji samostatně přes /activate. Vyžaduje hlavičku Idempotency-Key. Identifikátory (pixel_id, landing_id, chat_id, domain_id) berte z referenčních endpointů; musí patřit vašemu workspace.

PoleTyp
namestringpovinné
pixel_idnumberpovinné — id z GET /pixels
landing_idnumberpovinné — id z GET /landings
dest_typestringpovinné — telegram · whatsapp · messenger · instagram · url
approve_modestringvolitelné — auto_all · auto_tracked · manual · confirm_button; výchozí auto_tracked (automaticky pustí jen lidi z reklamy, ostatní žádosti dostane admin)
link_modestringvolitelné — per_click · static; výchozí podle cíle: per_click u telegram (pozvánka na každý klik, nutná pro atribuci vstupů), static u ostatních
slugstringvolitelné, [a-z0-9-]{2,40}; bez něj se odvodí z názvu (bez diakritiky, při kolizi -2, -3…)
dest_urlstring | nullpovinné u url, whatsapp (chat.whatsapp.com/… nebo wa.me/…), messenger (m.me/…) a instagram (ig.me/…, instagram.com/…)
chat_idnumber | nullid z GET /chats; u cíle Telegram nutné před zapnutím
domain_idnumber | nullid z GET /domains; bez něj výchozí doména
event_click, event_joinstringnázvy Meta událostí; výchozí Lead a Subscribe
welcome_msgstring | nulluvítací zpráva bota
currencystringvýchozí CZK
antibotbooleanvýchozí true
channel_id, project_idnumber | nullvolitelné vazby na registrovaný kanál a projekt
activeignoruje se; kampaň vzniká vypnutá
curl -X POST https://meta-pixel.cz/api/v1/campaigns \
  -H "Authorization: Bearer adj_<klíč>" \
  -H "Idempotency-Key: 2c9d7b41-kampan-signaly-leden" \
  -H "content-type: application/json" \
  -d '{"name":"Signály leden","pixel_id":3,"landing_id":9,"chat_id":5,
       "dest_type":"telegram"}'
HTTP/1.1 201 Created

{
  "id": 14, "workspace_id": 4, "slug": "signaly-leden", "name": "Signály leden",
  "pixel_id": 3, "chat_id": 5, "landing_id": 9, "domain_id": null,
  "dest_type": "telegram", "dest_url": null,
  "approve_mode": "auto_tracked", "link_mode": "per_click",
  "event_click": "Lead", "event_join": "Subscribe",
  "welcome_msg": null, "currency": "CZK", "active": 0, "antibot": 1,
  "meta_campaign_id": null, "channel_id": null, "project_id": null,
  "created_at": 1767225600
}

Chyby: 400 invalid_body (chybějící Idempotency-Key, neplatné pole — např. "detail": "pixel_id does not exist"), 409 slug_taken u zadaného slugu, který už existuje, 409 idempotency_key_reused, 403 plan_limit, 429 write_limited.

PUT /api/v1/campaigns/:slug

Částečná úprava kampaně — pošlete jen pole, která měníte; ostatní zůstávají. Přijímá stejná pole jako POST /campaigns kromě slug (veřejná URL v běžících reklamách) a active (zapínání má vlastní endpoint); obě se v těle tiše ignorují. Výsledek po sloučení musí projít stejnou validací jako při založení.

curl -X PUT https://meta-pixel.cz/api/v1/campaigns/signaly-leden \
  -H "Authorization: Bearer adj_<klíč>" \
  -H "content-type: application/json" \
  -d '{"name":"Signály leden — VIP","landing_id":11,"welcome_msg":"Vítejte v klubu!"}'
{
  "id": 14, "workspace_id": 4, "slug": "signaly-leden", "name": "Signály leden — VIP",
  "pixel_id": 3, "chat_id": 5, "landing_id": 11, "domain_id": null,
  "dest_type": "telegram", "dest_url": null,
  "approve_mode": "auto_tracked", "link_mode": "per_click",
  "event_click": "Lead", "event_join": "Subscribe",
  "welcome_msg": "Vítejte v klubu!", "currency": "CZK", "active": 0, "antibot": 1,
  "meta_campaign_id": null, "channel_id": null, "project_id": null,
  "created_at": 1767225600
}

Chyby: 404 not_found, 400 invalid_body (např. landing_id does not exist — landing patří jinému workspace), 403 plan_limit, 429 write_limited.

POST /api/v1/campaigns/:slug/activate

Zapne kampaň — začne sbírat kliky a započítá se do limitu aktivních kampaní plánu. Před zapnutím server ověří, že kampaň má šanci fungovat. Bez těla, bez Idempotency-Key.

curl -X POST https://meta-pixel.cz/api/v1/campaigns/signaly-leden/activate \
  -H "Authorization: Bearer adj_<klíč>"
{ "slug": "signaly-leden", "active": true }

Chyby specifické pro tento endpoint:

HTTP/1.1 422 Unprocessable Entity
{ "error": "not_ready",
  "detail": "Kampaň nemá připojenou skupinu, zapnutím by návštěvníci skončili bez pozvánky. Nejdřív přiřaďte chat." }

HTTP/1.1 403 Forbidden
{ "error": "plan_limit",
  "detail": "Dosáhli jste limitu plánu Zdarma (1 aktivních kampaní). Přejděte na vyšší plán pro navýšení limitu." }

Dále 404 not_found a 429 write_limited. Zapnutí už zapnuté kampaně projde a vrátí totéž.

POST /api/v1/campaigns/:slug/deactivate

Vypne kampaň. Je to bezpečný směr — projde vždy: bez kontroly připravenosti, bez kontroly plánu a bez limitu zápisů. Funguje i po přechodu na nižší plán nebo s vyčerpaným limitem 10 zápisů/min, protože zastavení kampaně zastavuje útratu za reklamu. Jediná chyba je 404 not_found na neznámý nebo cizí slug. Vypnutá kampaň přestane sbírat kliky; její historie zůstává.

curl -X POST https://meta-pixel.cz/api/v1/campaigns/signaly-leden/deactivate \
  -H "Authorization: Bearer adj_<klíč>"
{ "slug": "signaly-leden", "active": false }

Mazání kampaní přes API není možné — nevratně by odstranilo historii kliknutí a atribuci. Nepotřebnou kampaň vypněte.

GET /api/v1/campaigns/:slug/stats

Trychtýř (funnel) a 30denní časová řada pro jednu kampaň.

curl https://meta-pixel.cz/api/v1/campaigns/black-friday/stats \
  -H "Authorization: Bearer adj_<klíč>"
{
  "funnel": { "visits": 4210, "clicks": 3190, "requests": 980, "joins": 812,
              "left": 40, "payments_count": 210, "revenue": 158900, "suspected_bot": 12 },
  "timeseries": [ { "date": "2026-07-01", "visits": 140, "clicks": 98, "joins": 24, "revenue": 4200 }, "…" ]
}

GET /api/v1/landings

Seznam landing stránek workspace — jen identifikace a čas poslední změny, bez HTML (seznam má být levný). Obsah vrací GET /landings/:id. Řazeno od nejnovější.

curl https://meta-pixel.cz/api/v1/landings \
  -H "Authorization: Bearer adj_<klíč>"
{
  "items": [
    { "id": 9, "name": "Signály — leden", "kind": "custom", "updated_at": 1767312000 },
    { "id": 7, "name": "Black Friday landing", "kind": "tme", "updated_at": 1761955200 }
  ]
}

GET /api/v1/landings/:id

Celá landing stránka včetně html, css, config_json a jazykových variant. Stejný tvar vrací POST, POST /from-template i PUT.

curl https://meta-pixel.cz/api/v1/landings/9 \
  -H "Authorization: Bearer adj_<klíč>"
{
  "id": 9, "workspace_id": 4, "name": "Signály — leden", "kind": "custom",
  "html": "<!doctype html><html lang=\"cs\">…<a href=\"{{cta_url}}\">Vstoupit do skupiny</a>…{{pixel_snippet}}</body></html>",
  "css": null,
  "config_json": null,
  "lang_variants_json": null,
  "created_at": 1767225600, "updated_at": 1767312000
}
PoleTyp
kindstringtme · clean · custom · ai
htmlstringkompletní HTML dokument s {{cta_url}} a {{pixel_snippet}}
cssstring | nullvkládá se do <style>
config_jsonstring | nullJSON s texty šablony (title, subtitle, button_text, …)
lang_variants_jsonstring | nullJSON {"en": {"html": "…", "css": "…"}}; čeština je vždy základní html

Chyby: 404 not_found — i pro id, které není kladné celé číslo.

GET /api/v1/landings/templates

Vestavěné šablony landing stránek včetně jejich HTML, CSS a výchozího config_json. Hodnota id je to, co posíláte do POST /landings/from-template.

curl https://meta-pixel.cz/api/v1/landings/templates \
  -H "Authorization: Bearer adj_<klíč>"
{
  "items": [
    { "id": "tme", "name": "t.me styl", "kind": "tme",
      "html": "<!doctype html>…", "css": "*{box-sizing:border-box;…}",
      "config_json": "{\"title\":\"Náš kanál\",\"subtitle\":\"…\",\"members_count\":\"0\",\"button_text\":\"VIEW IN TELEGRAM\",\"avatar_url\":\"\",\"theme\":\"light\"}" },
    { "id": "clean", "name": "Čistý (Apple)", "kind": "clean", "…": "…" },
    { "id": "signals", "name": "Signály (světlý)", "kind": "custom", "…": "…" },
    { "id": "dark", "name": "Tmavý t.me", "kind": "custom", "…": "…" },
    { "id": "vip", "name": "VIP Premium", "kind": "custom", "…": "…" },
    { "id": "minimal", "name": "Minimal", "kind": "custom", "…": "…" },
    { "id": "proof", "name": "Social proof", "kind": "custom", "…": "…" },
    { "id": "urgency", "name": "Urgence", "kind": "custom", "…": "…" }
  ]
}

GET /api/v1/landings/contract

Textové zadání pro psaní vlastního HTML landing stránky — účel, dosazované proměnné, pravidla a vzhled CTA. Přečtěte si ho (nebo ho předejte modelu) před tím, než pošlete HTML do POST /landings; HTML, které kontrakt poruší, server odmítne. Odpověď je text/plain, ne JSON.

curl https://meta-pixel.cz/api/v1/landings/contract \
  -H "Authorization: Bearer adj_<klíč>"
# Zadání: landing page pro Adjoin (Meta reklama → vstup do Telegram/WhatsApp skupiny)

## Účel
…
## Povinné a volitelné proměnné (vlož je PŘESNĚ v této podobě, nedosazuj vlastní hodnoty)
- {{cta_url}} — cílový odkaz CTA (na /go), dosadí Adjoin. Povinné.
- {{pixel_snippet}} — Meta Pixel kód, dosadí Adjoin. Když ho nevložíte, doplní se automaticky před </body>.
- {{title}} — nadpis (název skupiny/kanálu).
…
## Pravidla (jinak stránka neprojde kontrolou)
- Jeden soubor: CSS inline v <style> uvnitř <head>. Žádný externí CSS/JS/webfont/CDN.
- Žádný <iframe>. Žádný <script src="https://…"> — JavaScript smí být jen inline.
- Žádné odkazy na /api/ ani /admin/. Jediný odchozí proklik je {{cta_url}}.
- Aspoň jedno CTA tlačítko/odkaz s href="{{cta_url}}".
- Max 200 KB. …

POST /api/v1/landings

Založí landing stránku z vlastního HTML. Vyžaduje hlavičku Idempotency-Key. HTML musí splňovat kontrakt: obsahovat {{cta_url}} v href nebo onclick CTA prvku, být jediným dokumentem bez <iframe>, bez externích skriptů a bez odkazů na /api/ či /admin/, do 200 KB. Placeholder {{pixel_snippet}} se doplní automaticky před </body>, pokud chybí.

PoleTyp
namestringpovinné
kindstringpovinné — tme · clean · custom · ai; pro vlastní HTML použijte custom
htmlstringpovinné — kompletní HTML dokument
cssstring | nullvolitelné
config_jsonstring | nullvolitelné, musí být platný JSON
lang_variants_jsonstring | nullvolitelné, {"en": {"html": "…", "css": "…"}}; každý html musí obsahovat {{cta_url}}
curl -X POST https://meta-pixel.cz/api/v1/landings \
  -H "Authorization: Bearer adj_<klíč>" \
  -H "Idempotency-Key: 9a4e0c77-landing-signaly-leden" \
  -H "content-type: application/json" \
  -d '{"name":"Signály — leden","kind":"custom",
       "html":"<!doctype html><html lang=\"cs\"><head><meta charset=\"utf-8\"><title>Signály</title></head><body><h1>Signály zdarma</h1><a href=\"{{cta_url}}\">Vstoupit do skupiny</a></body></html>"}'
HTTP/1.1 201 Created

{
  "id": 9, "workspace_id": 4, "name": "Signály — leden", "kind": "custom",
  "html": "<!doctype html>…<a href=\"{{cta_url}}\">Vstoupit do skupiny</a>{{pixel_snippet}}</body></html>",
  "css": null, "config_json": null, "lang_variants_json": null,
  "created_at": 1767225600, "updated_at": 1767225600
}

Chyby: 400 invalid_body s českou hláškou kontraktu, např. "detail": "V HTML chybí {{cta_url}} — je potřeba u CTA tlačítka/odkazu (např. href=\"{{cta_url}}\")." nebo "HTML nesmí obsahovat <iframe>."; dále 409 idempotency_key_reused, 403 plan_limit, 429 write_limited.

POST /api/v1/landings/from-template

Založí landing stránku z vestavěné šablony — nejrychlejší cesta, když nechcete psát HTML. Texty a obrázky pak upravíte v config_json přes PUT /landings/:id nebo v adminu. Vyžaduje hlavičku Idempotency-Key.

PoleTyp
templatestringpovinné — tme · clean · signals · dark · vip · minimal · proof · urgency
namestringvolitelné; bez něj název šablony
curl -X POST https://meta-pixel.cz/api/v1/landings/from-template \
  -H "Authorization: Bearer adj_<klíč>" \
  -H "Idempotency-Key: 6f1c2a9e-landing-vip-unor" \
  -H "content-type: application/json" \
  -d '{"template":"vip","name":"VIP klub — únor"}'
HTTP/1.1 201 Created

{
  "id": 10, "workspace_id": 4, "name": "VIP klub — únor", "kind": "custom",
  "html": "<!doctype html>…", "css": "…",
  "config_json": "{\"title\":\"VIP klub\",\"subtitle\":\"…\",\"button_text\":\"…\"}",
  "lang_variants_json": null,
  "created_at": 1769904000, "updated_at": 1769904000
}

Chyby: 400 invalid_body — neznámá šablona ("detail": "template must be one of tme, clean, signals, dark, vip, minimal, proof, urgency"); 409 idempotency_key_reused, 403 plan_limit, 429 write_limited.

PUT /api/v1/landings/:id

Částečná úprava — pošlete jen pole, která měníte (name, kind, html, css, config_json, lang_variants_json). Výsledné HTML musí dál splňovat kontrakt. Landing, který právě používá běžící kampaň, server odmítne s 409 in_use, dokud nepošlete "allow_active": true — přepis stránky pod živou reklamou (třeba oprava textu) je legitimní, ale musí být vědomý.

curl -X PUT https://meta-pixel.cz/api/v1/landings/9 \
  -H "Authorization: Bearer adj_<klíč>" \
  -H "content-type: application/json" \
  -d '{"name":"Signály — leden (v2)","css":"h1{font-size:32px}","allow_active":true}'
{
  "id": 9, "workspace_id": 4, "name": "Signály — leden (v2)", "kind": "custom",
  "html": "<!doctype html>…", "css": "h1{font-size:32px}",
  "config_json": null, "lang_variants_json": null,
  "created_at": 1767225600, "updated_at": 1767398400
}

Bez allow_active u landingu pod aktivní kampaní:

HTTP/1.1 409 Conflict
{ "error": "in_use",
  "detail": "Landing používá běžící kampaň „Signály leden“. Pošlete allow_active: true, pokud ho chcete přepsat i tak." }

Dále 404 not_found, 400 invalid_body (porušení kontraktu, neplatný JSON v config_json), 403 plan_limit, 429 write_limited. Pole allow_active se do landingu neukládá — je to jen volba požadavku.

DELETE /api/v1/landings/:id

Smaže landing stránku. Je to jediná mazací operace v API a projde jen u landingu, který nepoužívá žádná kampaň — ani běžící, ani vypnutá (kampaň landing drží i ve vypnutém stavu). Nejdřív kampaň přepojte na jiný landing přes PUT /campaigns/:slug, nebo ji smažte v adminu. Nevratné.

curl -X DELETE https://meta-pixel.cz/api/v1/landings/9 \
  -H "Authorization: Bearer adj_<klíč>"
{ "ok": true }

Chyba 409 in_use rozlišuje, jaká kampaň landing drží — řešení je v obou případech jiné:

{ "error": "in_use", "detail": "Používá běžící kampaň „Signály leden“. Nejdřív ji odpojte." }

{ "error": "in_use", "detail": "Používá kampaň „Signály leden“ (vypnutá). Přepojte ji na jiný landing nebo kampaň smažte." }

Dále 404 not_found, 403 plan_limit, 429 write_limited.

GET /api/v1/pixels

Meta pixely workspace — jen identifikace, nikdy CAPI tokeny ani testovací kódy. Hodnota id je to, co posíláte jako pixel_id do kampaně; pixel_id je číslo pixelu u Mety.

curl https://meta-pixel.cz/api/v1/pixels \
  -H "Authorization: Bearer adj_<klíč>"
{ "items": [ { "id": 3, "name": "Hlavní pixel", "pixel_id": "1234567890123456" } ] }

GET /api/v1/chats

Telegram skupiny a kanály, ve kterých je připojený bot workspace — bez tokenů botů. has_static_invite říká, zda má chat záložní statický zvací odkaz.

curl https://meta-pixel.cz/api/v1/chats \
  -H "Authorization: Bearer adj_<klíč>"
{
  "items": [
    { "id": 5, "title": "Trading klub VIP", "tg_chat_id": "-1001234567890", "bot_id": 2, "has_static_invite": true }
  ]
}

GET /api/v1/domains

Vlastní domény workspace. active je true až po úspěšném DNS ověření — do té doby by landing na doméně neběžel. Domény lze přes API jen číst.

curl https://meta-pixel.cz/api/v1/domains \
  -H "Authorization: Bearer adj_<klíč>"
{ "items": [ { "id": 1, "hostname": "go.tradingklub.cz", "active": true } ] }

GET /api/v1/members

Seznam členů, filtrovatelné a stránkované.

ParametrTyp
campaign_idnumbervolitelné
sinceunix čas (s)jen členové od tohoto okamžiku
limit, offsetnumberstránkování, max limit 500
curl "https://meta-pixel.cz/api/v1/members?campaign_id=12&since=1751328000" \
  -H "Authorization: Bearer adj_<klíč>"
{ "items": [ { "id": 501, "tg_user_id": "123456789", "username": "petr",
    "campaign_name": "Black Friday", "joined_at": 1751330400, "payments_sum": 990 } ],
  "has_more": false }

POST /api/v1/payments

Zapíše platbu a — pokud je člen napojen na kampaň s pixelem — pošle Metě Purchase event. Člena identifikujete buď member_id, nebo tg_user_id. Volitelný external_ref (id platby u vás, např. Stripe pi_…) chrání před zdvojením: stejná hodnota podruhé vrátí původní platbu s duplicate: true a Purchase se neposílá znovu. paid_at (unix sekundy nebo ISO 8601) nastaví skutečný čas platby; bez něj se použije teď.

curl -X POST https://meta-pixel.cz/api/v1/payments \
  -H "Authorization: Bearer adj_<klíč>" \
  -H "content-type: application/json" \
  -d '{"tg_user_id":"123456789","amount":990,"currency":"CZK","note":"faktura #442","external_ref":"pi_3QxT2bK9"}'
HTTP/1.1 201 Created

{ "id": 87, "member_id": 501, "amount": 990, "currency": "CZK",
  "source": "admin", "external_ref": "pi_3QxT2bK9", "capi_sent": true, "duplicate": false }

Chyby: 400 invalid_body (amount musí být číslo > 0, currency třípísmenný kód, external_ref do 200 znaků), 404 not_found — žádný odpovídající člen ve workspace. Opakovaný external_ref vrací 200 s původní platbou a duplicate: true; pokud stejný external_ref použil jiný workspace, vrací se 409 conflict bez detailu o cizí platbě.

POST /api/v1/events

Pošle libovolný vlastní Conversions API event (nad rámec Lead/Subscribe/Purchase) k danému kliku — stejný tvar jako S2S postback POST /pb/:token, jen autentizovaný API klíčem.

curl -X POST https://meta-pixel.cz/api/v1/events \
  -H "Authorization: Bearer adj_<klíč>" \
  -H "content-type: application/json" \
  -d '{"click_id":"aBc123XyZ012","event":"ViewContent","value":10,"currency":"CZK"}'
{ "ok": true }

Chyby: 400 invalid_body (click_id a event jsou povinné), 404 not_found — klik neexistuje nebo nepatří kampani vašeho workspace.

MCP pro AI asistenty

Připojíte Adjoin do ChatGPT, Claude nebo jiného asistenta a ptáte se na výsledky nebo zakládáte kampaně slovy — „kolik lidí přišlo z Black Friday minulý týden", „založ kampaň Signály leden se šablonou VIP a zapni ji". Asistent volá stejná data a stejné operace jako REST API, jen místo curl příkazů píšete běžnou větou.

MCP (Model Context Protocol) je otevřený standard, kterým AI klienti mluví s externími nástroji. Adjoin provozuje MCP server na adrese:

https://meta-pixel.cz/mcp

Dvě cesty přihlášení

ZpůsobPro kohoPlán
OAuthChatGPT, Claude Desktop / claude.ai, Claude Code, VS Code, Antigravity — klient si sám zaregistruje aplikaci, otevře přihlášení do Adjoinu a obrazovku souhlasu. Nic nekopírujete.každý plán
API klíčCursor, skripty a klienti, kteří umí poslat hlavičku Authorization: Bearer adj_…. Klíč vytvoříte v adminu (Nastavení → API klíče).Business

V obou případech asistent pracuje s jedním workspace — tím, ke kterému patří klíč, nebo tím, který jste zvolili při souhlasu. Přístup kdykoli zrušíte na stránce AI asistent (položka v levém menu administrace), u API klíče jeho zneplatněním.

Připojení klientů

Postupy odpovídají oficiální dokumentaci jednotlivých klientů (září 2026). Krok za krokem, i s dosazením vašeho klíče, je na stránce AI asistent (položka v levém menu administrace).

Claude Desktop / claude.ai

Customize → Connectors → Add custom connector → vložte URL https://meta-pixel.cz/mcp → Connect → v otevřeném okně Adjoinu klikněte Povolit. Na plánu Free lze mít 1 vlastní konektor; na Team/Enterprise konektor přidává Owner v Organization settings.

https://meta-pixel.cz/mcp

ChatGPT

Vyžaduje Režim vývojáře (Settings → Apps/Connectors → Advanced → Developer mode), dostupný na webu pro plány Plus, Pro, Business, Enterprise a Edu. Poté Create connector → MCP Server URL → Authentication: OAuth.

https://meta-pixel.cz/mcp

Claude Code

Přidejte server a poté v příkazu /mcp zvolte Authenticate — otevře se souhlas v prohlížeči.

claude mcp add --transport http -s user adjoin https://meta-pixel.cz/mcp

S API klíčem místo OAuth:

claude mcp add --transport http -s user adjoin https://meta-pixel.cz/mcp \
  --header "Authorization: Bearer adj_<klíč>"

Antigravity

Soubor ~/.gemini/config/mcp_config.json (klíč se jmenuje serverUrl, ne url):

{"mcpServers":{"adjoin":{"serverUrl":"https://meta-pixel.cz/mcp"}}}

Cursor

Soubor .cursor/mcp.json v projektu; doporučujeme API klíč:

{"mcpServers":{"adjoin":{"url":"https://meta-pixel.cz/mcp","headers":{"Authorization":"Bearer adj_<klíč>"}}}}

VS Code

Soubor .vscode/mcp.json:

{"servers":{"adjoin":{"type":"http","url":"https://meta-pixel.cz/mcp"}}}

Ostatní klienti (mcp-remote)

Klienti, kteří umí jen lokální (stdio) servery, se připojí přes most mcp-remote; s API klíčem doplňte --header "Authorization: Bearer adj_<klíč>".

npx mcp-remote https://meta-pixel.cz/mcp

Nástroje

Server nabízí 17 nástrojů. Čtecí nástroje vrací data workspace; zápisové volají stejnou servisní vrstvu jako REST API a platí pro ně stejná pravidla — povinný idempotency_key (nejvýš 128 bajtů; stejný klíč podruhé vrátí uložený úspěšný výsledek; chybová odpověď se neukládá, takže po opravě parametrů lze stejný klíč poslat znovu; stejný klíč s jiným obsahem vrátí chybový text a starý výsledek se nevrací), limit 10 zápisů/min na workspace (sdílený s REST API; přehrání uloženého výsledku se do něj nepočítá), kampaň vzniká vypnutá. U OAuth přístupu zápisy vyžadují rozsah adjoin:write, který uživatel potvrzuje při souhlasu; u API klíče plán Business. Vypnutí kampaně (set_campaign_active s active: false) je fail-safe: obchází kontrolu plánu i limit zápisů, jen idempotency_key zůstává povinný.

NástrojTypCo děláPovinné parametry
adjoin_overviewčteníPřehled workspace: plán, počet kampaní, souhrn trychtýře za 7 a 30 dní. Vhodný první dotaz.
list_campaignsčteníSeznam kampaní se statistikami, volitelně za 7/30/90 dní nebo jen běžící.— (days, only_active volitelné)
get_campaignčteníDetail jedné kampaně podle slugu: nastavení, trychtýř a co se u ní reálně měří.slug
list_membersčteníČlenové, kteří vstoupili do skupiny; kontaktní údaje jsou zkrácené (vyhodnocování, ne export).— (campaign_slug, limit volitelné)
get_setup_statusčteníStav zprovoznění kampaně — co chybí, aby měření fungovalo.slug
get_audit_logčteníPoslední změny ve workspace včetně těch, které udělal model přes MCP.— (limit volitelné)
list_setup_optionsčteníJedním voláním všechny pixely, chaty a domény workspace — jen ID a názvy, žádné tokeny. Použít před create_campaign.
list_landingsčteníSeznam landingů (id, název, druh, poslední změna) bez HTML.
get_landingčteníCelý landing včetně html, css a config_json.landing_id
get_landing_contractčteníPravidla pro HTML landingu — přečíst před psaním vlastního HTML.
create_landingzápisZaloží nový landing z vlastního HTML (druh custom); HTML musí splňovat kontrakt.name, html, idempotency_key
create_landing_from_templatezápisZaloží landing z vestavěné šablony (tme, clean, signals, dark, vip, minimal, proof, urgency).template, idempotency_key
update_landingzápisUpraví název, html nebo css landingu; landing pod běžící kampaní jen s allow_active: true.landing_id, idempotency_key
delete_landingzápisSmaže landing, který nepoužívá žádná kampaň (běžící ani vypnutá) — jinak vrátí chybu in_use s návodem, co udělat. Jediná mazací akce na serveru, nevratná.landing_id, idempotency_key
create_campaignzápisZaloží kampaň z pixelu, landingu a cíle — vždy vypnutou; slug se dopočte z názvu.name, pixel_id, landing_id, dest_type, idempotency_key
update_campaignzápisUpraví nastavení kampaně podle slugu; neumí ji zapnout ani změnit slug.slug, idempotency_key (+ aspoň jedno měněné pole)
set_campaign_activezápisZapne nebo vypne kampaň; zapnutí projde jen u připravené kampaně v rámci plan limitu, vypnutí vždy.slug, active, idempotency_key

Stejně jako POST /campaigns doplňuje create_campaign výchozí režimy, když je asistent nepošle: link_mode podle cíle (per_click u Telegramu, static jinde) a approve_mode: auto_tracked. Chyby se asistentovi vrací jako text (např. „Slug „signaly“ už používá jiná kampaň (slug_taken)…"), ne jako HTTP stavy, takže na ně umí reagovat další otázkou.

Co asistent nemůže

  • Sáhnout do jiného workspace. Žádný nástroj nepřijímá workspace_id; workspace je dán klíčem nebo souhlasem.
  • Mazat kampaně, členy, platby ani historii kliknutí. Jediné, co lze smazat, je landing, který nepoužívá žádná kampaň — běžící ani vypnutá.
  • Posílat zprávy členům — žádné broadcasty ani drip sekvence.
  • Utrácet za reklamy — žádné rozpočty ani správa reklam.
  • Spravovat API klíče, uživatele, domény, boty, GDPR výmazy ani cokoli ze superadmin oblasti.
  • Zapnout kampaň bez kontroly. Kampaň vzniká vypnutá; set_campaign_active ověří připojený chat a plan limit. Vypnutí projde vždy.

Každý zápis přes MCP se zaznamená do Protokolu workspace se zdrojem (via: "mcp" nebo "mcp_oauth") a aktérem — vždy dohledáte, co asistent změnil. Nástroj get_audit_log ukáže totéž přímo v konverzaci.

OAuth a protokol

Tato část je pro vývojáře MCP klientů a integrací. Běžný uživatel ji nepotřebuje — klienti z předchozí sekce vše provedou sami.

OAuth 2.1

Server implementuje OAuth 2.1 s dynamickou registrací klienta a PKCE. Klient bez tokenu (nebo s neplatným či prošlým tokenem) dostane z /mcp odpověď 401 s hlavičkou, podle které si najde autorizační server (RFC 9728) a rovnou zná rozsahy, o které má žádat:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://meta-pixel.cz/.well-known/oauth-protected-resource", scope="adjoin:read adjoin:write"

{ "error": "unauthorized", "detail": "Vyžadován Bearer token (API klíč nebo OAuth)." }
EndpointÚčel
GET /.well-known/oauth-protected-resourcemetadata chráněného zdroje: resource, authorization_servers, scopes_supported
GET /.well-known/oauth-authorization-servermetadata autorizačního serveru: endpointy, code_challenge_methods_supported: ["S256"], grant_types_supported: ["authorization_code", "refresh_token"], token_endpoint_auth_methods_supported: ["none"]
POST /oauth/registerdynamická registrace klienta (RFC 7591); tělo { "client_name", "redirect_uris" }, jen https (nebo localhost); vrací 201 s client_id (mcp_…), bez client secret — veřejný klient. Limit 10 registrací za hodinu z jedné IP adresy (429 rate_limited). Záznam klienta platí 30 dní; po první úspěšné výměně kódu za token 180 dní
GET /oauth/authorizesouhlasová obrazovka; parametry response_type=code, client_id, redirect_uri, code_challenge, code_challenge_method=S256, scope, state, volitelně resource; nepřihlášeného uživatele přesměruje na přihlášení do adminu a vrátí ho zpět
POST /oauth/tokenvýměna kódu za token (grant_type=authorization_code + code_verifier, client_id, redirect_uri) a obnova (grant_type=refresh_token); tělo application/x-www-form-urlencoded
  • PKCE S256 je povinné — bez code_challenge vrací /oauth/authorize chybu; implicit ani password grant neexistují.
  • Souhlas může udělit jen administrátor workspace. Souhlasová obrazovka zobrazuje název aplikace a doménu (z ověřeného redirect_uri), kam bude autorizační kód odeslán — zkontrolujte ji, než kliknete na Povolit. Stránku souhlasu nelze vložit do <iframe> (X-Frame-Options: DENY, frame-ancestors 'none') — ochrana proti clickjackingu.
  • Rozsahy: adjoin:read (kampaně, statistiky, členové) a adjoin:write (zakládat a upravovat kampaně a landingy, zapínat a vypínat, mazat nepoužívané landingy). Požadovaný scope obsahující write vydá oba rozsahy, jinak jen čtení. Token bez adjoin:write dostane u zápisového nástroje textovou chybu, ne 401.
  • Parametr resource (RFC 8707) se v authorize i token ověřuje: přijatá hodnota je https://meta-pixel.cz/mcp (nebo holý origin https://meta-pixel.cz; koncové lomítko se ignoruje). Jiná hodnota → /oauth/authorize přesměruje na redirect_uri s error=invalid_target (a state), /oauth/token vrátí 400 { "error": "invalid_target", "error_description": "resource musí být https://meta-pixel.cz/mcp" }. Hodnota z authorize se váže ke kódu — v token requestu ji zopakujte stejnou, nebo vynechte. Token vydaný s resource se na /mcp kontroluje jako audience. Chybějící resource je povolen kvůli starším klientům.
  • Platnost: access token (adjoat_…) 30 dní, refresh token (adjref_…) 180 dní; autorizační kód 10 minut, jednorázový. Při obnově se vydá nový pár a starý přestane platit.
  • Jeden token = jeden workspace, vybraný při souhlasu podle přihlášeného uživatele. Agentura s více klienty projde souhlasem pro každý workspace zvlášť.
  • Změna rozsahu souhlasu: pokud se význam adjoin:write rozšíří, starší write granty přestanou platit — /mcp vrátí 401 s WWW-Authenticate a obnova tokenu 400 invalid_grant („Rozsah oprávnění se změnil, připojte aplikaci znovu."). Klient tím projde souhlasem znovu; nic jiného dělat nemusí.
  • Zrušení: uživatel ruší přístup na stránce AI asistent (položka v levém menu administrace); access i refresh token přestanou platit okamžitě. Granty se revokují (access i refresh token, včetně záznamu v přehledu AI asistent) také automaticky: při změně hesla uživatele, při jeho odebrání z workspace a při změně jeho role na buyer. Po dobu pozastavení workspace vrací /mcp 403 workspace_suspended a obnova tokenu 400 invalid_grant („Workspace je pozastaven."); granty se v tom případě nerevokují a po obnovení workspace fungují dál.
POST /oauth/token
content-type: application/x-www-form-urlencoded

grant_type=authorization_code&code=…&code_verifier=…&client_id=mcp_…&redirect_uri=https://…

{ "access_token": "adjoat_…", "refresh_token": "adjref_…", "token_type": "Bearer",
  "expires_in": 2592000, "scope": "adjoin:read adjoin:write" }

Transport

  • JSON-RPC 2.0 přes POST https://meta-pixel.cz/mcp, tělo i odpověď application/json (Streamable HTTP bez SSE). Server je bezstavový — nevydává Mcp-Session-Id, každý POST je samostatný.
  • GET /mcp s Accept: text/event-stream vrací 405 s hlavičkou Allow: POST (SSE stream se nenabízí). Obyčejný GET /mcp s platným tokenem vrací diagnostiku: verzi serveru, podporované verze protokolu a seznam nástrojů.
  • Podporované verze protokolu: 2025-11-25 (nejnovější), 2025-06-18, 2025-03-26, 2024-11-05. V initialize server vrátí klientovu verzi, pokud ji podporuje, jinak 2025-11-25.
  • Hlavička MCP-Protocol-Version na dalších požadavcích: neznámá hodnota → 400 s JSON-RPC chybou -32600; chybějící hlavička se toleruje.
  • Notifikace (notifications/*) server přijme s 202 Accepted bez těla; dávku zpráv (JSON pole) zpracuje sekvenčně a vrátí pole odpovědí. Dávka smí mít nejvýš 20 zpráv — větší vrátí 400 s JSON-RPC chybou -32600; dávka složená jen z notifikací dostane 202 bez těla; prázdné pole vrátí 400 (-32600).
  • Metody: initialize, ping, tools/list, tools/call, resources/list a prompts/list (obě vrací prázdný seznam). Jiná metoda → JSON-RPC chyba -32601.
  • Chyba nástroje přichází jako výsledek s isError: true a českým textem v content; HTTP stav zůstává 200. Autentizace (401 unauthorized), pozastavený workspace (403 workspace_suspended) a limit 120 požadavků za minutu (429 rate_limited; u OAuth na jedno připojení, u API klíče na klíč) se vrací jako HTTP chyby stejně jako u REST API.

Příklad: initialize a tools/list s API klíčem

curl -X POST https://meta-pixel.cz/mcp \
  -H "Authorization: Bearer adj_<klíč>" \
  -H "content-type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize",
       "params":{"protocolVersion":"2025-11-25","capabilities":{},
                 "clientInfo":{"name":"muj-klient","version":"1.0.0"}}}'
{
  "jsonrpc": "2.0", "id": 1,
  "result": {
    "protocolVersion": "2025-11-25",
    "capabilities": { "tools": { "listChanged": false } },
    "serverInfo": { "name": "adjoin", "version": "1.1.0" },
    "instructions": "Adjoin měří vstupy do Telegram/WhatsApp skupin z Meta reklam. …"
  }
}
curl -X POST https://meta-pixel.cz/mcp \
  -H "Authorization: Bearer adj_<klíč>" \
  -H "MCP-Protocol-Version: 2025-11-25" \
  -H "content-type: application/json" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
{
  "jsonrpc": "2.0", "id": 2,
  "result": {
    "tools": [
      { "name": "adjoin_overview", "description": "Přehled workspace: plán, počet kampaní, …",
        "inputSchema": { "type": "object", "properties": {} } },
      { "name": "get_campaign", "description": "Detail jedné kampaně podle slugu: …",
        "inputSchema": { "type": "object", "properties": { "slug": { "type": "string" } }, "required": ["slug"] } },
      "…"
    ]
  }
}

Volání nástroje má stejný tvar s metodou tools/call a params: { "name", "arguments" }:

curl -X POST https://meta-pixel.cz/mcp \
  -H "Authorization: Bearer adj_<klíč>" \
  -H "MCP-Protocol-Version: 2025-11-25" \
  -H "content-type: application/json" \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call",
       "params":{"name":"set_campaign_active",
                 "arguments":{"slug":"signaly-leden","active":true,"idempotency_key":"zapnout-signaly-leden-1"}}}'
{ "jsonrpc": "2.0", "id": 3,
  "result": { "content": [ { "type": "text", "text": "{\n  \"slug\": \"signaly-leden\",\n  \"bezi\": true\n}" } ] } }