-
Notifications
You must be signed in to change notification settings - Fork 2
Custom Fields
Workspace-scoped named fields on any entity type. Values live in each row's
data JSONB; this registry just declares which keys the workspace
expects so agents can see them.
The data JSONB column on every entity is free-form, which is great for
agent flexibility but bad for discoverability. Without a registry, an agent
syncing from Apollo might write data.linkedin, another writing from a
CSV might write data.linkedin_url, and a third might write data.li.
Now you've got three keys for the same concept.
Custom fields are the opt-in "yes, we track this; call it that" declaration.
Owners and admins:
curl -X POST http://localhost:8000/custom-fields \
-H "Authorization: Bearer nk_..." \
-d '{
"entity_type": "contact",
"name": "linkedin_url",
"label": "LinkedIn URL",
"field_type": "url",
"required": false,
"description": "profile URL on linkedin.com"
}'Fields:
| Field | Meaning |
|---|---|
entity_type |
contact | company | deal | activity | note | task
|
name |
snake_case key that goes into data.<name>. Unique per (entity_type, workspace). |
label |
human-readable; for UI / docs |
field_type |
string | text | number | bool | date | url | email | select
|
required |
advisory in v1 (no runtime validation yet) |
default_value |
JSONB; can be any shape |
options |
for select — list of allowed values |
description |
free-form |
curl -s http://localhost:8000/custom-fields?entity_type=contact \
-H "Authorization: Bearer nk_..."Members can read. The agent should call this at session start to know what
fields the workspace cares about, then set them in data.<name> on writes.
curl -X PATCH http://localhost:8000/custom-fields/<id> \
-H "Authorization: Bearer nk_..." \
-d '{"label":"LinkedIn","required":true}'
curl -X DELETE http://localhost:8000/custom-fields/<id> \
-H "Authorization: Bearer nk_..."Nakatomi doesn't alter the schema when you register a custom field. Values
live in the row's data JSONB. So a contact with a registered
linkedin_url field looks like:
{
"id": "...",
"first_name": "Ada",
"email": "ada@example.com",
"data": {
"linkedin_url": "https://linkedin.com/in/adalovelace"
}
}This keeps migrations free — registering or unregistering a field is a
row-level operation in custom_field_definitions, never a ALTER TABLE.
Filter on JSONB keys via Postgres-native operators:
-- contacts with a linkedin_url set
SELECT * FROM contacts
WHERE workspace_id = '...'
AND data ? 'linkedin_url';
-- deals tagged as strategic in data
SELECT * FROM deals
WHERE workspace_id = '...'
AND data->>'strategic' = 'true';The REST list endpoints don't accept JSONB filters as query params in
v1 — that's on the roadmap. For now, if an agent needs to filter by a
custom field, it paginates and filters client-side, or queries Postgres
directly (advanced).
GET /schema surfaces the custom_field entity pointer so agents know
the endpoint exists. The per-workspace field list comes from
GET /custom-fields — call both for full context.
- Runtime validation (required fields enforced on write)
- Typed coercion (e.g.
numberfields validated as numeric on write) - REST filter params (
?data.linkedin_url=https://...) - MCP tools for custom field management
- UI surface on the dashboard
Repository · Issues · MIT licensed · maintained by Matt Dula