Objects
Tables like
people, company, task, or any custom object.Fields
Typed columns: text, enum, relation, user, date, and more.
Entries
Rows, written one at a time or in batches up to 200.
Cells
Individual values, set, appended, or read per field.
Query
Filter, sort, paginate, search, and aggregate.
Statuses
Kanban columns with colors for any object.
people, company, and task are protected and cannot be deleted. Tasks are
CRM entries on the task object; dench crm tasks … adds shortcuts over it
(see Tasks).Create an object
1
Inspect what exists
2
Create it with an icon
3
Add fields
4
Optional: kanban statuses
Field types
- Common
- Enum
- Relation
- User
text, email, phone, url, number, boolean, date, file, tags.The protected Notes field
Every object ships a protected
Notes (richtext) field for free-form prose,
meeting notes, summaries, task details. It is hidden from list views and shown
only when an entry is opened. Do not invent parallel Description/Summary
columns, write Markdown to Notes.Write data
Read data
Assigned to me
--assigned-to me|unassigned|<userId|email> (alias --mine) narrows any of
these reads to rows whose people field includes that member, exactly like the
avatar pill next to Filter in the app. It is a personal scope, not a view
filter: nothing is saved onto the view. --assignee-field <Field> picks the
user field when an object has several; by default the analytics owner role,
then the task Assignee, then a field named like owner/assignee, then the first
user field. Over the API pass assignedTo / assigneeField. Counts, kanban
column sums and dench crm analytics compute --mine follow the same scope.
Tasks
Tasks are rows on the protectedtask object, so every entries/cells command
works on them. The shortcuts below wrap the same functions the To-Do(s) view
and the record panel use, and notify assignees.
Priority
Everytask object ships a Priority enum — Low, Medium, High — with no
default; a task without one sorts last. Priority drives the order of every
task surface: To-Do(s) (High → Medium → Low → unset, most recently active
within a level), the home rail, a record’s task strip, and the Tasks table
and kanban (whose default sort is Priority desc, Due Date asc).
Enum sorts follow option order, not spelling — Priority desc walks the
scale, and a Status sort follows the kanban columns. Lists that begin with an
obviously-top word (Urgent, High, P0) are read highest-first, so a
workspace that already modelled priority its own way ranks correctly too.
Priority accepts the option in any casing ("high" → "High"); an unknown
value is rejected with the valid options. In composers, Ship it !high (or
!h, !!!) sets it from the keyboard.
Subtasks
A subtask is an ordinary task whoseParent Task relation points at another
task — same object, same table, same kanban, one level deep. The relation is
created the first time a workspace nests a task, so workspaces that never do
never see the column.
- A new subtask inherits the parent’s record relations (Account, Contact, Opportunity …) so it groups under the same record on To-Do(s); it does not inherit Assignee, Due Date, Priority or Notes.
- Subtasks show as rows everywhere with a
↳ Parentchip; parents show a2/5progress ring. Completing the last subtask does nothing to the parent. View settings → Rows → Show subtasks hides them by writing aParent Task is emptyfilter rule (saveable into a view). POST /crm/tasks/{entryId}/subtasks,PUT /crm/tasks/{entryId}/parent({ "parentEntryId": "<id>" | null }) andDELETE /crm/tasks/{entryId}({ "subtasks": "delete" | "keep" }) are the API forms. Linking a task to a task throughPOST /crm/objects/task/entries/{id}/tasksalso creates a subtask.
Repeating tasks
A repeat is a property of the task’s Due Date — set it from the↻ Repeat control under the date (beside Remind). If the task has no due
date yet, picking a repeat sets one: the first day the rule matches from
today (Weekly on Mon chosen on a Thursday → due next Monday). Every state
reads as one sentence: “Repeats every 2 weeks on Mon · next Sep 22”. Presets come
from the due date (Daily · Every weekday · Weekly on Wed · Every 2 weeks ·
Monthly on the 17th · Yearly); Custom opens interval / weekdays / month
day or “2nd Tuesday” / ends; Advanced holds the trigger and what carries
over.
- A new task per occurrence. The finished one stays Done with its own
comments and history; occurrences are linked through a hidden
_Seriesfield (filter_Series is <seriesId>lists them;dench crm tasks series <id> --occurrencesdoes the same). - Trigger. When this one is done (default): completing the current
occurrence — from To-Do(s), the kanban,
dench crm cells set task <id> Status Done, or the API — creates the next one. On schedule: the next one is created at 00:05 local on its due date whether or not the current one is done. - Never overdue. If a late completion would land in the past, the next due date skips forward to the first occurrence from today. Month-end rules keep their intent (Jan 31 → Feb 28 → Mar 31); Skip weekends rolls Saturday/Sunday to Monday.
- What carries over: every field except Status and Due Date; Notes, Assignee and subtasks are on by default and toggleable; comments never. The assignee isn’t pinged when they just completed it themselves.
- Ends never, after N times, or on a date. Clearing the live task’s due
date pauses the series (“set a due date”); setting one resumes it. The
task page’s
↻line offers Skip this one · Pause / Resume · View all occurrences · Stop repeating; deleting the live occurrence asks This occurrence (the next is created now) or All future occurrences. - Subtasks can’t repeat on their own — set repeat on the parent (its subtasks copy to each occurrence).
rule and rrule, plus summary (the
sentence) and nextDueKey.
Activity and comments
Every record has an Activity feed under Notes: threaded comments merged with its change history.dench crm history reads the audit trail; dench crm comments reads and writes the conversation. See Comments.
Relation-first modeling
When to use a relation
When to use a relation
If a field name matches or aliases an existing object (Company, Client,
Deal, Owner), use a
relation (or user) field, not text.Auto-link defaults
Auto-link defaults
people → company, deal → people/company, task → people/project,
invoice → company/deal. Create the target object if it should exist.Batch everything
Batch everything
Use
entries create-many, entries update-many, cells set-many, or
batch --file for three or more writes.Who can see a record
Every object has avisibility that decides who can read its records. It is
a property of the object, and the same rule applies everywhere a record can
appear: lists, the table, search, the picker, aggregates, the API and the
agent’s tools.
meeting ships as owner_plus_members (the person who recorded it plus
everyone whose calendar held the same call) and calendar_event as
owner_only. Records on a non-workspace object are stamped with their
creator as owner, and only the owner can edit or delete them.
Switching an object that already has records to owner_only or
owner_plus_members hides every record that has no owner — including from the
admin who made the change — until an owner is set. Switch back to workspace
to see them again.
Only workspace admins can change it, including on protected objects (an
org-scoped API key cannot; a signed-in admin is required):
An org-scoped API key or an agent run with no signed-in person reads only
workspace objects. Sign in as a member (or bind the key to one) to read
meetings and calendar events.Meetings
A meeting is a CRM record on the protectedmeeting object. Create, update and
delete it like any other entry; the Meetings page reads the same rows.
dench meetings import --csv calls.csv | --jsonl calls.jsonl brings in many
calls at once: meeting fields per row plus optional transcript,
actionItems and attendees columns. Same --map, --dedupe-key and
--on-conflict flags as dench crm import. Attendees are linked to existing
people and members by email, never created. Run it signed in — meetings are
owner-scoped.
API: /v1/meetings, /v1/meetings/{id}/transcript, …/action-items,
…/participants.
Records Dench writes for you
Some objects are filled by Dench itself and cannot be created by hand. They are ordinary CRM records otherwise:dench crm entries list <object>, dench crm query, the API and the agent all read them.
A LinkedIn message files an
interaction with Type LinkedIn only when the
other person is a linked people record. Messages from a private LinkedIn
account keep their text out of the CRM until a viewer-aware read rule exists
for inbox rows.