Skip to content

feat(brands): self-serve activate-brand endpoint | LLMO-5605#2706

Open
igor-grubic wants to merge 3 commits into
mainfrom
LLMO-5605-api-self-serve-activate-brand-endpoint-piece-2-activate-optional-prompts-schedul
Open

feat(brands): self-serve activate-brand endpoint | LLMO-5605#2706
igor-grubic wants to merge 3 commits into
mainfrom
LLMO-5605-api-self-serve-activate-brand-endpoint-piece-2-activate-optional-prompts-schedul

Conversation

@igor-grubic

@igor-grubic igor-grubic commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

1. Abstract

Adds POST /v2/orgs/{spaceCatId}/brands/{brandId}/activate — the explicit, customer-triggered "make this brand live" action: resolves the onboarded primary site, sets the brand active with baseSiteId in the same write, optionally generates AI prompts, and ensures the recurring brand-presence schedule. LLMO-5605, Piece 2.

2. Reasoning

Piece 2 of self-serve domain onboarding (LLMO-3749 / LLMO-5605). After Piece 1 stands up the site entity, the customer needs an explicit action to activate a (e.g. pending) brand whose primary URL is already onboarded — optionally kicking off AI-prompt generation and the recurring brand-presence schedule.

3. High-level overview of the changes

New endpoint POST /v2/orgs/{spaceCatId}/brands/{brandId}/activate. For an existing (e.g. pending) brand whose primary URL is already onboarded:

  1. Resolve the onboarded primary Site → set the brand active with baseSiteId in the same write (400 if no onboarded site; 409 on brands_base_site_unique).
  2. If generatePrompts: submit a direct prompt_generation_base_url DRS job with source: 'brand-activation' (never Brandalf, never the llmo-customer-analysis cascade). Idempotent via an in-flight listJobs check.
  3. Ensure the recurring brand-presence schedule only when prompts exist / are generated, via the shared createBrandPresenceSchedule helper (POST + tolerate 409).
  4. Returns 200 quickly ({ brandId, status:'active', baseSiteId, promptGenerationJobId?, scheduleId? }); prompts generate async.
  • Auth: membership (hasAccess) + explicit PAID entitlement (TierClient), mirroring Piece 1 — no admin requirement (decision: don't 403 paying non-admin members).
  • OpenAPI specs (api.yaml / brands-v2-api.yaml / schemas.yaml) + generated docs updated; bumps @adobe/spacecat-shared-drs-client → 1.13.0.

4. Required information

5. Affected / used mysticat-workspace projects

7. Test plan

(a) Local — unit coverage for: same-write activate (200); generatePrompts: true → one prompt job + one schedule; false / no-prompts → neither; double-click idempotency; the 400 / 403 / 404 / 409 paths; and DRS-not-configured. Integration tests (test/it/) for the endpoint are still outstanding — CLAUDE.md requires IT for new endpoints; this is a pre-merge item.

(b) Per-env (dev / stage): activate a pending brand whose primary URL is onboarded → confirm status:'active' with baseSiteId set in one write; with generatePrompts:true confirm a single source:'brand-activation' prompt job and one brand-presence schedule (re-activate is idempotent — listJobs dedup + schedule 409 tolerated); with generatePrompts:false confirm neither is created; confirm a non-PAID or non-member caller is rejected.

8. Deployment & merge order

Final consumer in the LLMO-5605 Piece 2 chain.

Ordered sequence: drs-client 1.13.0 (done) + DRS source filter deployed → land after the in-repo /status + demotion-guard PRs → merge this endpoint; the audit-worker migration lands in parallel.

🤖 Generated with Claude Code

POST /v2/orgs/:spaceCatId/brands/:brandId/activate — explicit, customer-triggered
"make this brand live": resolve the brand's onboarded primary site, set it active
with baseSiteId in one write, optionally generate AI prompts (a direct
prompt_generation_base_url job, source 'brand-activation' — never Brandalf, never the
llmo-customer-analysis cascade), and ensure the recurring brand-presence schedule
(prompts-gated, via the shared createBrandPresenceSchedule helper).

- Auth: membership + PAID entitlement (no admin requirement, mirrors Piece 1).
- Idempotent: in-flight listJobs check before prompt-gen; schedule POST tolerates 409.
- Returns 200 quickly (prompts generate async). 400/403/404/409 per the contract.
- OpenAPI path + schemas; 24 unit tests; bumps spacecat-shared-drs-client to 1.13.0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@MysticatBot

Copy link
Copy Markdown

Mysticat review failed: Claude CLI crashed (exit 1): stderr= stdout={"type":"result","subtype":"error_max_budget_usd","duration_ms":5,"duration_api_ms":1694089,"is_error":true,"num_turns":1,"stop_reason":null,"session_id":"6122fae3-68a9-48bb-90ae-a503adffa0c8","total_cost_usd":12.047866350000003,"usage":{"input_tokens":0,"cache_creation_input_tokens":0,"cache_read_i

@igor-grubic igor-grubic requested review from MysticatBot and removed request for MysticatBot June 26, 2026 12:55
@MysticatBot

Copy link
Copy Markdown

Mysticat review failed: Claude CLI crashed (exit 1): stderr= stdout={"type":"result","subtype":"error_max_budget_usd","duration_ms":3,"duration_api_ms":1799198,"is_error":true,"num_turns":1,"stop_reason":null,"session_id":"637454f6-7c99-43b2-8e09-a0d743e3fdce","total_cost_usd":11.398289300000004,"usage":{"input_tokens":0,"cache_creation_input_tokens":0,"cache_read_i

…ry test | LLMO-5605

CI build (full suite) caught two route-registry assertions the new endpoint must
satisfy:
- required-capabilities.js: map POST /v2/orgs/:spaceCatId/brands/:brandId/activate to
  organization:write (same as the sibling brand-write routes) so it isn't flagged as
  an unclassified route.
- test/routes/index.test.js: add the route to the expected static/dynamic route set.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

This PR will trigger a minor release when merged.

@codecov

codecov Bot commented Jun 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

…on) | LLMO-5605

Resolve conflicts from main's brand work (status-transition endpoint, demotion
guards, primary-URL changes, and @ts-check enabled on brands.js):
- brands.js: keep BOTH activateBrandForOrg (this PR) and transitionBrandStatusForOrg
  (main); reconcile imports (BrandGovernanceClient, metrics-emf).
- Move the PAID-entitlement check to src/support/llmo-paid-gate.js so the now
  @ts-check'd brands.js need not import Entitlement (absent from the package .d.ts).
- activate: pass audience explicitly, use createResponse for the object body, and
  truthy spreads — to satisfy tsc --checkJs.
- OpenAPI: keep both /activate and /status path blocks; regenerate docs/index.html.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants