Skip to content
arch v1.0.0

Verifluence — Implemented Use Cases

Documents the currently working use cases derived from API handlers, DB schema, and frontend pages. Last updated: June 2026.


Actors

ActorDescription
PublicUnauthenticated visitor
StreamerAuthenticated content creator
OperatorAuthenticated casino / sportsbook company
AdminPlatform administrator (managed via separate admin app + impersonation tokens)

UC-1 · Operator Onboarding

UC-1.1 · Register via Invitation

Actor: Operator Preconditions: Admin has created an Invitation record and emailed the code to the operator.

Flow:

  1. Operator opens /entry?invitation=CODE.
  2. System validates the invitation code (status = pending).
  3. Operator accepts the Terms of Service + Privacy Policy consent. (The display name comes from the invitation — it is not entered here.)
  4. System redeems the code: creates an operators row (status = prepared), marks the invitation used, and signs the operator into the session. The display name is taken from the invitation.
  5. Operator is redirected to /operator/onboarding to complete their profile (see UC-1.4).

Postconditions: Operator account exists with status prepared, then runs the onboarding wizard. Admin must set status published to grant full access (e.g. campaign creation).

⚠️ Known gap: the ToS/Privacy consent accepted on /entry is not persisted on the operator. There is no consent column on operators; consent_version is only written to operator_wallets, and only when a wallet is later connected (UC-1.2). Redeem stamps nothing at sign-up time.

TestAssertion
not yet automated — requires admin-seeded invitations row

UC-1.2 · Connect Operator Wallet

Actor: Operator Preconditions: Operator is authenticated. MetaMask or compatible EVM wallet installed in browser.

Flow:

  1. Operator opens /operator/wallet.
  2. Operator clicks "Connect Wallet"; MetaMask prompts for signature.
  3. System records operator_wallets row with wallet address and current consent version.
  4. Multiple wallets can be connected; each is stored independently.

Postconditions: Wallet address available for on-chain campaign funding.

TestAssertion
not yet automated — on-chain / MetaMask, not API-testable

UC-1.3 · Operator Sign In

Actor: Operator Preconditions: Operator account exists.

