Custom fields on every entity
Your org defines the schema, no code and no migration. They render on the web forms and the phone forms, filter in list views, and come back through the API. Retiring a definition is a soft delete, so the values already recorded stay readable.
A documented JSON API
JWT-authenticated, every entity, with a generated OpenAPI schema and both Swagger and Redoc served by the backend itself. The mobile app is a client of it, which is the only guarantee that matters: the API is not a side door, it is the door.
Tenant isolation in the database
Postgres row-level security, keyed on the organisation in your token and applied by the database rather than by application code. A query that forgets its filter returns nothing instead of returning someone else.
Attachments where the work happens
Files on leads, contacts, tasks and tickets from either client, and on deals from the phone. An account takes an attachment through the API with no screen calling it yet, and an invoice does not take one at all.
Two roles, and no role matrix
Admin and User. That is the whole list, and saying so is more useful than a permissions grid you would have to test. A self-hoster's Django superuser counts as an admin in every organisation they belong to. Access is decided by role plus ownership plus organisation, and the checks live in the API rather than in the interface.
Saved views on six lists
Filter leads, contacts, accounts, deals, tickets or invoices the way you work them, save it under a name, and open it again with one tap. A view saved on the web opens on the phone. Views are private to whoever saved them.
An audit log for admins
Sign-ins, organisation switches, refused requests and paused webhooks, newest first and filterable by event, person and date, in Settings on the web and the phone. Read-only, and closed to API tokens.
Hidden looks the same as missing
A record in your own organisation that you are not allowed to open answers exactly like one that does not exist, on every API call. Nobody learns that an id is real by being refused it.
The numbers you can act on
Ticket analytics by agent and by queue with customer satisfaction beside them, a weighted pipeline total, and a real accounts-receivable aging report with buckets rather than a single overdue flag. Not a dashboard builder.