# Verbs (/reference/concepts/verbs)



Conjugation Pattern [#conjugation-pattern]

Every verb in the system follows the same four-part conjugation:

```
verb
  ├── verb()        → execute (imperative)
  ├── verbing()     → BEFORE hook (present participle)
  ├── verbed()      → AFTER hook (past tense)
  └── verbedBy      → audit trail (passive voice)
```

```typescript
import { Contact } from '@headlessly/crm'

// Execute the verb
await Contact.qualify({ id: 'contact_fX9bL5nRd' })

// BEFORE hook — runs before execution
Contact.qualifying(contact => {
  if (!contact.email) throw new Error('Cannot qualify without email')
  return contact
})

// AFTER hook — runs after execution
Contact.qualified((contact, $) => {
  $.Issue.create({
    title: `Follow up with ${contact.name}`,
    contact: contact.$id,
    status: 'Open',
  })
})

// Audit — queryable reverse lookup
const events = await Contact.qualifiedBy('agent_mR4nVkTw')
```

Default CRUD Verbs [#default-crud-verbs]

Every Noun receives three CRUD verbs automatically. No declaration needed.

| Verb     | Participle | Past Tense | Audit       | Event            |
| -------- | ---------- | ---------- | ----------- | ---------------- |
| `create` | `creating` | `created`  | `createdBy` | `Entity.Created` |
| `update` | `updating` | `updated`  | `updatedBy` | `Entity.Updated` |
| `delete` | `deleting` | `deleted`  | `deletedBy` | `Entity.Deleted` |

```typescript
import { Contact } from '@headlessly/crm'

// Full CRUD conjugation — available on every entity
Contact.creating(contact => { /* validate before create */ })
const created = await Contact.create({ name: 'Alice', stage: 'Lead' })
Contact.created(contact => { /* react after create */ })

Contact.updating((contact, changes) => { /* validate before update */ })
await Contact.update('contact_fX9bL5nRd', { stage: 'Qualified' })
Contact.updated(contact => { /* react after update */ })

Contact.deleting(contact => { /* guard before delete */ })
await Contact.delete('contact_fX9bL5nRd')
Contact.deleted(contact => { /* clean up after delete */ })
```

To remove a CRUD verb, set it to `null` in the Noun definition. See [Digital Objects](/docs/reference/concepts/digital-objects) for details.

Custom Verb Declaration [#custom-verb-declaration]

Custom verbs are declared as properties on the Noun definition. The key is the verb infinitive; the value is the PascalCase past-tense event name:

```typescript
import { Noun } from 'digital-objects'

export const Deal = Noun('Deal', {
  name: 'string!',
  value: 'number!',
  stage: 'Prospecting | Qualification | Proposal | Negotiation | Closed | Won | Lost',
  contact: '-> Contact.deals',

  advance: 'Advanced',
  close:   'Closed',
  lose:    'Lost',
  reopen:  'Reopened',
})
```

Each declaration generates the full conjugation:

| Declaration           | Execute          | Before             | After             | Audit             |
| --------------------- | ---------------- | ------------------ | ----------------- | ----------------- |
| `advance: 'Advanced'` | `Deal.advance()` | `Deal.advancing()` | `Deal.advanced()` | `Deal.advancedBy` |
| `close: 'Closed'`     | `Deal.close()`   | `Deal.closing()`   | `Deal.closed()`   | `Deal.closedBy`   |
| `lose: 'Lost'`        | `Deal.lose()`    | `Deal.losing()`    | `Deal.lost()`     | `Deal.lostBy`     |
| `reopen: 'Reopened'`  | `Deal.reopen()`  | `Deal.reopening()` | `Deal.reopened()` | `Deal.reopenedBy` |

BEFORE Hooks (Validation) [#before-hooks-validation]

BEFORE hooks run synchronously before the verb executes. They receive the entity (or creation payload) and can:

* **Validate** — throw to reject the operation
* **Transform** — return a modified entity to change what gets written
* **Enrich** — attach computed properties before persistence

```typescript
import { Contact } from '@headlessly/crm'
import { Subscription } from '@headlessly/billing'

// Reject invalid operations
Contact.qualifying(contact => {
  if (!contact.email) throw new Error('Cannot qualify without email')
  if (!contact.organization) throw new Error('Contact must belong to an organization')
  return contact
})

// Transform before write
Subscription.creating(sub => {
  return { ...sub, startDate: new Date().toISOString() }
})
```

If a BEFORE hook throws, the verb does not execute and no event is emitted. The error propagates to the caller.

AFTER Hooks (Side Effects) [#after-hooks-side-effects]

AFTER hooks run asynchronously after the verb succeeds and the event is written. They receive the entity and a `$` context for cross-domain operations:

```typescript
import { Deal } from '@headlessly/crm'

Deal.closed((deal, $) => {
  // Cross-domain: create a billing subscription
  $.Subscription.create({ plan: 'pro', contact: deal.contact })

  // Cross-domain: update the contact stage
  $.Contact.update(deal.contact, { stage: 'Customer' })

  // Cross-domain: create onboarding content
  $.Content.create({
    title: `Onboarding: ${deal.name}`,
    type: 'Article',
    status: 'Draft',
  })
})
```

The `$` argument provides access to all 35 entities across all domains. Import it from `@headlessly/sdk` when used outside a hook:

```typescript
import { $ } from '@headlessly/sdk'
```

Verb Execution Across Interfaces [#verb-execution-across-interfaces]

The same verb works identically across all five interfaces:

SDK [#sdk]

```typescript
import { Contact } from '@headlessly/crm'

await Contact.qualify({ id: 'contact_fX9bL5nRd' })
```

MCP [#mcp]

```ts title="headless.ly/mcp#do"
await $.Contact.qualify({ id: 'contact_fX9bL5nRd' })
```

CLI [#cli]

```bash
npx @headlessly/cli do Contact.qualify contact_fX9bL5nRd
```

REST [#rest]

```bash
POST https://crm.headless.ly/~my-startup/Contact/contact_fX9bL5nRd/qualify
```

Events [#events]

```json title="headless.ly/mcp#search"
{ "type": "Event", "filter": { "type": "Contact.Qualified", "target": "contact_fX9bL5nRd" } }
```

All interfaces produce the same event, the same audit trail, and the same hook execution.

Code-as-Data Execution [#code-as-data-execution]

Verb handlers (BEFORE and AFTER hooks) are serialized via `fn.toString()` and stored in the tenant's Durable Object. They execute inside the DO -- no separate function infrastructure, no cold starts, no network hops.

```
Handler registered → fn.toString() → stored in DO SQLite
Verb executed      → handler deserialized → runs in-DO → ~0ms latency
```

This means handlers have access to the full entity graph within the DO but cannot make external network calls. For external integrations, use WebSocket or Webhook subscription modes instead. See [Events](/docs/reference/concepts/events) for subscription mode details.

Complete Verb Table [#complete-verb-table]

All custom verbs across the 35 core entities:

Identity [#identity]

| Entity | Verb       | Event       | Description       |
| ------ | ---------- | ----------- | ----------------- |
| User   | `invite`   | `Invited`   | Send an invite    |
| User   | `suspend`  | `Suspended` | Suspend a user    |
| User   | `activate` | `Activated` | Activate a user   |
| ApiKey | `revoke`   | `Revoked`   | Revoke an API key |

CRM [#crm]

| Entity       | Verb       | Event       | Description                 |
| ------------ | ---------- | ----------- | --------------------------- |
| Contact      | `qualify`  | `Qualified` | Move from Lead to Qualified |
| Contact      | `capture`  | `Captured`  | Capture from form or import |
| Contact      | `assign`   | `Assigned`  | Assign to team member       |
| Contact      | `merge`    | `Merged`    | Merge duplicate records     |
| Contact      | `enrich`   | `Enriched`  | Enrich with external data   |
| Organization | `enrich`   | `Enriched`  | Enrich with external data   |
| Organization | `score`    | `Scored`    | Update organization score   |
| Deal         | `advance`  | `Advanced`  | Move to next pipeline stage |
| Deal         | `close`    | `Closed`    | Close as won                |
| Deal         | `lose`     | `Lost`      | Close as lost               |
| Deal         | `reopen`   | `Reopened`  | Reopen a closed deal        |
| Lead         | `convert`  | `Converted` | Convert lead to a deal      |
| Lead         | `lose`     | `Lost`      | Mark lead as lost           |
| Deal         | `win`      | `Won`       | Mark deal as won            |
| Activity     | `log`      | `Logged`    | Log an activity             |
| Activity     | `complete` | `Completed` | Mark activity complete      |
| Activity     | `cancel`   | `Cancelled` | Cancel an activity          |

Projects [#projects]

| Entity  | Verb       | Event       | Description              |
| ------- | ---------- | ----------- | ------------------------ |
| Project | `archive`  | `Archived`  | Archive a project        |
| Project | `complete` | `Completed` | Complete a project       |
| Project | `activate` | `Activated` | Activate a project       |
| Issue   | `assign`   | `Assigned`  | Assign to team member    |
| Issue   | `close`    | `Closed`    | Close the issue          |
| Issue   | `reopen`   | `Reopened`  | Reopen a closed issue    |
| Comment | `resolve`  | `Resolved`  | Resolve a comment thread |

Content [#content]

| Entity  | Verb       | Event       | Description                 |
| ------- | ---------- | ----------- | --------------------------- |
| Content | `publish`  | `Published` | Publish content             |
| Content | `archive`  | `Archived`  | Archive content             |
| Content | `schedule` | `Scheduled` | Schedule for future publish |
| Asset   | `process`  | `Processed` | Process uploaded asset      |

Billing [#billing]

| Entity       | Verb         | Event         | Description                      |
| ------------ | ------------ | ------------- | -------------------------------- |
| Subscription | `activate`   | `Activated`   | Activate subscription            |
| Subscription | `pause`      | `Paused`      | Pause subscription               |
| Subscription | `cancel`     | `Cancelled`   | Cancel subscription              |
| Subscription | `reactivate` | `Reactivated` | Restart a cancelled subscription |
| Subscription | `upgrade`    | `Upgraded`    | Move to a higher-value plan      |
| Subscription | `downgrade`  | `Downgraded`  | Move to a lower-value plan       |
| Subscription | `renew`      | `Renewed`     | Renew subscription               |
| Invoice      | `pay`        | `Paid`        | Mark invoice as paid             |
| Invoice      | `finalize`   | `Finalized`   | Finalize for payment             |
| Invoice      | `void`       | `Voided`      | Void an invoice                  |
| Payment      | `capture`    | `Captured`    | Capture payment                  |
| Payment      | `refund`     | `Refunded`    | Issue refund                     |

Support [#support]

| Entity | Verb       | Event       | Description              |
| ------ | ---------- | ----------- | ------------------------ |
| Ticket | `assign`   | `Assigned`  | Assign to agent          |
| Ticket | `escalate` | `Escalated` | Escalate priority        |
| Ticket | `resolve`  | `Resolved`  | Resolve the ticket       |
| Ticket | `reopen`   | `Reopened`  | Reopen a resolved ticket |
| Ticket | `close`    | `Closed`    | Close the ticket         |

Analytics [#analytics]

| Entity | Verb       | Event         | Description              |
| ------ | ---------- | ------------- | ------------------------ |
| Metric | `snapshot` | `Snapshotted` | Record a metric snapshot |
| Funnel | `activate` | `Activated`   | Activate a funnel        |
| Metric | `record`   | `Recorded`    | Record a metric value    |
| Metric | `reset`    | `Reset`       | Reset a metric           |
| Funnel | `analyze`  | `Analyzed`    | Analyze funnel data      |
| Goal   | `achieve`  | `Achieved`    | Mark goal as achieved    |
| Goal   | `complete` | `Completed`   | Complete a goal          |
| Goal   | `miss`     | `Missed`      | Mark goal as missed      |
| Goal   | `reset`    | `Reset`       | Reset goal progress      |

Marketing [#marketing]

| Entity   | Verb       | Event       | Description                |
| -------- | ---------- | ----------- | -------------------------- |
| Campaign | `launch`   | `Launched`  | Launch a campaign          |
| Campaign | `pause`    | `Paused`    | Pause a campaign           |
| Campaign | `complete` | `Completed` | Complete a campaign        |
| Segment  | `refresh`  | `Refreshed` | Refresh segment membership |
| Form     | `publish`  | `Published` | Publish a form             |
| Form     | `submit`   | `Submitted` | Record form submission     |
| Form     | `archive`  | `Archived`  | Archive a form             |

Experimentation [#experimentation]

| Entity      | Verb       | Event       | Description            |
| ----------- | ---------- | ----------- | ---------------------- |
| Experiment  | `start`    | `Started`   | Start an experiment    |
| Experiment  | `pause`    | `Paused`    | Pause an experiment    |
| Experiment  | `stop`     | `Stopped`   | Stop an experiment     |
| Experiment  | `conclude` | `Concluded` | Conclude with results  |
| FeatureFlag | `enable`   | `Enabled`   | Enable a feature flag  |
| FeatureFlag | `disable`  | `Disabled`  | Disable a feature flag |
| FeatureFlag | `rollout`  | `RolledOut` | Progressive rollout    |

Platform [#platform]

| Entity      | Verb         | Event          | Description                |
| ----------- | ------------ | -------------- | -------------------------- |
| Workflow    | `activate`   | `Activated`    | Activate a workflow        |
| Workflow    | `pause`      | `Paused`       | Pause a workflow           |
| Workflow    | `trigger`    | `Triggered`    | Trigger workflow execution |
| Workflow    | `archive`    | `Archived`     | Archive a workflow         |
| Integration | `connect`    | `Connected`    | Connect an integration     |
| Integration | `disconnect` | `Disconnected` | Disconnect an integration  |
| Integration | `sync`       | `Synced`       | Sync integration data      |
| Agent       | `do`         | `Done`         | Execute an action          |
| Agent       | `ask`        | `Asked`        | Ask for information        |
| Agent       | `decide`     | `Decided`      | Make a decision            |
| Agent       | `approve`    | `Approved`     | Approve an action          |
| Agent       | `notify`     | `Notified`     | Send a notification        |
| Agent       | `delegate`   | `Delegated`    | Delegate to another agent  |
| Agent       | `escalate`   | `Escalated`    | Escalate to a human        |
| Agent       | `learn`      | `Learned`      | Learn from interaction     |
| Agent       | `reflect`    | `Reflected`    | Reflect on outcomes        |
| Agent       | `invoke`     | `Invoked`      | Invoke agent execution     |
| Agent       | `deploy`     | `Deployed`     | Deploy an agent            |
| Agent       | `pause`      | `Paused`       | Pause an agent             |
| Agent       | `stop`       | `Stopped`      | Stop an agent              |
| Agent       | `retire`     | `Retired`      | Retire an agent            |

Communication [#communication]

| Entity  | Verb      | Event       | Description       |
| ------- | --------- | ----------- | ----------------- |
| Message | `send`    | `Sent`      | Send a message    |
| Message | `deliver` | `Delivered` | Mark as delivered |
| Message | `read`    | `Read`      | Mark as read      |
