Cohesive client-aware UX across the agency surface. Tier 2 (audiences) and Tier 3 (campaigns / briefs / contracts / margin / restrictions) all ship together.
What's now client-aware
| Entity | Tier | What ships | |---|---|---| | Marketplace bids | 1 (already) | Picker on bid create + filter + badge on /marketplace/bids | | Marketplace deals | 1 + 3 | Filter + badge on /marketplace/deals. Margin columns client_charge_amount + creator_payout_amount on marketplace_deals | | Audience segments / custom / lookalikes / suppression | 2 | Picker in all 4 modals + filter bar on /audiences + client badge on segment cards | | Campaigns | 3 | client_id column, picker in CampaignWizard basics step, filter on /campaigns | | Briefs | 3 | client_id column (UI surfacing pending) | | Contracts | 3 | client_id column (UI surfacing pending) | | Workspace reviews | 3 | client_id propagated from deal on review create | | Restrictions enforcement | 5 (semantic fix) | restrictions_guard.check_restriction_conflict() now prefers client_brand.restrictions['brand_category'] over the agency workspace's brand_category when advertiser_client_id is provided. Fixes Brand A vs Brand B cross-bleed. |
Schema (migration `9c0d1e2f3a4b`)
client_id(FK toclient_brandsON DELETE SET NULL) added to:campaigns,briefs,contracts,workspace_reviews.marketplace_deals.client_charge_amount+marketplace_deals.creator_payout_amount(Numeric(15,4), nullable). Margin =client_charge_amount - creator_payout_amount.
Backend
/marketplace/bidsand/marketplace/dealsaccept?client_id=<uuid>or?client_id=unattributedfilter./campaignsaccepts the same filter.- All 4 audience list endpoints accept the filter (Tier 2 already wired).
- Campaign create accepts and validates
client_idviavalidate_client_for_agency()helper. WorkspaceReviewcreate propagatesdeal.client_idautomatically — reviews carry the same client tag as the deal they came from.
Frontend
- New shared component:
<AgencyClientFilter>— a single dropdown with All / Unattributed / per-client. Renders only forAGENCY_ADMINwith ≥1 active client. Wired on bids, deals, campaigns. Hooks:useAgencyClients(). <ClientBadge>chip component for inline display.- CampaignWizard (basics step) gets a "Client (optional)" picker.
- Existing audiences page already had its own picker — unchanged.
Known limits (intentional)
- Briefs and contracts have the column but no UI picker yet — to add when those modules get the next pass.
- Margin tracking has the columns; deal create form still uses single
negotiated_price. Margin UI follows in v3 once we know if agencies want to enter both numbers at create time or capture only at completion. - Per-client team-member scoping, multi-tier approvals (
PENDING_CLIENT_APPROVAL), and per-client favorites still deferred.
---

