Opportunities

Create a brief from an opportunity

Accept a gap opportunity: mint a content brief from it and attach the brief to the row. Requires the write:opportunities scope AND an API key linked to a user (the linked user becomes the brief's created_by); an unlinked key is refused 403 before any lookup, so it never learns whether the id exists.

Only gap_ ids are accepted. prog_ ids return 400 (programmatic opportunities use batch generation) and decay_ ids return 400 (use the update action). A gap_ id whose suffix is not a valid UUID returns 400.

Ordering is load-bearing and observable. The opportunity is fetched first, so a 404 costs nothing and never mutates the row. The monthly brief allowance is checked BEFORE the row is claimed, so an at-cap organization is refused with the row untouched rather than left stranded at in_progress. Only then is the row claimed with a conditional UPDATE to status = in_progress; that single statement is also the closed-check and the concurrency check, so a dismissed/completed row, a row that already carries a suggested_brief_id, and a row another live accept is holding all lose it and get 409. Any failure after the claim releases it and restores the prior status.

Billing and metering: one accepted call consumes one brief from the organization's monthly brief allowance (calendar-month window; imported briefs and regeneration forks do not count). When the allowance is exhausted the call returns 429 with code brief_limit_exceeded and a truthful Retry-After giving the seconds until the monthly window rolls - this is a QUOTA, not a burst limit, so retrying before that instant cannot succeed and an SDK's default backoff must not re-hammer it. When the organization's subscription is canceled, paused, past due beyond grace, or its trial has expired, the call returns 403 with code subscription_inactive instead. A topic matching one of the organization's never-write-about terms returns 422 and no quota is consumed.

Success is 201 and asynchronous: the brief row exists immediately with status pending, and outline generation plus full research run in the background. Poll GET /api/v1/briefs/{brief_id} (or /briefs/{brief_id}/status) for readiness. The brief inherits the opportunity's cluster (cluster_id) and site, and its relevance bucket rides along - anything but confident is held for review at dispatch rather than auto-published.

post/opportunities/{id}/brief

Request

  • Base URL: https://next.frase.io/api/v1
  • URL: https://next.frase.io/api/v1/opportunities/{id}/brief
  • Auth: API key in header X-API-KEY

Path parameters

idstring required
Example:gap_3f1c2b8e-9a4d-4e17-8b2f-0c5d6e7a8b90

Gap opportunity ID (gap_UUID). Other prefixes are rejected with 400.

Response

Brief creation started

Changes

No recorded changes to this operation across all 1 revision of this API.