Marketplace
Architecture
The definitive reference for how the Delivery Hub marketplace works, why it is built on the existing DH platform, and what needs to be decided before implementation.
Agenda and Priorities
What this document covers, in order of importance
Architecture Decision
Where does bounty logic live? Portal database, Salesforce fields, or hub model?
Data Model
What fields get added to which objects? How many are new vs already exist?
The Controller
What DeliveryBountyController looks like -- every method, every trigger, every side effect
How the Pieces Connect
DH orgs -> Nimba sandbox -> Dev Hub -> cloudnimbusllc.com -- the sync chain that already runs
Edge Cases
NTE overruns, multi-dev conflicts, auto-promote, team claims, AI agents, recruiter placements
Glen's Feedback Summary
Every piece of feedback with its architectural implication and decision status
Open Questions
Remaining decisions that need answers before implementation begins
Recommended Next Steps
Prioritized action items with time estimates
How Delivery Hub IS the Marketplace
Glen said "I don't think it is" -- this section makes the case
The Core Insight
A "marketplace" is just a pattern: someone posts work, someone claims it, someone delivers it, someone pays. Delivery Hub already does all of this. The "marketplace" is not a new system -- it is making the existing system visible to a wider audience. cloudnimbusllc.com is the storefront window; Salesforce (via Delivery Hub) is the store.
Three Architecture Options
Developer
Browser
cloudnimbusllc.com
Thin UI Layer (same as today)
Salesforce (DH Package)
Single source of truth
Existing proxy at /api/delivery-hub/[...path] already handles all API calls. Add ~20 fields to 3 existing objects + 1 new Apex controller. Portal just adds new pages.
Pros
- - Single source of truth (Salesforce)
- - Existing sync engine works
- - Existing hour logging works
- - Existing invoicing works
- - Minimal new code (~20 fields, 1 class)
- - 1-2 weeks to build
Cons
- - Every schema change needs a package version + CI
- - Salesforce API limits at scale
- - Single-org until sync engine routes multi-client
Recommendation
Architecture C is what Glen described and what already works. MF is already doing this: MF Prod syncs to Nimba syncs to Dev Hub syncs to the portal. The marketplace is just opening up the pattern. Build B first (add fields + controller to DH package), then evolve to C (multi-client hub routing as more clients onboard). Architecture A is a rewrite that buys nothing.
Full Comparison
| Concern | A: Portal DB | B: DH Fields | C: Hub Model |
|---|---|---|---|
| Data model work | Build from scratch | ~20 fields on 3 objects | ~20 fields + hub routing config |
| Source of truth | Two (portal + SF) | One (Salesforce) | One per org, aggregated at hub |
| Sync complexity | High (bidirectional portal <> SF) | None (proxy passthrough) | Existing (already works, proven) |
| Portal changes | Full backend app (DB, ORM, API) | New pages + API route calls | New pages + API route calls |
| Salesforce changes | None | 20 fields + 1 controller | 20 fields + 1 controller + hub config |
| Works without SF | Yes | No | No (portal could have lite mode later) |
| Multi-client support | Manual per-client DB config | Via existing sync engine | Via sync engine (proven with MF) |
| Time to build | 4-6 weeks | 1-2 weeks | 2-3 weeks |
| Scalability ceiling | High (own DB, no SF limits) | SF API limits (fine for years) | SF API limits per org (distributed) |
| Existing infrastructure reuse | None -- everything rebuilt | 100% -- all existing features work | 100% -- all existing features work |
The Exact Data Model Changes
~20 new fields on 3 existing objects. Zero new objects.
Total new fields: ~24 across 3 objects. WorkItem__c gets 11 bounty fields. WorkRequest__c gets 7 claim/bid fields. NetworkEntity__c gets 6 developer profile fields. No new custom objects required -- existing objects already model the full bounty lifecycle.
New Fields Required
WorkItem__c
The Bounty| Field API Name | Type | Purpose |
|---|---|---|
| IsBountyBool__c | Checkbox | Flags this work item as a public bounty |
| BountyAmountCurrency__c | Currency | Fixed-price bounty amount |
| BountyStatusPk__c | Picklist | Bounty lifecycle: Draft, Published, Claimed, In Review, Completed, Cancelled |
| BountyDeadlineDate__c | Date | Submission deadline for the bounty |
| BountyDifficultyPk__c | Picklist | Skill level required: Beginner, Intermediate, Advanced, Expert |
| BountySkillsTxt__c | LongTextArea | Comma-separated required skills for matching |
| BountyRepoUrlTxt__c | URL | GitHub repository link for the work |
| BountyPublicTokenTxt__c | Text(255) | Public access token for unauthenticated viewing |
| BountyVisibilityPk__c | Picklist | Who can see this bounty: Internal, Approved Vendors, Public |
| BountyMaxClaimsNumber__c | Number | How many developers can claim simultaneously |
| BountyNteCeilingCurrency__c | Currency | Maximum NTE any single claim can propose |
WorkRequest__c
The Claim / Bid| Field API Name | Type | Purpose |
|---|---|---|
| NteHoursNumber__c | Number | Developer's proposed NTE hours |
| NteAmountCurrency__c | Currency | Developer's proposed NTE dollar amount |
| ProposalTxt__c | LongTextArea | Developer's technical approach and proposal narrative |
| SubmissionUrlTxt__c | URL | PR link when work is submitted for review |
| SubmissionNoteTxt__c | LongTextArea | Submission notes, test coverage summary, known issues |
| NteConsumedPct__c | Formula(Percent) | Logged hours / NTE hours -- for budget guard alerts |
| ComplianceScoreNumber__c | Number | NTE compliance metric that feeds into developer reputation |
NetworkEntity__c
Developer / Vendor Profile| Field API Name | Type | Purpose |
|---|---|---|
| DeveloperBioTxt__c | LongTextArea | Public-facing developer or team bio |
| DeveloperSkillsTxt__c | LongTextArea | Comma-separated skill tags for matching |
| CompletedBountiesNumber__c | Number | Rollup count of completed bounties |
| AverageRatingNumber__c | Number(2,2) | Average rating from client reviews |
| IsPublicProfileBool__c | Checkbox | Opt-in to the public developer directory |
| InsuranceStatusPk__c | Picklist | Liability insurance status: Verified, Unverified, Not Required |
Existing Fields That Already Cover Bounty Needs
These fields are already in the DH package and do not need to be created. The marketplace uses them as-is.
| Object | Field | Type | Marketplace Purpose |
|---|---|---|---|
| WorkItem__c | AcceptanceCriteriaTxt__c | HTML LongTextArea (32K) | Bounty acceptance criteria -- already exists |
| WorkItem__c | EstimatedHoursNumber__c | Number | Baseline estimate -- already exists |
| WorkItem__c | BillableRateCurrency__c | Currency | Hourly rate -- already exists |
| WorkItem__c | Tags__c | LongTextArea | Skills tagging -- already exists |
| WorkRequest__c | StatusPk__c | Picklist | Claim lifecycle (includes "Open for Bids") -- already exists |
| WorkRequest__c | WorkProofUrl__c | URL | PR submission link -- already exists |
| WorkRequest__c | QuotedHoursNumber__c | Number | Developer estimate -- already exists |
| WorkRequest__c | HourlyRateCurrency__c | Currency | Agreed rate -- already exists |
| NetworkEntity__c | GithubUsernameTxt__c | Text | GitHub identity -- already exists |
| NetworkEntity__c | EntityTypePk__c | Picklist | Client / Vendor / Both -- already exists |
| WorkLog__c | (entire object) | Custom Object | Full hour logging with approval workflow -- already exists |
| DeliveryDocument__c | (entire object) | Custom Object | Invoices, client agreements, contractor agreements -- already exists |
| DeliveryTransaction__c | (entire object) | Custom Object | Payment records and financial tracking -- already exists |
The Controller: DeliveryBountyController
One Apex class. 12 methods. Every read, write, and side effect documented.
@RestResource(urlMapping='/bounties/*') global with sharing class DeliveryBountyController { // Public API (portal REST proxy) @HttpGet global static List<BountyView> getPublicBounties(BountyFilter filters) // Developer actions @AuraEnabled public static WorkRequest__c claimBounty(Id workItemId, String proposal, Decimal nteHours, Decimal nteAmount) public static WorkRequest__c submitWork(Id workRequestId, String prUrl, String notes) public static DashboardView getDeveloperDashboard(String email) public static NetworkEntity__c registerDeveloper(...) // Org / Admin actions public static WorkItem__c promoteToBounty(...) public static WorkRequest__c reviewClaim(...) public static WorkRequest__c acceptSubmission(...) public static WorkRequest__c requestRevision(...) // Budget guards + scoring public static NteStatusView getNteStatus(Id workRequestId) public static void scoreSubmission(...) }
Public API (called by portal via REST proxy)
getPublicBounties
getPublicBounties(filters: BountyFilter) => List<BountyView>Returns all published bounties visible to the caller. Filters by difficulty, skills, org, status. Respects BountyVisibilityPk__c settings.
WorkItem__c (WHERE IsBountyBool__c = true AND BountyStatusPk__c = 'Published')
None
None
getBountyByToken
getBountyByToken(token: String) => BountyDetailViewFetches a single bounty by its public access token. Used for unauthenticated sharing links. Returns full detail including acceptance criteria, existing claims count, and deadline.
WorkItem__c, WorkRequest__c (count)
None
None
Developer Actions
claimBounty
claimBounty(workItemId, proposal, nteHours, nteAmount) => WorkRequest__cCreates a new WorkRequest (claim) against a published bounty. Validates max claims not exceeded, deadline not passed, and NTE is within ceiling.
WorkItem__c (bounty details + claim count)
WorkRequest__c (new record)
Email notification to org admin
submitWork
submitWork(workRequestId, prUrl, notes) => WorkRequest__cDeveloper submits their completed work for review. Updates claim status to 'Submitted'. Captures PR URL and submission notes.
WorkRequest__c
WorkRequest__c (status update)
Email notification to reviewer, Ghost Recorder activity log
getDeveloperDashboard
getDeveloperDashboard(email: String) => DashboardViewReturns all active claims, submitted work, completed bounties, NTE consumption stats, and reputation score for a developer.
NetworkEntity__c, WorkRequest__c, WorkLog__c, WorkItem__c
None
None
registerDeveloper
registerDeveloper(name, email, github, bio, skills) => NetworkEntity__cCreates a new NetworkEntity with EntityType=Vendor and a PortalAccess record. Sets up the developer profile for the marketplace directory.
NetworkEntity__c (duplicate check)
NetworkEntity__c, PortalAccess__c
Welcome email, profile setup notification
Org / Admin Actions
promoteToBounty
promoteToBounty(workItemId, amount, difficulty, skills, deadline, visibility, maxClaims) => WorkItem__cPromotes an existing internal work item to a public bounty. Sets all bounty fields, generates a public access token, and changes BountyStatusPk__c to Published.
WorkItem__c
WorkItem__c (bounty fields populated)
Sync engine propagates to hub, bounty board refreshes
reviewClaim
reviewClaim(workRequestId, decision, notes) => WorkRequest__cAdmin approves or rejects a developer's claim. If approved, generates contractor agreement via Document Engine. If rejected, sends feedback to developer.
WorkRequest__c, WorkItem__c
WorkRequest__c (status update)
If approved: Document Engine creates contractor agreement. Email to developer.
acceptSubmission
acceptSubmission(workRequestId) => WorkRequest__cMarks submitted work as accepted. Triggers Document Engine to generate invoice with all logged hours as line items. Moves bounty to Completed if no other active claims.
WorkRequest__c, WorkLog__c (all logged hours), WorkItem__c
WorkRequest__c (Accepted), WorkItem__c (BountyStatusPk__c update)
Document Engine: contractor agreement finalization + invoice generation. Email with PDF CC to glen@cloudnimbusllc.com.
requestRevision
requestRevision(workRequestId, feedback) => WorkRequest__cSends work back to developer with specific revision feedback. Status changes to 'Revision Requested'. Developer can re-submit.
WorkRequest__c
WorkRequest__c (status + feedback)
Email to developer with revision details
Budget Guards & Scoring
getNteStatus
getNteStatus(workRequestId) => NteStatusViewReturns current NTE consumption: total hours logged, percentage consumed, threshold alerts (70%, 90%, 100%). Used by both developer dashboard and admin review.
WorkRequest__c, WorkLog__c (SUM of hours)
None
If >= 70%: warning flag. If >= 90%: alert. If >= 100%: soft stop notification.
scoreSubmission
scoreSubmission(workRequestId, rating, qualityNotes) => voidAdmin rates a completed submission. Updates developer's average rating on NetworkEntity__c. Factors in NTE compliance (did they stay within budget?).
WorkRequest__c, NetworkEntity__c
NetworkEntity__c (AverageRatingNumber__c, CompletedBountiesNumber__c)
Reputation update visible on developer profile
How the Pieces Connect
The sync chain that is already running in production
MF Production
Client's DH installation
37 WorkItems, 59 WorkLogs
Nimba Sandbox
Hub / aggregator
Receives from all clients
Dev Hub
Central hub org
Aggregated view
cloudnimbusllc.com
Portal + Marketplace UI (thin layer)
/bounties, /portal, /platform
What Exists Today
- - V3 sync engine with echo suppression and dedup
- - Push flow via Sync_Item__c queue + SyncItemProcessor
- - Pull flow via DeliveryHubPoller + @HttpGet /changes
- - GlobalSourceIdTxt__c for cross-org identity
- - Multi-tenant isolation on vendor side
- - /api/delivery-hub/[...path] proxy in Next.js
What Gets Added for Marketplace
- - Bounty fields on WorkItem__c (sync automatically)
- - DeliveryBountyController REST endpoints
- - Portal pages: /bounties (board), /bounties/[id] (detail), /developer/dashboard
- - Developer registration flow
- - NTE consumption API for budget guards
- - Scoring hooks for reputation system
The portal stays thin. cloudnimbusllc.com does not need a database, ORM, or its own data model. Every API call passes through the existing proxy to Salesforce. The portal renders data and handles auth -- that is it. This is how the portal works today for the client dashboard, activity feed, and hour logging. Bounties follow the same pattern.
Edge Cases and How They Are Handled
Every scenario that could go wrong, with the planned resolution
Developer Exceeds NTE
Warning at 70%, alert at 90%, soft stop at 100%. Logged in compliance score. Client decides whether to pay overrun. Developer's NTE compliance score is affected -- it's a soft guideline, not a hard block, but it factors into reputation.
Multiple Devs, One Delivers
Delivered dev gets paid via Document Engine (contractor agreement + invoice). Non-delivered dev's claim moves to Cancelled. Client only pays for accepted work. Both claims are tracked for transparency.
Client Rejects All Submissions
Bounty goes back to Published. Devs who submitted get documented hours (billable per the lifecycle agreement -- all phases are billable). Client can re-scope and re-post. Rejection feedback is recorded.
Internal Employee vs External Dev
Both claims tracked equally via WorkRequest__c. Manager sees both proposals side by side. Can accept one or both. Internal employee's familiarity with the repo is a natural advantage -- and that is fine.
Auto-Promote Internal to Marketplace
Configurable trigger: deadline proximity, NTE consumption rate, manual override. Different businesses want different things -- some want aggressive auto-promote, some want manual control only. Creates a Published bounty from the existing internal WorkItem.
Developer Claims as a Team
NetworkEntity with EntityType=Vendor, multiple PortalAccess records. Team submits one WorkRequest with aggregated NTE. Individual members log hours via WorkLog. Platform aggregates for invoicing.
Scratch Org for Developer
CCI spins up scratch org, installs DH package, syncs WorkItems from hub sandbox. Dev works in scratch org, pushes to branch, opens PR. CI validates. This is the Salesforce-specific enhancement -- not required for the core lifecycle.
Recruiter Places a Developer
Recruiter is a NetworkEntity (type=Vendor). Their developer is a PortalAccess member. Recruiter takes a configurable cut (markup on their entity). Platform rake applies on top. Both are tracked in the rate chain.
AI Agent Claims a Bounty
NetworkEntity with a special type flag (AI Agent). Automated PR submission via GitHub API. CI validates test coverage and PMD. Human review still required for acceptance -- AI can attempt, but a human decides whether to pay.
Manual Post-Deployment Steps
AcceptanceCriteria includes checklist items marked as 'manual.' Reviewer checks these off separately from CI results. Both must pass for acceptance. Documented in Ghost Recorder audit trail.
Glen's Feedback Summary
Every piece of feedback, its architectural implication, and decision status
"Open to any revenue model"
Implication: Platform rake is configurable per-org, per-project, or per-hour. No need to hardcode a percentage. Start with whatever is easiest and adjust.
"Glen bills At Large at $90/hr. At Large bills MF at ~$125-135/hr."
Implication: The rate chain already exists in real life. The platform needs to model it: client rate, vendor rate, platform rake. Each hop takes a cut.
"I don't think the marketplace IS Delivery Hub -- I thought this was portal-only"
Implication: This is the key point this document addresses. See Section 2. The marketplace IS Delivery Hub because DH already does everything a marketplace needs: post work, claim it, deliver it, review it, pay. The portal is a window, not a separate system.
"Wants to understand the architecture before committing to implementation"
Implication: This document IS the understanding artifact. No code will be written until Glen reviews this page and makes the A vs B vs C decision. Reading this page should be sufficient to decide.
Open Questions Needing Answers
Remaining decisions before implementation. Glen's prior answers and follow-ups.
Rate chain rake -- what percentage, and when?
Glen's answer: "Open to any model."
Follow-up: Should we start with a simple 10% platform fee and adjust? Or no fee while it is just Glen + Untangleit? The system supports any percentage -- this is a business decision, not a technical one.
First bounty -- which MF WorkItem?
Glen's answer: "MF work, whatever is in WIP."
Follow-up: Want me to pull the current MF WorkItem list from Nimba and propose one that is well-scoped for Untangleit? Should be something with clear acceptance criteria and a reasonable NTE.
Untangleit onboarding -- how does Edson currently receive work?
No answer yet. Need to understand: Email? Jira? Just calls? What is the minimum viable improvement the marketplace provides over his current workflow?
Hub sandbox -- is Nimba permanent or do we need a dedicated marketplace hub?
No answer yet. Nimba Sandbox is the current hub for MF. As more clients onboard, does it stay as the central aggregator, or do we need a dedicated "marketplace hub" org? (Architecture C implication.)
Recruiter model -- how do recruiters find you today?
No answer yet. Word of mouth? LinkedIn? Do we need a recruiter-facing page on cloudnimbusllc.com? Or is the developer directory sufficient for recruiters to browse?
THE KEY QUESTION: Architecture A, B, or C?
This is the decision that unblocks everything else. The recommendation is B first (1-2 weeks, minimal risk), then evolve to C (multi-client routing, proven pattern). But Glen needs to review this page and decide.
A: Portal DB
4-6 weeks, full rewrite
B: DH Fields
1-2 weeks, start here
C: Hub Model
2-3 weeks, evolve to
Recommended Next Steps
Prioritized action items -- what to do and when
THIS WEEK: Launch March Invoice
UrgentEst: Same dayDocument Engine is already built. Enter hours for At Large (March 2026), select period, generate PDF, email with CC to glen@cloudnimbusllc.com. Independent of marketplace architecture decision.
Send David the /platform Walkthrough Link
This WeekEst: Same dayShare this architecture page + the platform overview page with David. Get his reaction to the A vs B vs C decision. His input matters because he has built similar systems.
Review This Architecture Page
This WeekEst: 1 hourGlen reads this page end-to-end. Makes the A vs B vs C decision. Resolves the open questions. This unblocks everything below.
If Architecture B/C: Add Bounty Fields to DH Package
NextEst: Half day~24 new fields on WorkItem__c, WorkRequest__c, NetworkEntity__c. DeliveryBountyController with 12 methods. Half a day of Apex work. Package version bump + CI.
Wire Bounty Board to Real Salesforce Data
NextEst: 1-2 daysReplace the mock bounty data on /bounties with real API calls through the /api/delivery-hub proxy. Connect to DeliveryBountyController.getPublicBounties().
Assign First MF WorkItem as Bounty to Untangleit
Week 2Est: 1 dayPick a well-scoped MF WorkItem. Use promoteToBounty() to publish it. Edson (Untangleit) claims it via the portal. Real test of the full flow: claim, develop, log hours, submit, review, accept, invoice.
Build Portal Hour Logging for Developers
Week 2Est: 2-3 daysSo Untangleit can log time through cloudnimbusllc.com against all lifecycle phases. Uses existing WorkLog__c + proxy pattern. Developer-facing UI for creating, editing, and submitting hours.
Generate Contractor Agreement + Invoice via Document Engine
Week 3Est: 1 dayFull dogfood loop: Untangleit completes bounty -> Document Engine generates contractor agreement + invoice -> PDF emailed to all parties -> payment via Melio. This closes the financial loop.
Timeline Summary
Steps 1-3 can happen this week (independent of architecture decision). Steps 4-5 take 1-2 weeks and require the architecture decision. Steps 6-8 are the real-world test with Untangleit and complete the first full marketplace loop. Total: 3-4 weeks from decision to first completed bounty.