CRM
Activity Calls, emails, meetings, demos, and follow-ups -- every interaction logged against contacts and deals.
import { Noun } from 'digital-objects'
export const Activity = Noun ( 'Activity' , {
subject: 'string!' ,
type: 'Call | Email | Meeting | Task | Note | Demo | FollowUp' ,
description: 'string' ,
deal: '-> Deal.activities' ,
contact: '-> Contact.activities' ,
organization: '-> Organization' ,
campaign: '-> Campaign' ,
assignee: '-> Contact' ,
createdBy: '-> Contact' ,
dueAt: 'datetime' ,
startAt: 'datetime' ,
endAt: 'datetime' ,
duration: 'number' ,
allDay: 'boolean' ,
timezone: 'string' ,
status: 'Pending | InProgress | Completed | Cancelled' ,
priority: 'Low | Medium | High | Urgent' ,
completedAt: 'datetime' ,
outcome: 'string' ,
recordingUrl: 'string' ,
meetingLink: 'string' ,
reminderAt: 'datetime' ,
complete: 'Completed' ,
cancel: 'Cancelled' ,
log: 'Logged' ,
})
Field Type Required Description subjectstring Yes Activity subject line or title typeenum No Call, Email, Meeting, Task, Note, Demo, or FollowUp descriptionstring No Detailed notes or body content deal-> Deal No Deal this activity relates to contact-> Contact No Contact this activity involves organization-> Organization No Organization this activity relates to campaign-> Campaign No Campaign this activity is part of assignee-> Contact No Person responsible for this activity createdBy-> Contact No Person who created this activity dueAtdatetime No Deadline for tasks and follow-ups startAtdatetime No Start time for meetings and calls endAtdatetime No End time for meetings and calls durationnumber No Duration in minutes allDayboolean No Whether this is an all-day event timezonestring No IANA timezone for the activity statusenum No Pending, InProgress, Completed, or Cancelled priorityenum No Low, Medium, High, or Urgent completedAtdatetime No Timestamp when the activity was completed outcomestring No Result or notes from the completed activity recordingUrlstring No URL to a call or meeting recording meetingLinkstring No Video meeting URL reminderAtdatetime No Timestamp for a reminder notification
Field Direction Target Description deal-> Deal.activities The deal this activity is part of contact-> Contact.activities The contact this activity involves organization-> Organization The organization this activity relates to campaign-> Campaign Marketing campaign this activity belongs to assignee-> Contact Person assigned to complete this activity createdBy-> Contact Person who logged this activity
Verb Event Description createCreatedCreate a new activity updateUpdatedUpdate activity fields deleteDeletedDelete an activity completeCompletedMark the activity as done with an outcome cancelCancelledCancel a pending or in-progress activity logLoggedLog a past activity (call notes, meeting summary)
import { Activity } from '@headlessly/crm'
// BEFORE hook -- validate completion
Activity. completing ( activity => {
if ( ! activity.outcome) throw new Error ( 'Outcome required when completing an activity' )
})
// Execute -- complete the activity
await Activity. complete ( 'activity_e5JhLzXc' )
// AFTER hook -- schedule follow-up
Activity. completed (( activity , $ ) => {
if (activity.type === 'Demo' ) {
$.Activity. create ({
subject: `Follow up after demo: ${ activity . subject }` ,
type: 'FollowUp' ,
contact: activity.contact,
deal: activity.deal,
assignee: activity.assignee,
dueAt: new Date (Date. now () + 2 * 86_400_000 ). toISOString (),
status: 'Pending' ,
priority: 'High' ,
})
}
})
Pending --> InProgress --> Completed
\ \
\ \
-----------> Cancelled
Pending : Scheduled or created, not yet started
InProgress : Currently underway (e.g., an ongoing meeting)
Completed : Finished with an outcome recorded
Cancelled : Cancelled before completion
Type Use Case Typical Fields CallPhone calls, voice chats duration, recordingUrl, outcomeEmailEmail correspondence description (email body), outcomeMeetingScheduled meetings startAt, endAt, meetingLink, durationTaskAction items and to-dos dueAt, priority, assigneeNoteInternal notes and observations descriptionDemoProduct demonstrations startAt, endAt, meetingLink, outcomeFollowUpScheduled follow-up actions dueAt, priority, assignee
Activity is the interaction log that gives context to every other entity:
CRM (Deal) : Activities track every touchpoint on a deal. Deal.activities gives a timeline of the entire sales process. Deals without recent activities may trigger rotting alerts.
CRM (Contact) : Contact.activities shows the full interaction history with a person. The lastEngagement field on Contact is derived from the most recent activity.
Marketing : Campaign-linked activities track outreach and engagement. Demo requests from campaigns feed back into attribution.
Support : Support interactions can be logged as activities to maintain a unified timeline.
Analytics : Activity events (created, completed, cancelled) feed engagement metrics. Activity counts per deal stage reveal bottlenecks.
import { Activity } from '@headlessly/crm'
// Update the contact's last engagement when an activity is logged
Activity. logged (( activity , $ ) => {
if (activity.contact) {
$.Contact. update (activity.contact, {
lastEngagement: new Date (). toISOString (),
})
}
})
import { Activity } from '@headlessly/crm'
// Find overdue tasks
const overdue = await Activity. find ({
type: 'Task' ,
status: 'Pending' ,
dueAt: { $lt: new Date (). toISOString () },
})
// Get activities for a specific deal
const dealActivities = await Activity. find ({
deal: 'deal_k7TmPvQx' ,
status: 'Completed' ,
})
// Log a completed call
await Activity. log ({
subject: 'Discovery call with Alice' ,
type: 'Call' ,
contact: 'contact_fX9bL5nRd' ,
deal: 'deal_k7TmPvQx' ,
duration: 30 ,
outcome: 'Interested in enterprise plan, scheduling demo next week' ,
status: 'Completed' ,
})
{
"type" : "Activity" ,
"filter" : { "deal" : "deal_k7TmPvQx" , "type" : "Meeting" },
"sort" : { "startAt" : "desc" },
"limit" : 10
}
# List activities for a deal
curl https://crm.headless.ly/~acme/activities?deal=deal_k7TmPvQx
# Get a specific activity
curl https://crm.headless.ly/~acme/activities/activity_e5JhLzXc
# Create a meeting activity
curl -X POST https://crm.headless.ly/~acme/activities \
-H 'Content-Type: application/json' \
-d '{"subject": "Demo with Acme", "type": "Demo", "contact": "contact_fX9bL5nRd", "deal": "deal_k7TmPvQx", "startAt": "2025-03-15T14:00:00Z", "duration": 45}'
# Complete an activity
curl -X POST https://crm.headless.ly/~acme/activities/activity_e5JhLzXc/complete