Custom fields
Custom fields let an organization extend any supported entity with schema it controls — without touching the codebase. Admins create field definitions in the settings UI; the new fields immediately become available on detail pages, in list filters, and through the JSON API.
Supported entities
| Entity | Use cases |
|---|---|
| Lead | Industry vertical, lead source detail, BANT scoring |
| Contact | LinkedIn URL, preferred channel, DOB |
| Account | ABM tier, ARR, renewal date |
| Opportunity | Competitor, expected delivery date |
| Task | Estimated hours, billable flag |
| Case (Ticket) | Severity, SLA, escalation level |
| Invoice / Estimate / RecurringInvoice | PO number, tax exemption ID, payment terms |
Field types
- text — single-line string.
- textarea — multi-line string.
- number — integer or float.
- checkbox — boolean.
- date — ISO
YYYY-MM-DD. - dropdown — pick from an admin-defined
[ { value, label } ]list.
Creating a field
In the web app:
- Go to Settings → Custom fields.
- Pick the entity (e.g. Leads).
- Click Add field and choose a key, label, type, and whether it is required or filterable.
A key is the JSON property name — it must match ^[a-z][a-z0-9_]*$ and is immutable once created. You can rename the label freely.
Reading and writing values
Every entity that supports custom fields exposes a custom_fields JSON object on its read serializer. For example, a lead with two custom fields:
{
"id": "ab12…",
"title": "Acme renewal interest",
"status": "qualified",
"custom_fields": {
"industry": "saas",
"bant_score": 72
}
}
To write values, include the same object in your POST or PATCH body. The backend validates every value against the org's active definitions:
- Unknown keys are dropped silently and logged.
- Dropdown values must match one of the allowed
values. - Required fields error out if neither the request nor the existing record carries a value.
Soft delete preserves history
When you delete a field definition, BottleCRM deactivates it instead of dropping the data. Existing records keep their values, but new writes for that key are rejected as unknown. Reactivate the definition any time and the data is back.
Filtering by custom fields
If a field is marked filterable, the list endpoint accepts cf_<key>=<value> query parameters:
GET /api/leads/?cf_industry=saas&cf_bant_score__gte=50
On the web app, those filters appear automatically in the list view's filter drawer.