Skip to content

Custom field definitions API

The endpoints under /api/custom-fields/ manage the schema for custom fields, the field definitions admins create in the settings UI. The actual values you store on individual records use each entity's regular endpoint (see Custom fields feature).

Permissions

  • List / read: any authenticated user (the frontend needs definitions to render detail-page forms).
  • Create / update / delete: admin role only.

List definitions

GET /api/custom-fields/?target_model=Lead&active_only=true
Authorization: Bearer <token>
{
  "definitions": [
    {
      "id": 17,
      "target_model": "Lead",
      "key": "industry",
      "label": "Industry",
      "field_type": "dropdown",
      "options": [
        { "value": "saas", "label": "SaaS" },
        { "value": "manufacturing", "label": "Manufacturing" }
      ],
      "is_required": false,
      "is_filterable": true,
      "is_active": true,
      "display_order": 0
    }
  ]
}

Query parameters:

  • target_model: filter by entity name (Lead, Account, Contact, Opportunity, Task, Case, Invoice, Estimate, RecurringInvoice).
  • active_only=true: exclude soft-deleted definitions.

Create

POST /api/custom-fields/
{
  "target_model": "Lead",
  "key": "industry",
  "label": "Industry",
  "field_type": "dropdown",
  "options": [
    { "value": "saas", "label": "SaaS" },
    { "value": "manufacturing", "label": "Manufacturing" }
  ],
  "is_required": false,
  "is_filterable": true,
  "display_order": 0,
  "is_active": true
}

Constraints:

  • key must match ^[a-z][a-z0-9_]*$ and be unique per (org, target_model).
  • options is required only when field_type is dropdown.
  • target_model must be one of the nine supported entities.

Update

PUT /api/custom-fields/<id>/
{ "label": "Industry Vertical", "options": [ … ] }

Immutable fields: key and field_type cannot change after creation. The API returns 400 if either is in the body.

Delete (soft)

DELETE /api/custom-fields/<id>/

Sets is_active=false. Existing values on records are preserved; new writes for the deactivated key are rejected as unknown. Reactivate with PATCH { "is_active": true }.

Errors

{
  "error": true,
  "errors": {
    "key": "must match ^[a-z][a-z0-9_]*$",
    "options": "Dropdown fields require a non-empty list of {value, label} pairs"
  }
}

See Errors for the full error response shape.