Attachments are uploaded through module-specific routes (see the API pages for Sales, Purchases, Expenses, Compliance) but downloaded/deleted through a single generic pair:
| Method & Path | Permission | Purpose |
|---|---|---|
GET attachments/{attachment}/download | Derived from the parent record's module (sales/purchase/expense/compliance/payment) .view | Stream the file |
DELETE attachments/{attachment} | Derived module .manage (or compliance.generate) | Delete attachment; audit |
Module-specific upload/list endpoints (all POST/GET on {module}/{record}/attachments): invoices, bills, expenses, WHT credits, payments. Max size 10MB, allowed MIME types: pdf,png,jpg,jpeg,gif,webp,csv,xlsx,xls,doc,docx,txt,xml. Files are stored on the default disk (FILESYSTEM_DISK — Cloudflare R2 in production) under a per-tenant/per-model path with a randomized filename component — never given a public URL. See Attachments & File Storage and Security.