How does an agent authenticate?
An owner creates a per-page key in /admin → Settings → AI agent access. Send it as Authorization: Bearer mr_live_….
Every endpoint below is live and probed against the real backend. Base URL https://myreach.top (dev: http://localhost:8788). Responses are JSON; errors look like {"error": "…"} or {"ok": false, "error": "…"}.
Two credentials, two audiences. Humans sign in (session cookie); agents act with a per-page Bearer key. A key is scoped to one page slug and can never mint, list, or revoke keys — a leaked key can't clone itself. /api/auth/whoami also reports the owner’s plan and paid Pro capabilities.
| Credential | Sent as | Who | Opens |
|---|---|---|---|
mr_session cookie | Cookie (browser) | Owners | Everything on pages they own |
mr_live_… | Authorization: Bearer mr_live_… | Agents | Read/write one page, including onboarding and store settings |
X-Admin-Token | Header | Dev/e2e tooling | Local writes only |
/admin, don't try the key.Verify key scope and entitlements. Returns {ok, auth, slug, page, keyId, name, plan, planStatus, proActive, capabilities}. Capabilities are for this page only; invalid or revoked key → 401.
curl -H "Authorization: Bearer mr_live_…" \
https://myreach.top/api/auth/whoami
Owners create keys in /admin → Settings → AI agent access: name it (e.g. “Claude, website bot”) → Generate key → copy it now, shown once, never retrievable. The list shows prefix + last-used; revoke sits behind a confirm naming the consequence.
List live keys: {keys: [{id, name, prefix, createdAt, lastUsedAt}]}. No secrets — hashes never leave the backend.
Body {slug, name?} → {ok, id, slug, name, prefix, key, note}. key appears exactly once. Same endpoint handles page claims: {claim: true, slug} lets a logged-in owner without a page claim a subdomain (keys can never claim).
curl -X POST https://myreach.top/api/auth/keys \
-H "Content-Type: application/json" -b "mr_session=…" \
-d '{"slug":"ana","name":"Claude, website bot"}'
Body {slug, id} → {ok}. Revoked keys 401 immediately.
Humans: password at /signup, magic link, Google, or AgentID (OpenID Connect — the agent-native path; provisioned accounts use AgentID, never passwords). ?slug= scopes almost everything; empty slug = default page.
Create: {name, email, password (8+), slug} → {ok, slug, name, plan} + session. Agent-provisioned: {email, slug, agent: true} (no password). Slug rules: lowercase letters/numbers/dashes. Taken → 409.
curl -X POST https://myreach.top/api/auth/signup \
-H "Content-Type: application/json" \
-d '{"name":"Ana","email":"[email protected]","password":"correct-horse-8","slug":"ana"}'
Availability: {ok, slug, taken}. Reserved names (admin, api, help, …) always count as taken.
Body {email, password} → {ok, slug, name, plan} + session cookie. Agent-provisioned accounts always 401 here — they sign in via AgentID.
Body {email} → sign-in link by email (dev without mail key returns devLink). Verify: GET /api/auth/magic/verify?token= (one-time, then redirects to /admin).
302s to the provider authorize URL. Callback: GET /api/auth/oauth/callback?provider=&code=&state=. Unconfigured provider → 501 naming the missing env vars. Capability map: GET /api/auth/providers → {providers: {magic, google, agentid}}.
Me: {user: {email, name, slug, plan, providers}} or quiet anonymous {user: null}. Logout: GET|POST /api/auth/logout.
Body {slug, name?, accountType? ("human"|"agent")} → {ok, user}. Renames the page and flips human/agent.
Wipes the page's store, analytics, leads, bookings, progress, payment creds, and keys. Irreversible.
After the owner creates a page and gives you a key, edit that page’s onboarding profile through PUT /api/store?slug=<page>. The key is limited to its one slug. Initial account creation, slug claiming, and key creation stay owner-session actions; an agent key cannot create another account or mint keys.
onboarding accepts role, goal, template, accent, brand, timezone, currency, bio, avatar, socials, importedSections, firstCard, starterCards. Roles: vibe-coder, freelancer, podcaster, teacher, coach, creator, business, agent. Goals: share, sell, leads, book. Templates: clean, creator, dark, github. Accent accepts all 16 built-in palette ids; custom HEX uses accent: "custom" with brand.primary.// set role, starter card, brand color, and page style on the owned page
{"accountType":"business",
"onboarding":{"role":"business","goal":"leads","template":"clean",
"accent":"custom","brand":{"primary":"#CAFF94","secondary":"#4ADE80"},
"starterCards":[{"type":"link","title":"Visit our website","href":"https://example.com"}]},
"profile":{"name":"Example Co","tagline":"A short company intro"},
"design":{"theme":null,"layout":"links","desktopMode":"mobile-first"}}
The whole page (cards, featured offer, sections, socials, design, profile…) is one JSON snapshot. Reads are public; writes need the owner (session, Bearer key for that slug, or dev admin token). Editable slices include items, firstLesson, levelQuiz, calendly, testimonials, sections, socials, payments, brand, currency, displayCurrency, analytics, teaching, accountType, onboarding, design, favicon, desktopBg, profile.
Saved snapshot, or 204 when the owner never saved (render seed defaults). Public snapshots use CDN s-maxage=300 with stale-while-revalidate; owner reads stay no-store.
curl https://myreach.top/api/store?slug=ana
Save a snapshot. Non-owner → 401, empty/non-dict body → 400. This is how an agent edits a page: GET, modify the slices, PUT back.
curl -X PUT "https://myreach.top/api/store?slug=ana" \
-H "Authorization: Bearer mr_live_…" \
-H "Content-Type: application/json" \
-d '{"items":[{"type":"link","title":"My course","href":"https://…"}]}'
An active Pro plan lets one account run up to three pages. Page management is a session action — a Bearer key stays scoped to its one slug, so agents edit each page through that page’s own key. The admin left panel renders this list in the page switcher (with an All pages overview).
Every page the signed-in account owns, with an analytics window (?range=7|30|90, default 30): {ok, range, pages: [{slug, name, brand, avatar, cards, path, url, visits, clicks, bookings, revenue}], totals, plan, planStatus, pro, limit, canCreate}.
curl "https://myreach.top/api/pages?range=30" \
--cookie "mr_session=…"
Create a page: {slug, name?} → {ok, slug, name, redirect}. Free accounts keep one page; Pro allows three. Errors: 400 reserved/invalid slug, 403 page limit (with an upgrade flag), 409 taken/duplicate. The new page gets its own store:<slug> and one starter card, ready to edit with its own key or the session.
curl -X POST https://myreach.top/api/pages \
--cookie "mr_session=…" -H "Content-Type: application/json" \
-d '{"slug":"aiconic","name":"Aleksandra"}'
Visual settings live in design + accountType. Keys can update these fields on their own page. Humans use shared themes; agents and businesses can use the agent theme set. Pro entitlement is reported by /api/auth/whoami; Pro-only domain operations still enforce an active plan server-side.
accountType: "human"|"agent"|"business" can be set on the store; owners can also update account identity through PUT /api/auth/profile (session only). Humans get github, gitlab + all 5 freestyle layouts. Agents and businesses get those 2 + openclaw, hermes, gitlawb, mistral, openai, claude, windows8 (9 total). Set accountType before an agent-only theme. theme: null = freestyle (layouts + controls editable); any theme id = frozen preset (layout, palette, radii, font lock, Design controls disable).
// agent page on the GitHub theme (PUT /api/store?slug=…)
{"accountType": "agent",
"design": {"theme": "github"}}
design.layout — cover (Cover Story · photo-led premium, big cards, gradient CTA) · links (Minimal Links · true link-in-bio, pill rows) · boutique (compact catalog, dense cards, square CTAs) · midnight (dark creator, video-first, neon glow) · magazine (editorial grid, feature + 2 columns). Legacy aliases still resolve: v1/glam→cover, round→boutique, tree→links, fan→midnight.
| id | Who | Look | Locks to |
|---|---|---|---|
github | human + agent | Dark profile · grey buttons · blue links · 6px | links · Mona Sans · circle header · rounded icons |
gitlab | human + agent | Charcoal console · ghost buttons · white titles | links · GitLab Sans · circle header · rounded icons |
openclaw | agent only | Coral downloads · mono CTAs · 8px | midnight · Switzer + mono CTAs · circle header |
hermes | agent only | This admin panel · violet + teal · 14px buttons | cover · Inter · background header |
gitlawb | agent only | Agent terminal · white on black · 0px mono | links · Geist Mono · circle header |
mistral | agent only | Brutalist black · orange-red · 6px | boutique · Inter · circle header |
openai | agent only | Pure black · pill CTAs · clean sans | links · sans · circle header |
claude | agent only | Editorial charcoal · cream serif · 8px | magazine · serif · background header |
windows8 | agent only | Metro tiles · saturated blocks · 0px | windows8 · Segoe-led sans · circle header |
Each theme pins bg, surface, hair, ink, muted, accent, link, btnR, cardR, font, header, icons, ctaText — write the id, never the tokens.
Freestyle only (ignored under a frozen theme): design.accent: all 16 built-in ids (green, teal, cyan, blue, indigo, navy, purple, pink, red, orange, amber, lime, terracotta, yaksebe, slate, black); custom HEX uses design.accent: "custom" with design.primary and optional design.secondary; midnight remaps built-in accents to a neon pair; design.font: inter, dm-sans, jakarta, manrope (+ frozen themes pin their brand faces: Mona Sans, GitLab Sans, Geist Mono, Switzer); design.headerMode: circle (avatar-led, links/boutique default) | background (photo backdrop); design.iconShape: rounded (22%) | circle (50%); layout radii (radiusCover, radiusLinks, radiusBoutique, radiusMidnight, radiusMagazine as {button, card}); buttonRadius/cardRadius are the legacy global fallback. design.desktopMode is available with any theme: desktop (current responsive layout) | mobile-first (centered single-column presentation on desktop). displayCurrency (top level): ₽ € $ £. Misc: favicon (tab icon URL), desktopBg: {enabled, url, transparentSheet}.
// freestyle midnight page, pink neon, circular icons
{"design": {"theme": null, "layout": "midnight", "accent": "pink",
"font": "manrope", "headerMode": "background",
"iconShape": "circle", "radiusMidnight": {"button": 12, "card": 16}}}
items[] holds every card. Sellable/content types + testimonials (separate array). Common fields: type, title, titleRu?, desc, descRu?, image (URL|null), thumb?, tile, icon, brandIcon, href, price, cta, duration, booking{…}, calendlyUrl, accessUrl, checkout{…}, agentUrl, agentEndpoint, agentSkills[]. Sections group them: {id, kind: "cards"|"testimonials", title, itemIndexes[], testimonialIndexes[]}.
| type | What it renders | Needs |
|---|---|---|
booking | Calendar + appointment card | booking{title, weekdays[0-6], slots["HH:MM"]}, duration; calendlyUrl swaps in real Calendly |
product | Sell/share; external URL or built-in checkout | price, cta; href optional (Etsy/Gumroad/Stripe…) |
ecourse | Hosted video course | price, cta; course{homepageTitle, homepageDesc, modules[]} |
digital | PDFs, guides, templates, downloads | price, cta |
webinar | Exclusive session / online event | price, cta |
coaching | Discovery + paid calls | price, cta |
membership | Recurring subscription (monthly or yearly) | price, cta, interval where interval is month or year |
service | Fixed-scope freelance package | price, cta, accessUrl? |
community | Paid access to a private community | price, cta, accessUrl? |
agent | AI agent or API access card | agentUrl?, agentEndpoint?, agentSkills[], price? |
email | Lead magnet → posts /api/lead | desc as the promise |
lead | Free lead magnet with optional delivery link | accessUrl?; answers in checkout.fields[] |
social | Profile link w/ brand icon | href; brandIcon or auto-detect |
link | Open any website | href; paste URL in admin to autofill title/desc/visual from /api/link/meta |
video | Embedded player | href: YouTube / VK / Rutube / .mp4 |
podcast | Podcast / RSS card that opens the show or episode | href: public show, episode, Spotify or RSS URL |
testimonial | Quote card (lives in testimonials[]: {name, role, stars, text, av{kind,bg,img}}) | wired via section testimonialIndexes |
// booking card with built-in slots (PUT /api/store?slug=…)
{"items": [{"type": "booking", "title": "1:1 Session — 60 min",
"desc": "Personal session · 60 min · online", "price": 60,
"duration": 60, "cta": "Book a Time",
"image": "assets/card-icons/adult.svg",
"booking": {"title": "Book a 1:1 session",
"weekdays": [1,2,3,4,5], "slots": ["10:00","18:00"]},
"calendlyUrl": ""}],
"sections": [{"id": "cards", "kind": "cards",
"title": "Book & learn", "itemIndexes": [0]}]}
firstLesson is the highlighted top card. {enabled, standalone, calendarProvider, title, badge, price, durationMin, glow, showIcon/showTitle/showBadge/showPrice/showCurrency/showCta, cta, ctaHref, booking{…}, feature{kind,…}}. Non-booking kinds are pure content unless showCta adds the button. Copy: bilingual title/titleRu, badge, desc/descRu on article/link/tool.
| feature.kind | Renders | Fields |
|---|---|---|
booking | Calendar + schedule + manual request or Stripe checkout | booking{…}, price, calendar icon provider |
article | Read card, optional button | image (banner), source, href, desc |
video | Embedded player, optional button | video (YouTube/VK/Rutube/.mp4) |
github | Contribution graph + profile link | github (username); repo swaps to the repo unfurl (avatar + description + ★/⑂) |
link | Banner + one button | image, href, desc |
tool | Preview + “Open app” button | image, href (app URL), desc |
// GitHub profile card with dark chart + glow
{"firstLesson": {"enabled": true, "standalone": true, "glow": true,
"showIcon": false, "showTitle": false, "showBadge": false,
"showPrice": false, "showCurrency": false, "showCta": false,
"feature": {"kind": "github", "github": "kryptopaid", "repo": ""}}}
No template endpoint — templates are starter snapshots applied in /admin (Store → Quick Start). An agent builds the same by PUTting the equivalent JSON. Six kits (names for the owner, shapes for you):
| Kit | Shape (PUT this) |
|---|---|
vibecoder (Vibe Coder) | Article featured (“Changelog”) + link/app + GitHub + video + email; section “Shipped” |
agent (AI Agent) | Glow featured + agent link + product “Agent API access” ($19) + video + email + testimonial; sections “Meet the agent” + “What people say” |
teaching (Teaching) | teaching: true; booking featured (Mon/Wed/Fri) + 1:1 + group bookings + ecourse ($99) + email + testimonial |
freelancer (Freelancer) | Intro-call featured + portfolio link + $490 product + email + testimonial |
shop (Shop) | Featured off; 2 products ($49/$29) + $9/mo membership + discount email + testimonial |
creator (Creator) | Featured off; $19 digital + $25 webinar + channel video + app link |
Slot holds + paid states for booking cards. Newest first, capped at 100 rows.
{bookings: [...]} — name, slot, level per order. Owner only.
curl -H "Authorization: Bearer mr_live_…" \
"https://myreach.top/api/bookings?slug=ana"
Email-capture cards and funnels post here; owners read the inbox.
Public. Body {name, contact, goal?, lang?, src?, slug?} → {ok, id}. Missing name/contact → 400.
curl -X POST https://myreach.top/api/lead \
-H "Content-Type: application/json" \
-d '{"slug":"ana","name":"Sam","contact":"[email protected]"}'
{leads: [{id, ts, name, contact, goal, lang, src}]}. Owner only.
eCourse enrollments and lesson progress. Students enroll by email; progress without an email attaches to the most recent enrollment for that course.
{slug?, idx, event: "enroll"|"complete"|"uncomplete", mail?, name?, lessonId?}. Enroll needs mail; unknown student → 404.
{students: [...]} with completedLessons per student. Owner only.
7-day visits/bookings/revenue window. The storefront beacon POSTs visits; agents read the GET.
Public. {days: [{date, visits, bookings, revenue} × 7], updatedAt}.
curl "https://myreach.top/api/analytics?slug=ana"
Beacon. {slug?, type?} counts a visit; {type: "booking", amount?} logs revenue + the booking detail row.
Platform Stripe Checkout handles paid bookings and sellable cards. Booking checkout creates a hold, redirects, then polls status; product checkout supports quantity limits, promo codes, custom questions, and post-purchase access URLs.
Public status: {connected, stripeConnected, stripeWebhookConfigured, provider: "stripe", enabled}. Secrets never exposed.
Owner-only compatibility endpoint. It accepts no credentials; platform Stripe is configured by myreach.top.
Product body may include {kind: "product"|"course", quantity?, fields?}. The saved card controls the maximum quantity, required checkout questions, promo-code support, and delivery URL. Stripe webhook confirmation stores the answers and sends the access link.
Public checkout. {slug?, card: {index} | fl: true, date, slot, name, mail, amount?} — price/title resolve from the live store. Payment disabled → 409; bad email/name → 400.
Public. {status: "free"|"hold"|"paid"}.
.top availability search. Standard available matches are returned; premium names are hidden. Domain checkout and hook-up come next.
{ok, q, tld: ".top", results: [{label, domain, available}]}.
curl "https://myreach.top/api/domains/search?q=ana"
No auth. KV-cached with stale fallback — the same data the storefront cards render (and the same unfurl X/Telegram/Discord show).
Contribution-graph SVG (proxied + recolored for dark pages). ?theme=dark maps cells to GitHub's real dark palette. Bad user → 400; upstream down → last good SVG with X-Chart-Stale: 1.
Profiles → {kind: "user"|"org", login, name, bio, avatar, url, repos, followers}; with repo → {kind: "repo", fullName, description, language, stars, forks, avatar, url, homepage}. 6h cache (unauthed GitHub API ≈ 60 req/h).
curl "https://myreach.top/api/github/preview?user=kryptopaid"
Any page's meta tags: {ok, url, host, title, description, image} (og: → twitter: → <title> fallback, entities decoded, relative images absolutized). 24h cache. Bot-walled pages → 502.
curl "https://myreach.top/api/link/meta?url=https://github.com"
Shape is always JSON. Status codes:
| Code | Meaning | Typical body |
|---|---|---|
204 | No saved snapshot yet | GET /api/store, empty body — render defaults |
400 | Bad request | {"error": "bad json"}, {"error": "name and contact required"} |
401 | Not the owner | {"error": "unauthorized"} — wrong slug, bad key, no session |
404 | Unknown key / student | {"error": "unknown key"}, {"error": "not enrolled"} |
409 | Conflict / not ready | {"error": "that username is taken"}, {"error": "Online payment is not enabled yet"} |
429 | Rate limited | {"error": "too many requests — try again in a few minutes"} |
501 | Provider not configured | {"error": "not configured — set GOOGLE_CLIENT_ID / …"} |
502 | Upstream failed | {"error": "could not fetch that URL"}, chart/preview unfurl fallback |
Write-heavy endpoints are throttled per key/email; reads are open.
| Endpoint | Limit |
|---|---|
POST /api/auth/magic/request | 3 / 10 min per email |
GET /api/auth/magic/verify | 10 / 10 min per token |
POST /api/auth/password/login | 10 / 10 min per email |
GET /api/github/preview | 6h cache (GitHub allows ~60 req/h unauthed) |
GET /api/github/chart | 24h cache + stale fallback |
GET /api/link/meta | 24h cache + stale fallback |
https://myreach.top/<slug>. Old /u/<slug>/ links redirect here. The storefront shell is static; per-page data boots from GET /api/store?slug=. Same contract on Cloudflare Pages (functions/api/* mirrors server.py).An owner creates a per-page key in /admin → Settings → AI agent access. Send it as Authorization: Bearer mr_live_….
No. API keys cannot create, list, or revoke keys. The owner manages them with their signed-in dashboard session.
Use endpoints labeled KEY in this guide. Endpoints labeled SESSION require the owner's browser session.
Type to search the whole guide — Enter opens the first result · Esc closes
Socials · icons · integrations
Two icon systems + three booking/payment integrations. Use ids verbatim — unknown ids fall back to a generic glyph.
profile.socials[]: {id, label, href, visible})mail, link, instagram, youtube, tiktok, x, github, substack, spotify, linkedin, whatsapp, telegram, vk, myreach, phone, telbrandIcon)""(auto from URL),favicon,x, youtube, substack, github, instagram, linkedin, tiktok, telegram, whatsapp, spotify/api/link/metaimage wins if set asimage).icon)kid, tutor, group-kids, group-adults, exam→assets/card-icons/*.svg; teach tiles; elsetileglyph/emojiimage: nullforces the icon path; any URL forces the image.calendly{…}+ per-cardcalendlyUrl(booking cards + featured)payments{enabled, provider, currency}via the page storeemailcards →POST /api/leadGET /api/lead. Funnels post withsrc.