Attach the file
Blake now accepts the file your client actually sent. Word, Excel, PowerPoint, PDF, CSV, images, .eml, .msg, plain text, up to 50 MB per opportunity. The browser uploads direct to storage, bypassing the 4.5 MB function payload cap that was silently rejecting most real documents. Quota is re-checked against the actual stored size, not what the browser claimed. The 50 MB number in the panel header is real now.
A prospect emails you a 30 MB proposal in Word. A signed contract comes back as a PDF. Someone exports their pipeline as a spreadsheet and forwards it for context. None of this is exotic. All of it is how deals actually move.
Until this week, Blake quietly refused most of it.
What broke
The Documents panel on every opportunity has been there from day one, with a 50 MB per-opp quota in the header. Behind the scenes, uploads ran through a Next.js server action, which routed the file's bytes through our function on Vercel before forwarding them to storage. Vercel caps function request bodies at 4.5 MB on the plan Blake runs on. Anything bigger hit a 413 well before our own quota arithmetic got a chance to look at the file.
The user saw a generic upload failure. Our quota check, the friendly error copy, the 50 MB number in the header: all of it was decoration on a system that was rejecting most real documents at the door.
What we changed
The file no longer routes through our function at all. The browser asks our server for a signed upload URL, PUTs the bytes directly to Supabase Storage, then tells our server "it landed." Our server stats the object, confirms the byte count, re-checks the quota against what is actually stored (not what the browser claimed), and writes the documents row.
Two practical consequences.
One. The 50 MB number in the header is real now. A 40 MB deck attaches. A 20 MB signed MSA attaches. A spreadsheet your prospect's procurement team sent at 8 MB attaches.
Two. A client lying about file size cannot blow the quota. The size we record is the size storage saw on disk. Storage is the source of truth.
What you can attach
PDF. Word, current and legacy (.docx and .doc). Excel (.xlsx and .xls). PowerPoint (.pptx and .ppt). CSV. Plain text and Markdown. PNG, JPG, GIF, WebP. Email exports as .eml or Outlook's .msg.
The list is "the file your client actually sent you." We are not interested in being the gatekeeper that turns away a signed contract because it came as .docx instead of .pdf. The picker filters to allowed types so you do not waste a click. The server enforces the same list independently, since browsers report Office formats inconsistently and we would rather accept a file by extension than reject one by accident.
What stays the same
Inbound email attachments still flow through the existing pipeline. Forward a thread, the email lands, the attachment is on the opp. That side is unchanged.
The 50 MB cap is per opportunity, not per workspace. Delete a stale draft, get the space back, attach the latest version. The quota is computed from the documents you can see, not from total bytes in our bucket.
Files are stored privately. Reads go through ten-minute signed URLs generated when you open the page. Nothing in the documents bucket is publicly addressable.
What is next
The matching inbound work is on the list. Today, Resend hands us email attachments by metadata and we have to fetch each one through a separate API call. We are wiring that up, with the same allowlist, so a 12 MB PDF attached to a forwarded email lands the same way a manual upload does today.
Always be closing. But first, attach the file.