Contacts, organizations, deals, services, and tasks share one layout in the Virtual Marketer CRM web app (list, drawer, detail), with stable anchor ids for AI agents driving the UI.
Virtual Marketer CRM has five record types: Contacts, Organizations, Deals, Services, and Tasks. They all use the same screen layout, so this page documents them together for people and for AI agents that drive the UI.
Every record type has the same three surfaces:
/{type} (for example /contacts), locale-prefixed like https://crm.virtual-marketer.de/en/contacts. Search, filter, sort, and bulk actions live here.?open={entityType}:{id} to the list URL (for example /contacts?open=contact:{id}) to edit a record over the list./{type}/{id} (for example /contacts/42) for the full record, custom fields, and delete.Rows never carry a DOM id. Open a record by its URL or the drawer deep-link, never by clicking a row you located by id.
Each record type has its own route and its own toolbar ids, prefixed with the type. The four toolbar controls behave the same everywhere: Add opens the create drawer, Search live-filters the list, Filter opens the filter popover (filter syntax), and Display options controls columns, sorting, and view mode.
| Record type | Route | entityType | Sidebar | Toolbar: add, search, filter, display options |
|---|---|---|---|---|
| Contacts | /contacts | contact | #nav-contacts | #contacts-add, #contacts-search, #contacts-filter, #contacts-display-options |
| Organizations | /organizations | organization | #nav-organizations | #organizations-add, #organizations-search, #organizations-filter, #organizations-display-options |
| Deals | /deals | deal | #nav-deals | #deals-add, #deals-search, #deals-filter, #deals-display-options |
| Services | /services | service | #nav-services | #services-add, #services-search, #services-filter, #services-display-options |
| Tasks | /tasks | task | #nav-tasks | #tasks-add, #tasks-search, #tasks-filter, #tasks-display-options |
These ids are shared by every record type.
| Action | Where | Anchor id | What happens |
|---|---|---|---|
| Save (drawer) | Drawer footer | #drawer-save | Persists edits made in the drawer |
| Save / Reset (detail) | Detail topbar | #entity-save / #entity-reset | Persists or discards edits |
| Edit fields | Detail topbar | #entity-edit-fields | Toggles field editing |
| Add custom field | Detail topbar | #entity-add-custom-field | Creates a custom column |
| Delete | Detail topbar | #entity-delete | Opens the confirmation modal |
| Confirm / cancel delete | Confirmation modal | #confirm-delete / #confirm-delete-cancel | Irreversible delete or abort |
| Bulk delete | Selection bar after checkbox-select | #mass-delete | Deletes every selected row |
| Suggest emails | Selection bar, contacts only | #mass-infer-email | Proposes an address for each selected contact without one, from the pattern colleagues at the same domain use; nothing is written until confirmed |
Notes are a labeled field inside the form. Edit them there, then save.
Any record type can carry user-defined custom columns. There are no fixed workflow fields baked into a type, so there is no product-level deal stage or task status. get_record_schema returns whatever columns a workspace has configured for each type.
A singleSelect custom column can drive a board (kanban) view. #{type}-display-options switches the list between a table and a board grouped by a chosen singleSelect column. On the board, drag a record between columns to change that column's value; the equivalent in the drawer or detail form is to set the field and save. This applies to every record type that has at least one singleSelect column, not only to deals.
The demo workspace ships example columns to illustrate the pattern, for instance a "Status" column on deals and "Status" and "Priority" columns on tasks. These are seeded examples, not fixed behavior. Rename them, change their options, add more, or remove them per workspace.
Records link to other records through labeled relation fields inside the form. Pick the records, then save.
An organization custom column of type date can fill itself. Open the column in the custom field editor and pick a field under "Fill automatically when … is set". The first time that field gets a value on an organization, today's date (Europe/Berlin) is written into the date column. A date that already holds a value is never overwritten, and clearing the trigger field later leaves the date as it is.
Any other organization custom column can be the trigger except another date column. Choose "Do not fill automatically" to switch it off. Deleting either column removes the setting. Over MCP the same setting is manage_custom_columns with set_auto_date or remove_auto_date, and get_record_schema shows it as autoFilledFrom on the date column.
Custom fields appear in forms and tables in alphabetical order. To move one up, open it in the custom field editor and enter a number under "Position": a lower number comes first, and every field without a position follows alphabetically. Clearing the number removes the position. A text field (plain) with "Multi-line" shows as a growing text area instead of a single line, suited to longer text such as e-mail drafts or reasoning.
Every custom field has three more settings in the editor for the Virtual Marketer Agent. The description for the agent is the explicit rule of the field, for example "subject at most 60 characters, no emoji"; the agent reads it before every write, and it wins over anything learned. Learn from edits turns every change a person makes into a learning signal: correct an agent draft and the next drafts follow. Review changes critically is meant for consequential fields such as target group or style: an agent then changes the value only with a reason that is consistent with the briefing, and the reason is kept in the change history. Your own edits in the app are not affected. More in The Virtual Marketer Agent.
document.querySelector('#deals-add'). Ids are identical across locales and viewports; visible labels are not./{type}/{id} for the full page, or /{type}?open={entityType}:{id} for the drawer. Do not try to click a row.singleSelect column value, is one tool call instead of a click sequence.