Both sides of the same marketplace
One account, one record per project, and the paperwork and the money attached to it. Here is the whole path from either direction.
If you hire crews
General contractor
Company name, city, the trades you hire and how far your sites spread. Two minutes, and it decides which crews get suggested to you.
Four steps: basics, scope and requirements, budget range and dates, then review. Publishing notifies every matching crew inside range straight away.
Bids land in one table — price, crew size, earliest start, and the note explaining what is in and what is out. The spread across all bids is shown so an outlier is obvious.
Licence and insurance carry an expiry date. A lapsed certificate changes the badge and drops that company out of the compliance filter — you find out before the start date, not after.
Award from the project page. Milestone payouts run through Stripe Connect, so the awarded amount, the platform fee and the release status stay on one chain.
If you look for work
Subcontractor
Trades you genuinely self-perform, crew size, and the radius you will actually travel. Both are filters, so an honest radius earns you better matches than a hopeful one.
Your dashboard shows open work in your trades inside your radius that you have not already bid on. Nothing else.
Price, crew size, earliest honest start and a note. The form tells you where your number sits against the posted range and what you keep after the platform fee.
Messages attach to the project record, so a schedule question sits next to the bid it affects rather than in a separate inbox.
Stripe Connect handles identity and bank verification once. After that, milestone releases transfer straight to your account and the dashboard shows what is released and what is still held.
What happens to your data
Bids are the most sensitive thing on a marketplace, so access is enforced in the database rather than in application code. A subcontractor can read only its own bids; the contractor who owns a project can read every bid on it; nobody else can read either. That rule lives in a row-level security policy in Postgres, which means it holds even if a new page forgets to check.