Skip to main content

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

HTTPerrorMeaning
400invalid_hostnameHostname doesn't pass ICANN regex.
400invalid_origin_urlSSRF guard rejected the origin (private/IMDS/non-https).
400invalid_plan_tierUnknown plan.
400invalid_kindRule kind not in the allowed set.
400invalid_match_patternPattern empty or > 512 chars.
400invalid_priorityNot an integer in [0, 100000].
400invalid_rule_idRule id not a UUID.
400no_paths_or_all_flagPurge with neither paths nor all: true.
401missing_api_keyNo X-Zeq-SM-Key header.
401invalid_api_keyKey not found / revoked.
403insufficient_scopeKey valid but lacks admin (writes) or read scope.
403state_machine_not_foundCaller's key references a missing machine.
403not_ownerSite exists but belongs to a different machine.
404site_not_foundSite id unknown or soft-deleted.
404rule_not_foundRule id unknown.
409hostname_already_registeredHostname owned by someone else.
500internal_errorServer crash — see logger for stack.