Flow:

  1. Operator visits any /operator/* page.
  2. If not authenticated, system redirects to /signin2 (which itself redirects to the live /signin page).
  3. Operator enters registered email; system sends a 4-digit PIN via email.
  4. Operator enters PIN; session cookie is set.
  5. Operator is redirected to original destination or /operator/account.
TestAssertion
TC-AUTH-01Happy path — PIN sent and verified, session established

→ Product Guide: Registration & Onboarding · Wallet


UC-1.4 · Complete Operator Onboarding

Actor: Operator Preconditions: Operator authenticated (just redeemed an invitation via UC-1.1, or resuming a partially-completed onboarding).

Flow: A 2-step wizard at /operator/onboarding. It is resume-aware — re-entry derives the starting step from saved operator data, so closing the tab mid-flow returns the operator to where they left off.

  1. Step 1 — Company information. Company name, operator brand, website, jurisdiction, company representative, contact email → PUT /api/operators/:slug (persists legal_name, name, website_url, jurisdiction, contact_name, contact_email).
  2. Step 2 — Brand profile. Logo + cover image uploads, about, streaming categories, target regions, languages, average monthly campaigns → logo/cover upload endpoints + PUT /api/operators/:slug.
  3. On completion the operator is redirected to /operator/campaigns.

Postconditions: Operator company + brand profile populated. (An earlier payout/wallet onboarding step was removed — wallet connection is now a separate action, UC-1.2.)

TestAssertion
not yet automated

UC-2 · Campaign Management (Operator)

UC-2.1 · Create Campaign

Actor: Operator Preconditions: Operator authenticated and account status published.

Flow:

  1. Operator opens /operator/campaigns and clicks "New Campaign".
  2. Multi-step form collects:
    • Name, category (casino / sportsbook / poker / prediction_markets), description
    • Dates, timezone, schedule type + time slots
    • Budget cap (USDC), max payout per stream
    • Minimum follower, viewer, and monthly hours requirements
    • Platforms, GEOs, languages
    • Payout type (manual / automatic)
    • Minimum terms (the five negotiable deal-term floors), brief summary, deliverables, brand dos/don'ts
  3. System creates a campaigns row with status = prepared and settings JSONB.

Note: earlier drafts listed "payout model (fixed / fixed+rs), payout brackets, CPA/RS rates, contract length, termination clause, application deadline" — none of these are persisted columns. The campaign carries a payout_type (manual/automatic) and per-campaign minimum_terms floors; CPA/revshare rates live on the streamer (UC-9.3), not the campaign.

Postconditions: Campaign in Draft state. Not visible to streamers.

TestAssertion
not yet automated

UC-2.2 · Edit Campaign

Actor: Operator Preconditions: Campaign exists with status prepared.

Flow:

  1. Operator selects campaign in the list; clicks "Edit".
  2. Form is pre-populated with existing data.
  3. Operator modifies any allowed fields and saves.
  4. System issues PUT /api/campaigns/:id.

Constraints: Edit is presented in the UI only for Draft (prepared) campaigns, and this is now API-enforced. Once a campaign leaves prepared, PUT /api/campaigns/:id rejects any change to campaign terms (name, dates, budget, requirements, platforms, minimum_terms, …) with 409. Only status transitions, on-chain bookkeeping (escrow_id/network_id/funding_address) and brand assets stay editable. Funding writes happen while the campaign is still prepared, so they're unaffected; budget increases go through POST /api/campaigns/:id/top-up, not this endpoint.

TestAssertion
not yet automated

UC-2.3 · Delete Campaign

Actor: Operator Preconditions: Campaign status is prepared.

Flow:

  1. Operator clicks "Remove" in the campaign detail panel.
  2. System issues DELETE /api/campaigns/:id.
  3. Campaign row is hard-deleted.

Constraints: Only prepared campaigns can be deleted (API enforced).

TestAssertion
not yet automated

UC-2.4 · Fund Campaign (On-chain Escrow Deposit)

Actor: Operator Preconditions: Campaign is prepared. Operator has a connected wallet with sufficient USDC balance.

Flow:

  1. Operator opens campaign detail panel on /operator/campaigns.
  2. FundCampaignPanel displays escrow deposit form (amount, network, timelock).
  3. Operator approves USDC spend in MetaMask, then confirms the deposit transaction.
  4. System detects on-chain confirmation; updates campaign escrow_id, funding_address, and status to funded via PUT /api/campaigns/:id. (There is no dedicated funding endpoint, and network_id is not written at funding time — only at create.)

Postconditions: Campaign is visible to streamers. Funds locked in HTLC contract.

TestAssertion
not yet automated — on-chain / MetaMask, not API-testable

→ Product Guide: Campaign Creation · Fund Deposit


UC-3 · Campaign Discovery & Application (Streamer)

UC-3.1 · Browse Campaigns

Actor: Public / Streamer Preconditions: None (public access).

Flow:

  1. User opens /campaigns.
  2. System fetches campaigns with status funded or in_progress from the API.
  3. User can filter by tag (Beginner friendly, High budget, Closing soon) and sort (Featured, Highest budget, Newest).
  4. Campaign cards show operator name, category, platforms, budget, dates, min requirements.

Constraint — Streamer isolation (API-enforced): Authenticated streamers must never see other streamers' profiles, usernames, pricing, or deal terms. GET /api/streamers?public=1 returns 403 for any streamer-role session, and GET /api/streamers/:username returns 403 for a streamer viewing anyone other than themselves (a streamer may still read their own profile; admins are exempt).

TestAssertion
not yet automated — read-only list endpoint

UC-3.2 · View Campaign Detail

Actor: Public / Streamer

Flow:

  1. User opens /campaign/:id.
  2. System fetches campaign details.
  3. Unauthenticated users see full public info with a sign-in CTA.
  4. Authenticated streamers see an application card (apply / already applied / approved).
  5. Tabbed sections: Overview, Requirements, Reward.
TestAssertion
not yet automated — read-only endpoint

UC-3.3 · Submit an Offer (Streamer-initiated)

Actor: Streamer Preconditions: Streamer authenticated with status active (not suspended/archived). Campaign status funded or in_progress. No active (pending or accepted) offer already exists for this streamer on this campaign.

Flow:

  1. Streamer clicks "Apply" on the campaign detail page or card.
  2. Streamer adds an optional pitch message (no deal-term inputs on the streamer path).
  3. System creates an offers row via POST /api/campaigns/:id/applications (legacy shim) with initiated_by = 'streamer', status = 'pending', and no expiry (streamer offers do not expire).
  4. Operator receives a notification.

Postconditions: Offer visible in /profile/offers (streamer) and the campaign's Offers panel (operator).

Constraints:

  • Streamer must have set a price bracket first, else 400 (onboarding gate).
  • Suspended and archived streamer accounts are blocked (403).
  • Duplicate active offer → 409 (one per streamer per campaign at a time).

⚠️ Known gap: the "five deal-term fields pre-filled from minimum_terms" and "validate all starting terms ≥ campaign floor" behaviour applies only to the operator-initiated path (UC-4.4). The streamer "Apply" flow posts a message-only application — it sends no starting_terms, so no floor validation runs streamer-side. (Field name is minimum_terms, not minimal_terms.)

TestAssertion
TC-OFFER-01 ⚠️POST /api/campaigns/:id/offers → 201; test uses initiated_by = "operator" — streamer-initiated path not yet separately asserted

→ Product Guide: Applying to Campaigns


UC-3.4 · Withdraw Offer

Actor: Streamer Preconditions: Offer status is pending. Streamer is the initiating party.

Flow:

  1. Streamer opens /profile/offers.
  2. Clicks "Withdraw" on a pending offer.
  3. System issues PATCH /api/offers/:id with action = 'withdraw'.
TestAssertion
TC-OFFER-04revoke_decline blocked by uq_offer_active when conflict exists → 409
TC-OFFER-05revoke_decline succeeds when no conflict → offer returns to pending

UC-4 · Offer Review (Operator)

UC-4.1 · View Campaign Offers

Actor: Operator Preconditions: Operator has funded campaigns.

Flow:

  1. Operator opens /operator/active and selects a campaign.
  2. System fetches offers for the campaign via GET /api/campaigns/:id/offers.
  3. Offers listed with streamer username, Trust Score, starting terms, and current status.
TestAssertion
not yet automated — read-only list endpoint

UC-4.2 · Accept Offer → Open Negotiation

Actor: Operator Preconditions: Offer status pending, initiated by streamer (or operator is the streamer receiving the offer back). Campaign status funded or in_progress.

Flow:

  1. Operator selects a pending offer and clicks "Accept".
  2. System issues PATCH /api/offers/:id with action = 'accept'.
  3. API transitions offer status to accepted.
  4. API auto-creates a negotiations row seeded from the offer's starting_terms, with offer_id set and initial turn = 'operator' (since streamer proposed).
  5. Both parties are notified.

Postconditions: Negotiation exists with status in_progress.

TestAssertion
TC-OFFER-02Duplicate accept is idempotent or returns 409 — offer never left in inconsistent state

UC-4.3 · Decline Offer

Actor: Operator

Flow:

  1. Operator clicks "Decline" on a pending offer.
  2. System issues PATCH /api/offers/:id with action = 'decline'.
  3. Offer transitions to declined. Streamer can re-apply.
TestAssertion
TC-OFFER-03action = decline → offer status declined

UC-4.4 · Send Offer to Streamer (Operator-initiated)

Actor: Operator Preconditions: Operator has at least one funded/in-progress campaign (Tier 2 directory access). Streamer has no active offer on this campaign.

Flow:

  1. Operator opens a streamer's public profile at /s/:username.
  2. Clicks "Send Offer" and selects a campaign.
  3. Offer form shows five deal term fields pre-filled from campaign.minimum_terms.
  4. Operator adjusts values, adds an optional message, and sets an expiry date (default: 7 days).
  5. System validates starting terms ≥ campaign floors.
  6. System creates offers row with initiated_by = 'operator', status = 'pending', expires_at set.
  7. Streamer receives a notification.

Postconditions: Offer visible to both parties. Streamer can accept, decline, or let it expire.

Rate limit: 20 operator-initiated offers per 24-hour window.

TestAssertion
TC-OFFER-01 ⚠️POST /api/campaigns/:id/offers with initiated_by = "operator" → 201
Expiry enforcement and rate-limit (20/24h) not yet automated

→ Product Guide: Offer Review & Deal Negotiation · Budget Allocation


UC-4B · Deal Negotiation

UC-4B.1 · Propose or Approve a Term

Actor: Operator or Streamer Preconditions: Negotiation status = in_progress. It is this actor's turn.

Flow:

  1. Actor opens the negotiation panel and sees all five terms with current proposed values and approval states.
  2. Approve — Actor clicks "Approve" on a term they agree with.
    • System sets op_ok or str_ok = true on that term.
  3. Propose — Actor enters a new value for a term.
    • System updates value and proposed_by.
    • Resets the other party's ok flag to false on that term.
  4. After either action, the system checks whether all five terms have both op_ok = true and str_ok = true.
  5. If all agreed → negotiation status transitions to agreed; agreed_at is recorded.
  6. Otherwise → turn advances to the other party.

Constraints: Values should remain ≥ campaign floor values throughout negotiation. The server only enforces value >= 0 on propose; the campaign-floor check is client-side only (known gap).

TestAssertion
TC-NEG-01Streamer approves all 5 terms → status = agreed, agreed_at set, turn = NULL

UC-4B.2 · Cancel Negotiation

Actor: Operator or Streamer Preconditions: Negotiation status = in_progress.

Flow:

  1. Actor clicks "Cancel Negotiation".
  2. System issues POST /api/negotiations/:id/cancel.
  3. Negotiation transitions to cancelled. A new offer can be created to restart.
TestAssertion
not yet automated

UC-4B.3 · Fund Deal

Actor: Operator Preconditions: Negotiation status = agreed.

Flow:

  1. Operator opens the agreed negotiation and clicks "Fund Deal".
  2. System confirms the negotiation is agreed and the operator is the correct party.
  3. Operator supplies on-chain parameters (escrow ID, network, funding address, allocation ID and amount).
  4. Operator signs the HTLC allocation transaction in MetaMask.
  5. System creates a deals row with:
    • negotiation_id FK
    • agreed_terms — immutable JSONB snapshot of the five negotiated values
    • On-chain fields (escrow_id, network_id, funding_address, allocation_id, allocation_amount)
    • status = 'active'
  6. Both parties notified. Streamer's delivery panel becomes active.

Postconditions: Deal exists and is active. Agreed terms are permanently frozen.

TestAssertion
TC-NEG-02allocation_amount exceeds budget cap → 409
TC-NEG-03allocation_amount within budget cap → 201, status = active
TC-NEG-04wallet_address snapshotted from streamer_addresses at funding time

→ Product Guide: Offer Review & Deal Negotiation


UC-5 · Stream Delivery (Streamer)

UC-5.1 · Submit Stream Session Proof

Actor: Streamer Preconditions: Application status approved. Streamer has a slot_index to submit for.

Flow:

  1. Streamer opens /profile/campaigns.
  2. Selects approved campaign and an available slot.
  3. Pastes stream/VOD URL.
  4. Optionally selects a tracked Kick session from the dropdown.
  5. Clicks "Submit".
  6. System creates session_submissions row (status = pending_review).

Constraints: Multiple attempts per slot are allowed (the attempt counter increments); the API blocks resubmission only once a slot is confirmed (409). There is no DB unique constraint on session_submissions(offer_id, slot_index) — uniqueness is enforced one level down, on payout_records (uq_payout_records_offer_slot), so a slot pays out at most once. URL is currently required (tracked-session-only path is a known gap — F-4).

TestAssertion
TC-DEL-01slot_index outside allocation range → 400, error lists valid indices
TC-DEL-02Valid slot_index → 201, status = pending_review, attempt = 1
TC-DEL-03Slot already confirmed → resubmission blocked with 409

→ Product Guide: Delivery & Submission


UC-6 · Delivery Review & Payout (Operator)

UC-6.1 · Review Submission

Actor: Operator Preconditions: session_submissions row with status pending_review.

Flow:

  1. Operator opens /operator/active.
  2. Submission listed under the relevant application.
  3. Operator reviews stream URL and any notes.
  4. Operator clicks "Confirm" or "Reject".
TestAssertion
not yet automated — read-only list view

UC-6.2 · Confirm Submission → Release Payout

Actor: Operator Preconditions: Submission pending_review. Streamer KYC fully approved — both kyc_age_status and kyc_country_status = approved (API enforced — F-1). The legacy single kyc_status column was dropped in migration 0119; KYC is per-track since 0113.

Flow:

  1. Operator confirms submission.
  2. API:
    • Checks both kyc_age_status and kyc_country_status = approved on the streamer. Returns 403 if either track isn't approved.
    • Reads per-slot amount from allocation_preimages.slots[slot_index].
    • Inserts payout_records row with amount, wallet snapshot, allocation_id.
    • Updates session_submissions.status = 'confirmed'.
  3. Operator retrieves preimage for the slot (stored in allocation_preimages).
  4. Operator sends preimage to streamer off-platform (or automated via payout_type = automatic).
  5. Streamer calls withdraw() on-chain using the preimage.
  6. Streamer (or frontend) submits tx hash via PATCH /api/session-submissions/:id/tx.

Postconditions: payout_records row has tx_hash. Streamer has received USDC.

TestAssertion
TC-DEL-04Confirming last slot → deals.status = completed (auto-complete regression)
TC-DEL-05Confirming partial slot → deals.status remains active
TC-DEL-06Streamer KYC not approved → confirmation blocked with 403

UC-6.3 · Reject Submission

Actor: Operator

Flow:

  1. Operator clicks "Reject" on a submission.
  2. System updates session_submissions.status = 'rejected'.
  3. Streamer can resubmit (no enforcement on retry count — F open).
TestAssertion
not yet automated

→ Product Guide: Delivery Review · Payment Withdrawal · Refund


UC-7 · Earnings Tracking (Streamer)

UC-7.1 · View Earnings Dashboard

Actor: Streamer Preconditions: Authenticated.

Flow:

  1. Streamer opens /profile/dashboard.
  2. System fetches payout_records for the streamer (GET /api/streamers/:username/payouts).
  3. Dashboard shows:
    • Total Earned (sum of all payout_records.amount_usdc)
    • Earned This Month (filtered by current month)
    • In Escrow (sum of approved allocations not yet confirmed)
    • Pending (count of pending submissions)
    • Recent activity feed
TestAssertion
not yet automated — read-only aggregation

UC-7.2 · View Application / Payout History

Actor: Streamer

Flow:

  1. Streamer opens /profile/applications.
  2. All applications listed with campaign name, status, allocation amount, confirmed amount.
  3. Streamer can expand each application to see submission-level detail and payout records.
TestAssertion
not yet automated — read-only list

→ Product Guide: Profile


UC-8 · Streamer Profile & Verification

UC-8.1 · Streamer Registration

Actor: Public Preconditions: None, or operator has a funded account to view the streamer directory.

Flow:

  1. User opens /streamer-signup.
  2. Accepts consent, then connects their Kick account via OAuth — this is now the only signup path.
  3. System derives a unique username from the Kick handle and links a streamer_channels row (Kick fields populated).
  4. System creates streamers row (status = pending).

Postconditions: Account pending admin activation.

Note: the legacy email-+-4-digit-PIN signup was retired (the API branch still exists but has no live frontend caller). Email/PIN is bypassed when a kick_signup_token is present. Country/languages are completed later in the profile, not at signup.

TestAssertion
TC-AUTH-02PIN sent and verified for streamer email → session established

UC-8.2 · KYC Document Upload

Actor: Streamer Preconditions: Streamer authenticated.

Flow:

  1. Streamer opens /profile/kyc.
  2. Sumsub WebSDK is the primary KYC path (gated by streamer-kyc-sumsub-enabled); it drives the per-track statuses. Manual document upload (identity and/or proof of address, JPEG/PNG, max 5MB → S3 + streamer_documents row) is the fallback and the Sumsub-FINAL-rejection recovery path.
  3. Verification resolves two independent tracks: kyc_age_status (identity / liveness) and kyc_country_status (proof of address). Each is set to approved / rejected by Sumsub webhooks or by an admin in the admin app. (The legacy single kyc_status column was dropped in 0119.)
  4. If a track is rejected, the streamer can re-verify / re-upload after viewing the rejection reason.
TestAssertion
not yet automated — requires S3 or mock bucket

UC-8.3 · Connect / Manage Wallets

Actor: Streamer

Flow:

  1. Streamer opens /profile/wallet.
  2. Connects MetaMask wallet; system saves to streamer_addresses.
  3. Multiple addresses supported; each can have an optional label.
  4. Wallet address is used as payout destination in payout_records.
TestAssertion
TC-NEG-04 ⚠️streamer_addresses row read at deal funding — wallet snapshotted correctly; CRUD not tested

UC-8.4 · Update Profile & Deal Preferences

Actor: Streamer

Flow:

  1. Streamer opens /profile/personal.
  2. Edits personal info (country, languages) and streaming preferences (categories, GEOs, deal types, rates).
  3. Updates operator-facing bio.
  4. System issues PUT /api/streamers/:username.
TestAssertion
not yet automated

UC-8.5 · View Trust Score

Actor: Streamer / Public

Flow:

  1. Streamer opens /trust-score or operator views /s/:username.
  2. System displays trust score breakdown across 5 components: channel age, followers, average viewers, viewer/follower ratio, and stream frequency. (There is no literal "engagement" pillar — viewer_follower_ratio is the closest proxy.)
  3. Improvement suggestions displayed per pillar.
  4. Score is recomputed from streamer_trust_scores via the poller/webhook pipeline.
TestAssertion
not yet automated — score computed externally by poller

→ Product Guide: Registration & Onboarding · Profile · Wallet · Trust Score


UC-9 · Streamer Discovery (Operator)

UC-9.1 · Browse Streamer Directory

Actor: Operator Preconditions: Operator authenticated. Access is gated — redirects to /entry if not an operator.

Access to the streamer directory is tiered by whether the operator has at least one campaign with status funded or in_progress.

Enforcement (server-side): the funded-campaign tier split is enforced by the API. GET /api/streamers?public=1 reads the caller's session; when the caller is an operator with nofunded/in_progress campaign (Tier 1), every identity-revealing field (username, bio, Discord, Telegram, country, Kick slug/handle/display name/avatar, Twitter handle) is stripped from each row on top of the standard PII-strip (UC-9.3) — leaving the anonymised preview. GET /api/streamers/:username (the /s/:username profile page source) returns 403 for a Tier-1 operator. The gate is the campaigns.status IN ('funded','in_progress') EXISTS check (operatorHasFundedCampaign in api/src/streamers.ts), and it fails closed (anonymise on any error). Non-operator callers (logged-out visitors, streamers, admins) are unaffected by the tier gate.

Tier 1 — No funded campaign (anonymous preview)

  1. Operator opens /streamers.
  2. System detects no funded or in_progress campaign for this operator.
  3. API returns an anonymised streamer list: follower count, engagement score, trust score, verified badge, platform icons, categories, and approximate price bracket.
  4. Username, bio, contact handles, country, and all identifying fields are withheld by the API.
  5. Free-text search is disabled.
  6. Each card shows a "Fund a campaign to unlock full profiles" CTA.
  7. Clicking a streamer card or navigating to /s/:username returns 403.

Tier 2 — At least one funded campaign (full directory)

  1. Operator opens /streamers.
  2. System detects at least one funded or in_progress campaign.
  3. API returns full profiles: username, bio, country, languages, streaming categories, deal preferences, trust score, follower count, verification badge, platforms.
  4. Free-text search across username and categories is enabled.
  5. Operator can filter by platform (Kick / Twitch) and streaming category.
  6. Streamer cards show all public profile fields. Exact pricing is never shown — see UC-9.3.
  7. Operator can click through to the full public profile at /s/:username (see UC-9.2).

Note: The invite flow (operator sends a direct deal offer) is a separate proposal — see the offer flow (UC-4.4).

TestAssertion
not yet automated — tier-gating logic and list response

UC-9.2 · View Streamer Public Profile

Actor: Operator (Tier 2 only) Preconditions: Operator authenticated and has at least one funded or in_progress campaign.

Flow:

  1. Operator clicks a streamer card on /streamers or navigates directly to /s/:username.
  2. System checks the operator's tier. If Tier 1, returns 403 with body: { "error": "Fund a campaign to view full streamer profiles." }
  3. For Tier 2 operators, system returns the full public profile:
    • Avatar, username, verified badge, trust score
    • Bio (operator-facing), country, languages
    • Streaming categories, platform handles
    • Deal type preferences, price bracket (not exact rate — see UC-9.3)
    • Follower count, engagement score, live status
  4. Operator can initiate the invite flow from this page (future — see proposal).
TestAssertion
not yet automated — read-only + tier guard

UC-9.3 · Pricing Display Rules

Actor: Operator, System Preconditions: Streamer has set deal_cpa_rate and/or deal_revshare_rate.

Rule: Exact streamer rates (deal_cpa_rate / deal_revshare_rate) are never surfaced in the directory or on public profiles — both are stripped from ?public=1 and /s/:username responses.

Flow:

  1. The streamer picks a price_bracket themselves — it is an explicit column (migration 0153), not derived from the raw rate fields. The displayed bracket is the streamer's stated band.
  2. The bracket is shown on streamer cards (Tier 2 + Tier-1 preview) and on the public profile.
  3. Exact figures are revealed only after the deal terms are negotiated and an HTLC allocation (allocation_preimages row) exists — the operator's allocation panel then shows the agreed amount.

Bracket values (codes from migration 0153_streamer_price_bracket_v2.sql; labels from frontend/src/utils/priceBracket.ts):

CodeDisplay label
0_50Under $50
50_200$50 – $200
200_500$200 – $500
500_1000$500 – $1,000
over_1000$1,000+

Note: migration 0152 introduced the column with a different set of bands; 0153 superseded it with the codes above. Earlier drafts of this doc cited 0152's <$100 … $10k+ ranges — those are stale.

TestAssertion
not yet automated — bracket mapping logic

→ Product Guide: Dashboard & Streamer Discovery


UC-10 · Fund Breakdown Monitoring (Operator)

UC-10.1 · View Campaign Fund Breakdown

Actor: Operator

Flow:

  1. Operator opens /operator/campaigns or /operator/active.
  2. Each campaign card shows a FundDonut chart with four segments:
    • Rewarded — confirmed payout_records sum
    • Escrowed — approved allocation_amount minus rewarded
    • Refunded — refunded allocation_amount
    • Unallocated — remainder of budget_cap_usdc
  3. Detail panel shows the donut with legend and exact amounts.
TestAssertion
not yet automated — read-only aggregation

→ Product Guide: Fund Deposit


UC-11 · Admin Operations

UC-11.1 · Impersonate Streamer / Operator

Actor: Admin

Flow:

  1. Admin creates an impersonation token via admin app (stored in impersonation_tokens table).
  2. Admin opens /auth/impersonate?token=UUID.
  3. System validates token (single-use, time-limited).
  4. Session is established for the target account.
  5. Impersonation banner is displayed across all pages for the duration of the session.
TestAssertion
not yet automated — admin-only, single-use token

UC-11.2 · Manage Invitations

Actor: Admin (via admin app at admin.verifluence.io)

Flow:

  1. Admin creates invitations row with target email.
  2. System emails the invitation link to the operator.
  3. Admin can view invitation status (pending / used / expired).
TestAssertion
not yet automated — admin-only

→ Admin portal at admin.verifluence.io (no product guide page — internal tool only)


UC-12 · Deal Negotiation

Status: Implemented — see UC-4B for the full negotiation and deal funding flow.

The Offer / Negotiation / Deal entity model replaces the earlier prototype described here. Minimum term floors are set per campaign (not per streamer); both operator- and streamer-initiated offers are supported via initiated_by.

Anchor retained: the roadmap (Milestones.vue / roadmap.md / milestones.md) deep-links UC-12.x milestone items to #uc-12-deal-negotiation.


Out of Scope (Not Yet Implemented)

FeatureStatus
Campaign Paused / Archived statusNot built — status enum is prepared/funded/in_progress/completed/cancelled
Campaign Analytics (VOD metrics, conversions)Not built
Referral programNot built
Dispute / mediation flowNot built
Tracked-session-only delivery (no URL)Not built (F-4)
Server-side prepared-only campaign edit guardNot built (UC-2.2 gap)
Streamer-side deal-term floor validationNot built (UC-3.3 gap — only operator path validates)
Persisted operator consent at sign-upNot built (UC-1.1 gap — no consent column on operators)

Recently resolved (previously listed here)

These were tracked as gaps in earlier revisions and are now implemented:

Former gapNow
Delivery child tables remapped (migration 0079)Applied — remapped to offer_id (not deal_id); all three tables carry offer_id NOT NULL
Application auto-complete on last slot (F-3)Implemented — confirming the last slot sets deals.status = 'completed'
Slot index validation against allocation (F-5)Implemented — out-of-range slot_index → 400 listing valid indices
Budget cap enforcement on allocation (F-8)Implemented — deals.ts sums existing allocations, rejects over-cap with 409
In-app notifications (real-time)Implemented — Pusher channels (API pusher.ts + FE usePusher)
Negotiation UI (frontend components)Implemented — operator + streamer negotiation pages and components/negotiations/

Verifluence Documentation