Deploy Safely
Every destructive operation on a SpiderIQ project runs through a two-step preview → confirm flow. No more accidental deletions. No more "I thought I was on staging." No more wrong-tenant publishes.
The flow
- Preview — call the endpoint with
?dry_run=true. Nothing mutates. You get back a description of what the change would do, plus a one-timeconfirm_token(valid 7 days). - Confirm — call the same endpoint with
?confirm_token=cft_TOKENwhereTOKENis from step 1. The token consumes (single-use) and the mutation runs.
The MCP tools default to preview mode. The CLI prompts interactively by default. Agents have to explicitly opt into mutating.
Which operations are gated?
| Operation | Endpoint |
|---|---|
| Delete page | DELETE /dashboard/projects/{pid}/content/pages/{page_id} |
| Publish page | POST /dashboard/projects/{pid}/content/pages/{page_id}/publish |
| Unpublish page | POST /dashboard/projects/{pid}/content/pages/{page_id}/unpublish |
| Update settings | PATCH /dashboard/projects/{pid}/content/settings |
| Apply theme | POST /dashboard/projects/{pid}/templates/apply-theme |
| Delete component | DELETE /dashboard/projects/{pid}/content/components/{id} |
| Publish component | POST /dashboard/projects/{pid}/content/components/{id}/publish |
| Archive component | POST /dashboard/projects/{pid}/content/components/{id}/archive |
| Deploy site (preview) | POST /dashboard/projects/{pid}/content/deploy/preview |
| Deploy site (production) | POST /dashboard/projects/{pid}/content/deploy/production |
Deploying a site
Deploy is the canonical two-step flow:
Step 1 — Preview
curl -X POST \
-H "Authorization: Bearer $SPIDERIQ_PAT" \
"https://spideriq.ai/api/v1/dashboard/projects/$PID/content/deploy/preview"
Response:
{
"project": "SMS Chemicals",
"preview_url": "https://preview-ov5f-c3b75c9b.sites.spideriq.ai",
"diff_summary": "3 pages modified, 1 new blog post, theme unchanged",
"confirm_token": "cft_2m9xkl3p7r4v5w8q6a1b2c3d4e5f6g7h",
"expires_at": "2026-04-21T12:00:00Z",
"snapshot_hash": "8f2c91a0b3d4e5f6..."
}
Open preview_url in a browser. You're looking at the exact snapshot that would go live — same templates, same content, same settings — served from a staging Cloudflare KV. No DNS flip yet.
Step 2 — Confirm
Review, then:
curl -X POST \
-H "Authorization: Bearer $SPIDERIQ_PAT" \
"https://spideriq.ai/api/v1/dashboard/projects/$PID/content/deploy/production?confirm_token=cft_2m9xkl3p7r4v5w8q6a1b2c3d4e5f6g7h"
Response:
{
"project": "SMS Chemicals",
"status": "live",
"version_id": 48,
"deployed_at": "2026-04-14T12:34:56Z"
}
The preview token consumes. The real deploy runs. Your site goes live at the mapped custom domain.
CLI flow
The CLI wraps the dance for you:
# Interactive (default): preview → table diff → [y/N] → production
spideriq content deploy
# Non-interactive (CI/agent):
spideriq content deploy --json
# emits the preview envelope; then:
spideriq content deploy --confirm cft_2m9xkl3p…
# Skip preview entirely (legacy mode; audit event emitted):
spideriq content deploy --yolo
For other destructive commands:
spideriq content components delete abc-123 --dry-run # returns confirm_token
spideriq content components delete abc-123 --confirm cft_…
spideriq templates apply-theme default --dry-run
MCP flow
Destructive MCP tools default to dry_run=true when neither flag is passed. Agents get a preview + token on the first call; they must call again with confirm_token to actually mutate.
content_publish_page({ page_id: "abc-123" })
# → { dry_run: true, preview: {...}, confirm_token: "cft_..." }
content_publish_page({ page_id: "abc-123", confirm_token: "cft_..." })
# → the real publish result
For the deploy flow specifically, use the split tools:
content_deploy_site_preview()
# → { preview_url, confirm_token, preview: {...} }
content_deploy_site_production({ confirm_token: "cft_..." })
# → the real deploy
Error responses
| Status | Reason | When |
|---|---|---|
| 403 | TokenInvalid | Token string doesn't exist in DB |
| 403 | TokenClientMismatch | Token belongs to a different project |
| 403 | TokenActionMismatch | Token was issued for a different action |
| 403 | TokenResourceMismatch | Token was issued for a different resource |
| 409 | TokenConsumed | Token was already used once (single-use) |
| 410 | TokenExpired | Past expires_at (default 7 days) |
On 410, just call the preview endpoint again to get a fresh token. The snapshot will reflect current state.
What the preview captures
The preview envelope's preview field tells you exactly what will change — no surprises:
Page publish:
{
"action": "publish_page",
"page_id": "abc-123",
"slug": "pricing",
"title": "Pricing",
"current_status": "draft",
"will_become": "published"
}
Settings update:
{
"action": "update_settings",
"fields_changing": ["site_name", "primary_color"],
"current": { "site_name": "SMS", "primary_color": "#FF0000" },
"proposed": { "site_name": "SMS Chemicals", "primary_color": "#0088FF" }
}
Apply theme:
{
"action": "apply_theme",
"theme_name": "default",
"file_count": 29,
"files": ["layout/theme.liquid", "sections/header.liquid", ...],
"warning": "Applying a theme overwrites any per-file template edits for these paths."
}
A snapshot_hash (SHA-256) is stored on the token. Future tightening will reject a confirm if the underlying DB state has drifted since the preview — for now it's informational.
CI / automation
For automation where interactive confirmation isn't possible:
- Preferred: two-step with explicit
confirm_token— you still get a diff in the logs, and the token proves the preview ran. - Escape hatch:
--yolo(CLI) ordry_run: falsewithoutconfirm_token(MCP/API). Works, but every call writes an audit event tocontent_tenant_auditflagged as bypassed. Reserve this for trusted CI jobs.
Why this exists
Stage 0 of Phase 11+12 was triggered by an incident: an agent running in one Antigravity window picked up a token issued for a different client and overwrote a production site with the wrong content. The preview → confirm flow (Lock 4) is the last line of defense — even if Locks 1, 2, 3, and 5 somehow all failed simultaneously, the operator still has to eyeball the preview URL and type "y" (or copy the cft_… token) before anything mutates.
See also
- Session Binding — how the CLI/MCP picks the right project automatically (Lock 3)
- AI Agent Integration Guide — full MCP/CLI tool reference