Re-Synthesize Opportunities

Re-run opportunity synthesis and scoring from existing families + evidence, without re-crawling or re-calling DataForSEO.

POSThttps://api.boringmarketing.com/brands/{brand_id}/synthesize

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_idstringrequired

The 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

StatusMeaning
404Brand not found, or not owned by the caller.
409Returned 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.
422brand_id is not a valid UUID.