Freight agents in Brazil work over WhatsApp and depend on a third-party load board to find drivers. Brasa Cargas is a freight marketplace for Southeast Brazil that closes deals on WhatsApp: companies, carriers and freight agents post loads, drivers find them on Google or through a shared link, and talk directly to whoever posted.
The freight agent is the customer. Posting is free and needs no card; paid plans let an agent keep more loads live at once. Drivers don't pay and don't install an app: they open it on the phone, sign in with Google and they're in. They are the liquidity that makes a posted load worth something.
I founded it in April 2026 as Central de Cargas Ceasa, and it became Brasa Cargas on 29/06/2026. I build the product on my own, from architecture to operations, with Claude Code and Codex agents and cross-model review.
>High-level design
The application runs on Vercel: a Next.js 16 app (App Router, React 19) on Vercel Functions. A middleware records where the first visit came from and rewrites a freight agent's subdomain to the page with their brand. Agents and drivers open server-rendered pages; writes go through Server Actions and through route handlers under /api, which also receive the Stripe, Asaas and Twilio webhooks and exchange the sign-in code with Google.
State lives on Railway. Postgres, through Drizzle, holds the whole business. Redis only backs rate limiting. Load photos, driver documents and logos go to an S3-compatible bucket, uploaded straight from the browser through presigned URLs.
The async part is eight Vercel crons that call /api routes with a secret. Load expiry runs every hour and the trial lifecycle once a day; Pix reconciliation, notices and the dispatcher for a domain-event outbox in Postgres, every two minutes, sit behind flags. The commission with a split through the agent's subaccount is built and switched off. Code lives on GitHub: a branch push becomes a Vercel preview, production is built only with approval, and that build applies the Drizzle migrations before next build. There is no GitHub Actions; lint, types, tests, build and a migration check run in a pre-push hook.
Hover or focus a piece: it lights up with its links.
14 pieces15 links5 areas
Components
Next.js app on Vercel :: Public load and city pages, the freight agent's panel, the driver area and admin, server-rendered on Vercel Functions, with a middleware in front for attribution and the agent's branded subdomain.
Route handlers /api :: The same Vercel Functions serve checkout, upload and sign-in routes and the Stripe, Asaas and Twilio webhooks; they sign the bucket URLs and call the providers.
Vercel Cron :: Eight jobs that call /api/cron routes with a secret; load expiry and the trial lifecycle run as is, while Pix reconciliation, notices and the event outbox sit behind flags.
Postgres on Railway :: 76 tables and 121 migrations through Drizzle, with tenants by agent company, drivers as a global identity and a domain-event outbox in the same database.
Redis and bucket on Railway :: Redis only for rate limiting; an S3-compatible bucket for photos, documents and logos, with presigned URLs and direct browser uploads.
Payments :: Card subscriptions through Stripe (checkout, portal and webhook) and Pix through Asaas (charge and webhook); the commission through the agent's subaccount is switched off.
Sign-in and messaging :: Google OAuth with PKCE and a magic link to sign in, the agent's OTP through Twilio Verify, and WhatsApp through Twilio with a signed inbound webhook.
Deploy and observability :: GitHub for code, a preview per branch and approval-gated production on Vercel, migrations in the production build, a local pre-push gate; Sentry for errors, Vercel Analytics and GA4 for usage.
>Trade-offs
Agent subscriptions as the only revenue for the first 90 days.
Alternative rejected: Counting commission, escrow or a per-freight fee as revenue from the start.
Why: Subscriptions were the only money rail that could turn into real revenue that quarter; commission and freight payments depend on a legal opinion and a payment provider (ADR 0018).
Commission charged in Pix from the freight agent's own subaccount at the payment provider, with a native split of a fixed share to the platform.
Alternative rejected: The platform collecting the commission in its own account and paying it out later, or billing its fee as a separate charge.
Why: Taking in and paying out third-party money would push the platform toward being a payment institution, and paying the driver for the freight has its own rules (Law 11.442, CIOT, the ANTT freight floor) that stay outside the platform (ADR 0017, 0023 and 0024). The cost: the architecture is approved, production charging is not, and the rail stays off.
WhatsApp through an official channel (Twilio as BSP) and wa.me links.
Alternative rejected: An unofficial WhatsApp Web bot, or wiring Meta's Cloud API directly.
Why: The bot breaks the terms of service and the number can be banned, which contradicts the product's anti-ban stance. The Cloud API setup stalled in Meta's console; Twilio provided a number and a sandbox right away.
Driver list with no CPF, plate or phone; the phone shows only after the driver expressed interest in one of that agent's loads.
Alternative rejected: Using the company as the access boundary for driver data.
Why: Self-serve agents shared the same company, which would have given lateral access to every driver. Reveals are capped at 30 a day and audited in the service layer (ADR 0020, LGPD).
Notify drivers first through an inbox in their dashboard.
Alternative rejected: Starting with Web Push.
Why: The browser permission prompt can be blocked for good and iOS only delivers push to an installed PWA. Push waits until drivers are shown to come back to the dashboard (ADR 0012).
>Highlights
>700+ registered drivers and 2,700+ freight posts.
>Drivers join without installing an app: Google sign-in in 30 seconds, on the phone.
>2,458 commits on main between 14/04 and 20/10/2026.
>121 database migrations and 76 Postgres tables.
>787 test files and 75 Playwright E2E specs.
>A freight calculator based on the ANTT floor, plus origin and destination city pages.
>Fun facts
*The magic-link login splits GET from POST because WhatsApp, iMessage and Telegram fetch the link to build a preview. If GET consumed the token, the preview would burn the login before the user ever tapped it.
*21/07/2026 was the busiest day in the repo: 195 commits. July as a whole added up to 1,147.
*The first commit is from 14/04/2026, and 15 commits landed on main that day.