Expert portal
What experts can do today, and exactly what is missing.
The expert portal has no backend yet
The expert tables exist in the database (content.blog_authors, content.blog_author_credentials, content.expert_reward_*, coupon.coupon_redemption_items) but no service exposes an expert-facing endpoint over them. No endpoint maps the signed-in user to a content.blog_authors row. content.blog_authors.user_id exists and is UNIQUE, but no service reads it, so the portal cannot determine which expert is looking at it — and therefore cannot scope anything to them.
Nothing on this page is sample data: rather than show invented articles, attributed sales, or reward balances, the portal shows none and names the endpoint each one needs.
Where the work happens today
LiveEveryone who can reach this portal is staff — the route guard requires employee standing and a BACKOFFICE_EXPERT_ROLES role. Expert content is therefore authored and reviewed through the admin surfaces below, by an operator, on the expert’s behalf. Each still needs its own admin role.
Blog editor
The live content-service editor: draft, edit, submit for review, decide reviews, and publish. Requires a ContentAdmin allowlisted operator account.
Expert administration
Expert authorship (posts per author) and the expert approval queue — the only two expert reads that have a backend. Requires an admin operator account.
The one contract that unblocks most of this
Almost every capability below is blocked on the same thing — resolving the signed-in principal to an expert. content.blog_authors.user_id already exists and is UNIQUE; nothing reads it.
GET /admin/content/experts/me → { author_id, display_name, slug, professional_title, status, verified_at, reward_enabled }
Deferred — no endpoint exists
6 capabilitiesEach card names what an expert cannot do and the exact endpoint that has to exist first. Nothing here is clickable, because there is nothing to call. Where a card is marked data exists, the underlying rows are already being written and only the read is missing.
My profile & credentials
DeferredAn expert cannot view or edit their own byline, professional title, bio, or credentials. No endpoint maps the signed-in user to a content.blog_authors row. content.blog_authors.user_id exists and is UNIQUE, but no service reads it, so the portal cannot determine which expert is looking at it — and therefore cannot scope anything to them.
Missing endpoints
- GET /admin/content/experts/me → { author_id, display_name, slug, professional_title, status, verified_at, reward_enabled }
- PATCH /admin/content/experts/me → display_name, professional_title, bio_html, avatar, social links
- GET /admin/content/experts/me/credentials → the expert's own degrees/licences + verification state
Would be backed by
My blog drafts
DeferredAn expert cannot see or write their own articles. content-service's admin post list DOES accept an `author_id` filter, so the read itself is nearly there — but without the identity mapping above the portal cannot supply the right author_id, and the existing admin editor is gated by ContentAdmin (an operator allowlist), not by authorship.
Missing endpoints
- GET /admin/content/experts/me → { author_id, display_name, slug, professional_title, status, verified_at, reward_enabled }
- An expert-scoped authorization mode on /admin/content/posts/* that permits the AUTHOR of a post (not only a ContentAdmin operator) to read and edit their own drafts
Would be backed by
Submit an article for review
Data existsDeferredThe editorial and medical review workflow itself is now REAL (P9-12: submit → approve / request-changes / reject, each writing an attributable content.blog_post_reviews decision, with self-review refused and medical sign-off requiring a verified expert). What is missing is only the expert-facing door to it: the transitions are gated by ContentAdmin, so an expert cannot submit their own draft.
Missing endpoint
- POST /admin/content/posts/{id}/submit-review authorized for the post's AUTHOR, not only a ContentAdmin operator
Would be backed by
Request a coupon
Deferredcoupon.coupons carries expert_author_id, requested_by_user_id and a draft → pending_approval → approved lifecycle, and approval-service already accepts the `expert_coupon` action type. Nothing produces such a request, and no endpoint lets a non-operator create a coupon, so an expert cannot ask for one.
Missing endpoint
- POST /admin/coupons/requests (expert-authenticated) → creates a draft coupon with expert_author_id + requested_by_user_id set, then an approval-service `expert_coupon` request
Would be backed by
My attributed sales
Data existsDeferredThe DATA now exists: as of P9-05, checkout writes coupon.coupon_redemptions with expert_author_id, plus one coupon.coupon_redemption_items row per attributed order line (product, variant, quantity, item subtotal, discount, attributed sale). What is missing is a read: no service queries either table, so neither an expert nor an operator can see the totals. Nothing here should be shown until that endpoint exists — the previous portal displayed fixture numbers.
Missing endpoint
- GET /admin/coupons/attribution?expert_author_id&from&to → per-expert attributed orders / sales / discount totals, and the per-product breakdown
Would be backed by
My rewards & payouts
Deferredcontent.expert_reward_rules / expert_reward_ledger / expert_reward_payouts exist but no accrual ever runs and no endpoint reads them, so there is no reward balance to show. (payment-service /rewards/* is the CUSTOMER loyalty wallet — a different domain entirely; it must not be reused to imply expert earnings.)
Missing endpoints
- A reward accrual worker over content.expert_reward_rules
- GET /admin/content/experts/me/rewards → ledger + balance by status
- GET /admin/content/experts/me/payouts → payout history