ZapSites: how the flow fits together
AI-generated landing pages a CMR customer can put live on their own domain. This page is the map: read it before you start wiring up calls, the endpoint docs themselves cover request/response detail.The two lifecycles#
Everything here splits into two separate state machines that happen to share one id.1.
The page itself. You scan a URL, create a ZapSite from it, and keep iterating on its copy (regenerate, rollback, preview) until it's ready. None of this touches DNS or goes live anywhere. 2. A deployment. Once the page is ready, you deploy it to one or more of the customer's own domains. Each domain gets its own status, so one ZapSite can be live on several domains at once, each independently.
1. Scan, then create#
Start with POST /zap-sites/scan. You hand it a URL (usually the customer's own product page) and get back title, headings, button copy, brand colors, logo and an AI-suggested audience and CTA label. Nothing is created yet, this is pure extraction.Feed that straight into POST /zap-sites as the scan field, along with destination_url and whatever of name / cta_label / audience / theme / campaign_context you want to set. The response comes back 202-style with status: "GENERATING" immediately. Poll GET /zap-sites/:id until status flips to LIVE (or FAILED, check lastError) before you try to preview or deploy it.2. Iterate on the copy#
Three ways to change what the page says, all of which work even if the site is already deployed and LIVE, no redeploy needed, visitors just see the new copy the moment it's ready:PATCH /zap-sites/:id: direct edits, name / cta_label / theme / campaign_context plus CMR-only fields (notify_email, slack_webhook, form_fields) that never leave CMR. POST /zap-sites/:id/regenerate: re-runs AI generation with optional notes to steer it ("make the headline shorter"). Treat notes as a strong steer, not a literal find-and-replace, the AI can paraphrase. 3. Deploy to a domain#
The page needs to have finished generating (it needs copy), or you get a 400 telling you to generate it first.
Every domain in the list needs domain protection ACTIVE. This is checked all-or-nothing before anything is queued, if even one domain lacks it, the whole call 400s and nothing happens. Check/enable domain protection through the domain-protection endpoints first.
Domains that pass both checks get queued individually and go:QUEUED -> PROCESSING -> LIVE (or FAILED)
While a deployment is PROCESSING, any existing forwarding on the domain is cleared, the @/www edge A records are written, and the SSL certificate is issued, none of that needs polling step by step. Just watch for LIVE or FAILED. A FAILED deployment always carries lastError explaining why (a common one: the customer already has their own @/www A, CNAME or ALIAS record on the domain, so it's refused rather than silently overwritten).Poll GET /zap-sites/:id and watch deployments[].status the same way you'd poll for generation. A domain that isn't ACTIVE (wrong domain status) or is already serving a deployment doesn't error the whole request, it just shows up in the response's skipped array with a reason, while every other domain in the same call still queues normally.4. Managing a live deployment#
Once a deployment exists, two routes act on it directly (by its own deploymentId, not the ZapSite id):DELETE /zap-sites/:id/deployments/:deploymentId: takes the page down from that one domain, upstream detach plus removing the edge A records this deployment wrote (and only those, matched by edge IP). The domain's prior forwarding is not automatically restored. The ZapSite and its other deployments on other domains are untouched. 5. Leads#
The on-page form posts leads back through the upstream service to CMR, which relays the alert (email and/or Slack) under the partner's own brand, never the upstream service's name.GET /zap-sites/:id/leads: every captured lead, paginated, newest first. Test alerts are excluded unless you pass includeTests=true. POST /zap-sites/:id/test-alert: fires one test lead through the real pipeline so you can confirm notify_email / slack_webhook are actually wired up correctly before relying on them. Cleanup#
DELETE /zap-sites/:id tears the whole thing down: detaches every deployment upstream, queues DNS cleanup for each one, then deletes the ZapSite. Not reversible, no undo here.Rate limits#
Two separate caps, both per CMR user (not shared across users, unlike the upstream service's own limits which are shared across all CMR customers):Scanning (POST /zap-sites/scan): a daily cap that grows with the customer, not a flat number. 5 + (1 per domain they currently have domain protection ACTIVE on). No protected domains gets you 5 scans a day, 10 protected domains gets you 15. The window resets about 24 hours after your first scan of the day, not at midnight. Both return a 429 with "Too many requests. Please try again later." when exceeded.Ids, at a glance#
A ZapSite id always starts with SITE (e.g. SITECCHMN86W734P8PMM3E53EW82HCC2). It's opaque, don't try to decode it, and a raw UUID in its place is rejected with a 422.
A deployment id is a plain UUID, scoped under its ZapSite (/:id/deployments/:deploymentId).
Every route needs userId as a query param, the CMR user the ZapSite belongs to. A ZapSite is only ever visible to the user who owns it, even other users on the same partner account can't see it.
Modified at 2026-10-26 10:06:21