Zeq Edge · API Reference
All endpoints under /api/cf/*. Authentication: X-Zeq-SM-Key: zsm_… with admin scope for write operations, read scope for GET.
Content-Type: application/json on every body. Errors return { error: "<code>" } with a 4xx/5xx status.
Sites
POST /api/cf/sites
Register a hostname.
Body
{
hostname: string, // FQDN; ICANN syntax
origin_url: string, // https://… (urlGuard pre-flight)
plan_tier?: "free" | "pro" | "business",
insecure_origin_allowed?: boolean // Pro+ tier; default false
}
Returns 201
{
ok: true,
id: string, // UUID
edge_id: string, // 16 hex chars
hostname: string,
plan_tier: PlanTier,
machine_slug: string,
dns_instructions: {
cname_target: string,
edge_id: string,
hostname: string,
message: string // copy-pasteable
},
status: "pending_cname",
free_tier_daily_cap: number | null // 100000 on free tier; null on paid
}
Returns 200 + already_registered: true if the same owner re-registers the same hostname (idempotent).
Returns 409 hostname_already_registered if owned by someone else.
GET /api/cf/sites
List all sites belonging to the caller's state machine.
Returns
{
ok: true,
machine_slug: string,
sites: CfSite[]
}
GET /api/cf/sites/:id
Single-site detail with current rules + 24-window recent stats summary + DNS instructions.
Rules
POST /api/cf/sites/:id/rules
Body
{
kind: "page_rule" | "waf" | "redirect",
match_pattern: string, // path prefix; "*" allowed for waf
action_json: Record<string, unknown>, // kind-specific (see rule-engine.md)
priority: number, // 0..100000; lower wins; UNIQUE per site
enabled?: boolean // default true
}
UPSERT semantics on (site_id, priority) — re-posting the same priority replaces the rule.
DELETE /api/cf/sites/:id/rules/:rule_id
Returns { ok: true, deleted: "<rule_id>" } on success, 404 on miss.
Cache
POST /api/cf/sites/:id/purge
Body
{ paths: string[] } // each path starts with "/"; max 2 KiB
// or
{ all: true }
Returns { ok: true, purged: number } — count of cache keys evicted.
Analytics
GET /api/cf/sites/:id/analytics?window=60z|24h|7d
Returns
{
ok: true,
window: "60z" | "24h" | "7d",
window_zeqonds: number, // 60 / 111197 / 778383
aggregate_window_zeqonds: 60, // always — bucket size
summary: {
requests: number,
cache_hits: number,
cache_misses: number,
blocked_threats: number,
bytes_egress: number,
cache_hit_ratio: number // 0..1
},
buckets: Array<{
window_start_zeqond: bigint,
requests: bigint,
cache_hits: bigint,
cache_misses: bigint,
blocked_threats: bigint,
bytes_egress: bigint
}>
}
Buckets are returned in ascending window_start_zeqond order — first entry is the oldest in-window.
Error codes
| HTTP | error | Meaning |
|---|---|---|
| 400 | invalid_hostname | Hostname doesn't pass ICANN regex. |
| 400 | invalid_origin_url | SSRF guard rejected the origin (private/IMDS/non-https). |
| 400 | invalid_plan_tier | Unknown plan. |
| 400 | invalid_kind | Rule kind not in the allowed set. |
| 400 | invalid_match_pattern | Pattern empty or > 512 chars. |
| 400 | invalid_priority | Not an integer in [0, 100000]. |
| 400 | invalid_rule_id | Rule id not a UUID. |
| 400 | no_paths_or_all_flag | Purge with neither paths nor all: true. |
| 401 | missing_api_key | No X-Zeq-SM-Key header. |
| 401 | invalid_api_key | Key not found / revoked. |
| 403 | insufficient_scope | Key valid but lacks admin (writes) or read scope. |
| 403 | state_machine_not_found | Caller's key references a missing machine. |
| 403 | not_owner | Site exists but belongs to a different machine. |
| 404 | site_not_found | Site id unknown or soft-deleted. |
| 404 | rule_not_found | Rule id unknown. |
| 409 | hostname_already_registered | Hostname owned by someone else. |
| 500 | internal_error | Server crash — see logger for stack. |