Re-Synthesize Opportunities
Re-run opportunity synthesis and scoring from existing families + evidence, without re-crawling or re-calling DataForSEO.
Re-runs the opportunity engine synthesis layer on top of existing families and evidence. Does not re-crawl the brand, re-collect SERPs, or re-pull DataForSEO keywords — it reads everything from the database and re-runs the LLM synthesis + deterministic scoring.
When to use this: after updating brand context, changing competitors, tuning synthesis prompts, or when you want to refresh opportunity rankings without the cost of a full discovery.
Duration: ~30 seconds (vs ~14 minutes for full discovery).
Path parameters
brand_idstringrequiredThe brand UUID.
Request body
None.
Response (202 Accepted)
{
"run_id": "run-uuid",
"status": "queued"
}
The run is created with kind synthesize. Poll GET /brands/{brand_id}/runs/{run_id} until status is done, then fetch the refreshed opportunities via GET /brands/{brand_id}/opportunities.
Example
curl -X POST \
-H "X-API-Key: $BM_API_KEY" \
https://api.boringmarketing.com/brands/$BRAND_ID/synthesize
Synthesize reads families, keywords, and all evidence types (search, LLM, technical) from the database. If any of these are empty, run POST /brands/{id}/discover first. If you want to feed refreshed technical or LLM evidence into the synthesis, run the corresponding /enrich/* endpoint before synthesize.
Errors
| Status | Meaning |
|---|---|
404 | Brand not found, or not owned by the caller. |
409 | Returned when (a) another synthesize run is already active for this brand, or (b) a full (/analyze) run is currently writing to all tables. Other partial runs do not block this endpoint. Response carries an X-Running-Run-Id header with the existing run's UUID. Active runs older than 15 minutes are auto-expired before the conflict check. |
422 | brand_id is not a valid UUID. |