PATCH /v1/sites/:id/research-draft
Save partial edits to a site’s research payload without advancing into generation. Use this to checkpoint operator edits between separate review sessions — every field round-trips, so the next GET /v1/sites/:id/research reflects what was saved. The site stays in research_review until you POST to /v1/sites/:id/approve-research.
Cost: Free.
Request
curl -X PATCH https://api.warpweb.ai/v1/sites/8f3c2a1b/research-draft \
-H "Authorization: Bearer wwk_<your-key>" \
-H "Content-Type: application/json" \
-d '{
"services": [
{ "name": "Drain cleaning", "description": "Hydro-jetting and cabling." }
],
"brandColors": { "primary": "#0F4C81" }
}'An empty {} is valid — it bumps updated_at and returns 200, useful as a write-side keepalive from a UI.
Body
The same field-level edits as POST /v1/sites/:id/approve-research, minus the two side-effecting fields:
| Field | Type | Description |
|---|---|---|
services | array | Replace the services list. { name, description?, price? }. |
testimonials | array | Replace the testimonials list. { quote, author?, rating?, source? }. |
faqs | array | Replace the FAQ list. { question, answer }. |
hours | object | Weekday → hour string. Replaces wholesale. |
brandColors | object | { primary?, secondary?, accent?, personality? }. Omitted slots preserve existing values. |
existingSiteUrl | string | Update the recorded existing-site URL. |
ownerPrompt | string | Voice / tone direction. |
businessDescription | string | One-sentence pitch. |
designStyle | string | Override the design style. Unknown values are silently ignored. |
allowStockPhotos | boolean | Toggle the stock-photo (Pexels) layer. Default false (set at create time). Set true for generic stock fallback in slots that uploads + Google Places didn’t cover; false keeps those slots as Lucide-icon-over-brand-color sections. |
contentPlan | object | Partial overrides to the AI-generated content plan. Shallow-merged. |
Not accepted here: excludeImages and uploadedPhotos. Both perform filesystem side effects (deleting from staging, downloading remote URLs) that don’t belong in a non-advancing checkpoint. Pass them on approve-research when you’re ready to build.
Response
{
"success": true,
"saved_at": "2026-05-23T18:42:17.314Z"
}saved_at is the new updated_at on the site row — useful as an idempotency key when a UI wants to dedupe overlapping saves.
Errors
| Status | Body | Cause |
|---|---|---|
| 400 | { "error": "Site is not in research review phase" } | Drafts are only writable while the site is paused in research_review. Once generation starts, edits must go through POST /v1/sites/:id/revisions instead. |
| 400 | { "error": "No research data found" } | The site exists but research_data is empty. Should not happen for sites that reached research_review. |
| 404 | { "error": "Site not found" } | No site with that ID belongs to your account. |
Related
GET /v1/sites/:id/research— read back the saved draft.POST /v1/sites/:id/approve-research— commit + advance to generation.