// AUTO-GENERATED by scripts/generate-app-sdk.ts — do NOT edit by hand.
// Types for the `window.dench` App SDK injected into every Dench App iframe.
//
// ///
// /** @type {import("@dench.com/cli/app-sdk").Dench} */
// const dench = window.dench;
//
// There is NO runtime package: the SDK is already on window.dench.
/* eslint-disable */
// ── Operation request/response types (from cli/lib/api-schemas.ts) ──────
export interface ApiRequests {
/** List workspace-shared reusable setups and ready revisions (metadata only). — `dench env list` — GET /api/v1/environments */
"environments.list": {
limit?: number;
cursor?: string;
};
/** Inspect a saved setup, its manifest, and an optional immutable revision. — `dench env show` — GET /api/v1/environments/{name} */
"environments.get": {
name: string;
revision?: string;
};
/** Reserve an owner-bound source upload. Prefix the uploaded bytes with the returned proof before saving. — `dench env save` — POST /api/v1/environments/uploads */
"environments.upload": {
kind: "source";
size: number;
sha256: string;
};
/** Publish an explicitly saved source and manifest for one-time preparation. Returns pending/building until verified; does not run the task. — `dench env save` — POST /api/v1/environments/{name} */
"environments.save": {
name: string;
manifest: {
version: 1;
name: string;
purpose: string;
files: string[];
prepare: string[];
verify: string[];
env?: Record;
secretEnv?: Record;
secretFiles?: {
path: string;
secret: string;
mutable?: boolean;
}[];
statePaths?: string[];
portable?: boolean;
};
uploadId: string;
storageId: string;
sha256: string;
};
/** Retire a saved setup from new selection; retain immutable revisions and active job evidence. — `dench env delete` — DELETE /api/v1/environments/{name} */
"environments.delete": {
name: string;
};
/** List workspace-shared credential names, kinds and versions; never values. — `dench secrets list` — GET /api/v1/secrets */
"secrets.list": {
limit?: number;
cursor?: string;
};
/** Encrypt a workspace credential. Use CLI stdin or the secure chat form; return metadata only. Do not put values in chat, manifests or scripts. — `dench secrets set` — PUT /api/v1/secrets/{name} */
"secrets.set": {
name: string;
kind: "env" | "file";
value: string;
expectedVersion?: number;
};
/** Revoke a workspace credential for future resolution, optionally checking its version. — `dench secrets delete` — DELETE /api/v1/secrets/{name} */
"secrets.delete": {
name: string;
expectedVersion?: number;
};
/** Create an idempotent empty campaign draft with the caller's available sender pools. Does not launch or send. — `dench campaign draft` — POST /api/v1/campaigns/drafts */
"campaigns.drafts.create": {
/** Your stable id for this request, 8-64 characters. Retrying with the same id returns the same draft instead of creating another. */
clientRequestId: string;
name?: string;
/** IANA timezone, e.g. America/Los_Angeles. Sets a Mon-Fri 9-18 send window in it; without one the draft has no send window. */
timezone?: string;
/** Mailboxes to send from instead of your own running ones. Checked as the draft is created: one you can't use creates nothing. */
emailMailboxAccountIds?: string[];
/** LinkedIn accounts to send from instead of your own running ones. Checked as the draft is created. */
linkedinAccountIds?: string[];
};
/** Search people by filters without adding CRM records or campaign recipients. Search credits apply; this is not a free provider preview. — `dench campaign search` — POST /api/v1/campaigns/audience/search */
"campaigns.audience.search": {
sequenceId?: string;
filters: {
linkedinUrl?: string;
fullName?: string;
firstName?: string;
lastName?: string;
/** Current-title text only. For profile or work descriptions, use profileKeywords. Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
keywords?: string;
companyDomain?: string;
companyName?: string;
/** Current job titles (free text). Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
titles?: Array;
/** Past job titles (free text). */
pastTitles?: Array;
personNames?: Array;
/** Seniority in any past role, independent of pastCompanyNames/pastTitles. Current seniority vocabulary plus Founding Team. Founder includes Co-Founder; C-level and Founding Team do not imply founder. Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
pastSeniorities?: Array;
/** Text in headline/about or any current/past work description. OR includes, global exclusions. Always loose text matching; missing evidence does not match. */
profileKeywords?: Array;
/** Current employer business focus/model (e.g. SaaS, B2B, Cloud), from description/tagline/business tags; distinct from legal companyTypes. Same employer as other current-job filters. Always loose text matching. May need another filter to retrieve candidates. */
companyKeywords?: Array;
/** Exact enum: Owner, Founder, C-level, Partner, VP, Head, Director, Manager, Senior. */
seniorities?: Array;
/** Exact enum, e.g. Sales, Marketing, Software. Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
jobFunctions?: Array;
/** Exact enum, e.g. SDR/BDR, Demand Generation. Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
subFunctions?: Array;
/** Exact enum, e.g. Software Development. Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
industries?: Array;
/** Person locations — cities, states, countries, or metros (free text). Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
locations?: Array;
/** Exact buckets: 1-10, 11-50, 51-200, 201-500, 501-1000, 1001-5000, 5001-10000, 10001+. */
headcountRanges?: Array;
companyNames?: Array;
pastCompanyNames?: Array;
companyDomains?: Array;
/** Company HQ locations — cities, states, countries, or metros (free text). Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
companyHeadquarters?: Array;
/** Exact enum: Partnership, Nonprofit, Educational, Privately Held, Public Company, Self-Owned, Self-Employed, Government Agency. */
companyTypes?: Array;
/** Universities attended (free text). Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
universities?: Array;
/** Profile skills (free text). Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
skills?: Array;
/** Spoken languages, e.g. English, German. Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
languages?: Array;
yearsInRole?: {
min?: number;
max?: number;
};
yearsAtCompany?: {
min?: number;
max?: number;
};
daysSinceJobChange?: {
min?: number;
max?: number;
};
/** Company founded-year range, e.g. {min: 2015, max: 2024}. */
foundedYears?: {
min?: number;
max?: number;
};
/** Match free-text filters as whole strings. Default false: titles ['Founder'] also returns 'Co-Founder' and 'Founder & CEO'. Set true to narrow. Domains, LinkedIn URLs, enum filters and numeric ranges are always exact; keywords is always a loose text probe. */
exactMatch?: boolean;
preferCache?: boolean;
};
limit?: number;
pageToken?: string;
};
/** Search and add up to 50 people to an existing campaign. New identities become Leads. Search credits apply; use nextPageToken for another page. — `dench campaign add --search` — POST /api/v1/campaigns/{sequenceId}/people/search */
"campaigns.people.addFromSearch": {
sequenceId: string;
filters: {
linkedinUrl?: string;
fullName?: string;
firstName?: string;
lastName?: string;
/** Current-title text only. For profile or work descriptions, use profileKeywords. Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
keywords?: string;
companyDomain?: string;
companyName?: string;
/** Current job titles (free text). Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
titles?: Array;
/** Past job titles (free text). */
pastTitles?: Array;
personNames?: Array;
/** Seniority in any past role, independent of pastCompanyNames/pastTitles. Current seniority vocabulary plus Founding Team. Founder includes Co-Founder; C-level and Founding Team do not imply founder. Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
pastSeniorities?: Array;
/** Text in headline/about or any current/past work description. OR includes, global exclusions. Always loose text matching; missing evidence does not match. */
profileKeywords?: Array;
/** Current employer business focus/model (e.g. SaaS, B2B, Cloud), from description/tagline/business tags; distinct from legal companyTypes. Same employer as other current-job filters. Always loose text matching. May need another filter to retrieve candidates. */
companyKeywords?: Array;
/** Exact enum: Owner, Founder, C-level, Partner, VP, Head, Director, Manager, Senior. */
seniorities?: Array;
/** Exact enum, e.g. Sales, Marketing, Software. Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
jobFunctions?: Array;
/** Exact enum, e.g. SDR/BDR, Demand Generation. Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
subFunctions?: Array;
/** Exact enum, e.g. Software Development. Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
industries?: Array;
/** Person locations — cities, states, countries, or metros (free text). Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
locations?: Array;
/** Exact buckets: 1-10, 11-50, 51-200, 201-500, 501-1000, 1001-5000, 5001-10000, 10001+. */
headcountRanges?: Array;
companyNames?: Array;
pastCompanyNames?: Array;
companyDomains?: Array;
/** Company HQ locations — cities, states, countries, or metros (free text). Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
companyHeadquarters?: Array;
/** Exact enum: Partnership, Nonprofit, Educational, Privately Held, Public Company, Self-Owned, Self-Employed, Government Agency. */
companyTypes?: Array;
/** Universities attended (free text). Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
universities?: Array;
/** Profile skills (free text). Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
skills?: Array;
/** Spoken languages, e.g. English, German. Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
languages?: Array;
yearsInRole?: {
min?: number;
max?: number;
};
yearsAtCompany?: {
min?: number;
max?: number;
};
daysSinceJobChange?: {
min?: number;
max?: number;
};
/** Company founded-year range, e.g. {min: 2015, max: 2024}. */
foundedYears?: {
min?: number;
max?: number;
};
/** Match free-text filters as whole strings. Default false: titles ['Founder'] also returns 'Co-Founder' and 'Founder & CEO'. Set true to narrow. Domains, LinkedIn URLs, enum filters and numeric ranges are always exact; keywords is always a loose text probe. */
exactMatch?: boolean;
preferCache?: boolean;
};
limit?: number;
pageToken?: string;
checkDuplicatesAcrossWorkspace?: boolean;
};
/** Add up to 50 selected recipients without another search. Deduplicates, applies suppression, and preserves the campaign's launch state. — `dench campaign add --people-file` — POST /api/v1/campaigns/{sequenceId}/people/batch */
"campaigns.people.addBatch": {
sequenceId: string;
recipients: {
email?: string;
linkedinProfileUrl?: string;
displayName?: string;
/** Merge values keyed by name, e.g. {"Company":"Acme"} for {{Company}}. */
personalization?: Record;
}[];
newRecordsObject?: "lead" | "people";
checkDuplicatesAcrossWorkspace?: boolean;
};
/** Read a campaign's saved signal source, targeting and schedule. — `dench campaign sources get` — GET /api/v1/campaigns/{sequenceId}/sources */
"campaigns.sources.get": {
sequenceId: string;
};
/** Read signal choices, suggested terms and current source configuration. — `dench campaign sources signals` — GET /api/v1/campaigns/{sequenceId}/sources/signals */
"campaigns.sources.signals": {
sequenceId: string;
};
/** Update source cadence, pause/resume, targeting, signals or terms. Active sources may perform billable future work. — `dench campaign sources set` — PATCH /api/v1/campaigns/{sequenceId}/sources */
"campaigns.sources.update": {
sequenceId: string;
status?: "active" | "paused";
cadence?: "daily" | "weekly" | "monthly";
findMoreMatching?: boolean;
/** auto: new people a signal finds start the sequence on their own. manual: they wait for approval. */
approvalMode?: "auto" | "manual";
targeting?: {
titles: string[];
locations: string[];
companySizes: string[];
industries: string[];
excludes: string[];
companies?: string[];
companyTypes?: string[];
};
signal?: {
signal: "post_engagement" | "keyword" | "hiring" | "funding" | "launch" | "new_leader" | "voice_engagement" | "company_engagement" | "brand_engagement" | "lookalike";
enabled: boolean;
};
terms?: {
signal: "post_engagement" | "keyword" | "hiring" | "voice_engagement" | "company_engagement" | "brand_engagement";
values: string[];
};
};
/** Preview a signal source using its current audience. Provider credits may apply; does not enroll people. — `dench campaign sources preview` — POST /api/v1/campaigns/{sequenceId}/sources/preview */
"campaigns.sources.preview": {
sequenceId: string;
signal: "post_engagement" | "keyword" | "hiring" | "funding" | "launch" | "new_leader" | "voice_engagement" | "company_engagement" | "brand_engagement" | "lookalike";
terms?: string[];
};
/** Run a live campaign's signals now instead of waiting for the schedule (Run now). Provider credits apply; limited to 6 runs an hour. — `dench campaign sources run` — POST /api/v1/campaigns/{sequenceId}/sources/run */
"campaigns.sources.run": {
sequenceId: string;
};
/** Read the Sources tab: each signal's status, last and next run, people found and anything needing attention. — `dench campaign sources overview` — GET /api/v1/campaigns/{sequenceId}/sources/overview */
"campaigns.sources.overview": {
sequenceId: string;
};
/** List people a signal found who are waiting for approval (manual approval mode). — `dench campaign sources pending` — GET /api/v1/campaigns/{sequenceId}/sources/pending */
"campaigns.sources.pending": {
sequenceId: string;
};
/** Approve people waiting for review (they start the sequence) or reject them. — `dench campaign sources approve|reject` — POST /api/v1/campaigns/{sequenceId}/sources/review */
"campaigns.sources.review": {
sequenceId: string;
enrollmentIds: string[];
decision: "approve" | "reject";
};
/** Today's queue: who the campaign plans to contact today, warmest first, within the senders' limits. — `dench campaign queue` — GET /api/v1/campaigns/{sequenceId}/queue */
"campaigns.queue": {
sequenceId: string;
};
/** Read the company profile and suggested campaign audience. — `dench campaign builder profile` — GET /api/v1/campaigns/builder/profile */
"campaigns.builder.profile": Record;
/** Save a signal campaign's audience, signals and terms idempotently. Does not start a build. — `dench campaign builder save` — POST /api/v1/campaigns/builder/drafts */
"campaigns.builder.save": {
/** Your stable id for this request, 8-64 characters. Retrying with the same id returns the same draft instead of creating another. */
clientRequestId: string;
targeting: {
titles: string[];
locations: string[];
companySizes: string[];
industries: string[];
excludes: string[];
companies?: string[];
companyTypes?: string[];
};
signals: Array<"post_engagement" | "keyword" | "hiring" | "funding" | "launch" | "new_leader" | "voice_engagement" | "company_engagement" | "brand_engagement" | "lookalike">;
signalTerms?: {
/** Post topics, phrased as what the buyer does ("using AI for outbound"), not bare product names. */
post_engagement?: string[];
keyword?: string[];
/** Roles being hired for. */
hiring?: string[];
/** LinkedIn profile URLs of market voices whose posts to watch. */
voice_engagement?: string[];
/** LinkedIn company page URLs of companies and competitors. */
company_engagement?: string[];
/** The sender's own LinkedIn company page URL. */
brand_engagement?: string[];
/** Topic signals searched by their exact keywords only (no drafted phrases). */
exact?: Array<"post_engagement" | "keyword">;
};
/** Also add people who match the audience without a signal. */
findMoreMatching?: boolean;
name?: string;
/** How people come in: buying signals, people like one best lead, a pasted list or CSV, CRM records, or a people search. */
source?: "signals" | "lookalike" | "existing" | "crm" | "search";
screen?: "source" | "signals" | "lookalike" | "target" | "messages" | "outreach" | "review";
/** For source lookalike: the edited result of builder lookalike, with signals ["lookalike"]. */
lookalike?: {
seed: {
name: string;
linkedinUrl: string;
title?: string;
companyName?: string;
companyDomain?: string;
avatarUrl?: string;
};
title: string;
similarRoles: boolean;
roles: string[];
location?: string;
industry?: string;
relatedIndustries: boolean;
industries: string[];
companySizes: string[];
companies: {
name: string;
domain: string;
}[];
};
};
/** Read signal build progress, preview people, messages and errors. — `dench campaign builder get` — GET /api/v1/campaigns/builder/drafts/{draftId} */
"campaigns.builder.get": {
draftId: string;
};
/** Start the same billable signal/audience/message build used by the UI; poll the draft for progress. — `dench campaign builder build` — POST /api/v1/campaigns/builder/drafts/{draftId}/build */
"campaigns.builder.build": {
draftId: string;
};
/** Create a campaign from a reviewed signal draft. Does not launch the campaign. — `dench campaign builder create` — POST /api/v1/campaigns/builder/drafts/{draftId}/create */
"campaigns.builder.create": {
draftId: string;
name?: string;
timezone?: string;
/** Save paused: signals wait to run until launch. */
paused?: boolean;
};
/** Edit or reset one preview person's opening line before creating the campaign. — `dench campaign builder opener` — PUT /api/v1/campaigns/builder/drafts/{draftId}/people/{leadId}/opener */
"campaigns.builder.opener": {
draftId: string;
leadId: string;
opener: string;
};
/** List saved, unfinished campaign drafts that can be resumed. — `dench campaign builder drafts` — GET /api/v1/campaigns/builder/drafts */
"campaigns.builder.drafts": Record;
/** Read a saved draft's setup (source, name, targeting, signals, terms, lookalike) to resume it. — `dench campaign builder resume` — GET /api/v1/campaigns/builder/drafts/{draftId}/setup */
"campaigns.builder.resume": {
draftId: string;
};
/** Discard a saved draft that has not become a campaign. — `dench campaign builder discard` — DELETE /api/v1/campaigns/builder/drafts/{draftId} */
"campaigns.builder.discard": {
draftId: string;
};
/** Save a draft's Outreach and Review choices: AI or manual steps, the brief, the steps, who sends and when, and auto or manual approval. — `dench campaign builder outreach` — PATCH /api/v1/campaigns/builder/drafts/{draftId}/outreach */
"campaigns.builder.outreach": {
draftId: string;
/** ai: generate the sequence from the brief. manual: write steps. */
outreachMode?: "ai" | "manual";
brief?: {
/** What a reply should lead to, e.g. a demo. */
goal: string;
/** The problem the product solves for them. */
helpWith: string;
tone: "conversational" | "warm" | "direct" | "formal";
channels: "both" | "email" | "linkedin";
};
steps?: Array<{
type: "email" | "wait" | "linkedin_connection" | "linkedin_message";
/** Hours to wait, 1 to 1440. Required on a wait step. */
waitHours?: number;
emailSubject?: string;
/** Plain text only; draft steps keep no HTML body. */
emailTextBody?: string;
emailReplyToPrevious?: boolean;
onlyIf?: "always" | "connected" | "not_connected";
linkedinMessageBody?: string;
linkedinMessageMode?: "skip_if_not_connected" | "wait_until_connected";
connectionTimeoutHours?: number;
}>;
sending?: {
emailMailboxAccountIds: string[];
linkedinAccountIds: string[];
timezone: string;
startHour: number;
endHour: number;
/** 0 is Sunday, 6 is Saturday. */
weekdays: number[];
dailySendCap?: number;
};
/** auto: new people a signal finds start the sequence on their own. manual: they wait for approval. */
approvalMode?: "auto" | "manual";
};
/** Generate the draft's sequence from a brief (goal, problem, tone, channels), as Generate sequence does. AI credits apply. — `dench campaign builder sequence` — POST /api/v1/campaigns/builder/drafts/{draftId}/sequence */
"campaigns.builder.sequence": {
draftId: string;
brief: {
/** What a reply should lead to, e.g. a demo. */
goal: string;
/** The problem the product solves for them. */
helpWith: string;
tone: "conversational" | "warm" | "direct" | "formal";
channels: "both" | "email" | "linkedin";
};
};
/** Set up a lookalike from the best lead's LinkedIn URL (role, similar roles, place, industry, similar companies), suggest roles again, or re-scope to a company. Nothing is saved. Provider credits apply. — `dench campaign builder lookalike` — POST /api/v1/campaigns/builder/lookalike */
"campaigns.builder.lookalike": {
/** The best lead's LinkedIn profile URL. */
linkedinUrl?: string;
/** Suggest similar roles and related industries again. */
suggest?: {
title: string;
industry?: string;
};
/** Re-scope the lookalike to a different company. */
rescope?: {
seed: {
name: string;
linkedinUrl: string;
title?: string;
companyName?: string;
companyDomain?: string;
avatarUrl?: string;
};
company: string;
title?: string;
location?: string;
};
};
/** Suggest market voices (LinkedIn experts) for the campaign's topics, to watch with voice_engagement. AI credits apply. — `dench campaign builder voices` — POST /api/v1/campaigns/builder/voices */
"campaigns.builder.voices": {
topics?: string[];
market?: string;
};
/** Turn an audience description into targeting filters. AI credits apply. — `dench campaign builder autofill` — POST /api/v1/campaigns/builder/autofill */
"campaigns.builder.autofill": {
description: string;
};
/** Estimate audience or weekly signal volume using the UI's cached estimator. Provider credits may apply. — `dench campaign builder estimate` — POST /api/v1/campaigns/builder/estimate */
"campaigns.builder.estimate": {
kind: "people" | "week";
targeting: {
titles: string[];
locations: string[];
companySizes: string[];
industries: string[];
excludes: string[];
companies?: string[];
companyTypes?: string[];
};
signals?: Array<"post_engagement" | "keyword" | "hiring" | "funding" | "launch" | "new_leader" | "voice_engagement" | "company_engagement" | "brand_engagement" | "lookalike">;
signalTerms?: {
/** Post topics, phrased as what the buyer does ("using AI for outbound"), not bare product names. */
post_engagement?: string[];
keyword?: string[];
/** Roles being hired for. */
hiring?: string[];
/** LinkedIn profile URLs of market voices whose posts to watch. */
voice_engagement?: string[];
/** LinkedIn company page URLs of companies and competitors. */
company_engagement?: string[];
/** The sender's own LinkedIn company page URL. */
brand_engagement?: string[];
/** Topic signals searched by their exact keywords only (no drafted phrases). */
exact?: Array<"post_engagement" | "keyword">;
};
};
/** List all CLI/API operations and parity classifications. — `dench help` — GET /api/v1/commands */
"commands.list": Record;
/** Create an agent sign-in request. — `dench signin` — POST /api/v1/auth/signin-requests */
"auth.signin.create": {
codeHash: string;
sessionTokenHash: string;
agentName: string;
/** Agent kind, normalized to snake_case. */
agentKind: string;
/** Whether to join an existing workspace or create a new one. */
intent: "join_existing" | "create_workspace";
/** Required when intent is create_workspace. */
proposedOrganizationName?: string;
requestedOrganizationSelector?: string;
userAgent?: string;
};
/** Poll an agent sign-in request until approval. — `dench signin` — POST /api/v1/auth/signin-requests/{requestId}/poll */
"auth.signin.poll": {
codeHash: string;
sessionTokenHash: string;
};
/** Start the email OTP sign-in flow. — `dench signin --email` — POST /api/v1/auth/otp/start */
"auth.otp.start": {
/** Email to send the code to. */
email: string;
};
/** Verify an email OTP sign-in code. — `dench signin --email` — POST /api/v1/auth/otp/verify */
"auth.otp.verify": {
email: string;
/** 6+ digit code from the email. */
code: string;
agentName: string;
agentKind: string;
/** sha256 hex of the local session token. */
sessionTokenHash: string;
/** Whether to join an existing workspace or create a new one. */
intent: "join_existing" | "create_workspace";
proposedOrganizationName?: string;
organizationSelector?: string;
userAgent?: string;
};
/** Finalize an email OTP sign-in session. — `dench signin --email` — POST /api/v1/auth/otp/finalize */
"auth.otp.finalize": {
authToken: string;
/** Provide this OR newOrganizationName. */
organizationId?: string;
newOrganizationName?: string;
};
/** Read selected scoped workspace documents and a compact context index without writes. — `dench context --sections` — GET /api/v1/context/sections */
"workspace.context.sections": {
organizationId?: string;
sections?: Array<"identity" | "organisation" | "user" | "memory" | "rules" | "tools">;
};
/** Return the current Dench workspace context for the bearer. — `dench context` — GET /api/v1/context */
"workspace.context": Record;
/** Return the current workspace's standing instructions for external agents. — `dench context --instructions` — GET /api/v1/context/instructions */
"workspace.instructions": {
organizationId?: string;
};
/** Return workspace and agent status. — `dench status` — GET /api/v1/status */
"workspace.status": Record;
/** List agents visible in the workspace status payload. — `dench agents` — GET /api/v1/agents */
"workspace.agents": Record;
/** List approval requests visible in the workspace status payload. — `dench approvals` — GET /api/v1/approvals */
"workspace.approvals.list": Record;
/** List artifacts for the authenticated workspace. — `dench artifacts` — GET /api/v1/artifacts */
"workspace.artifacts.list": {
limit?: number;
};
/** List draft suggested-work artifacts. — `dench suggested-work` — GET /api/v1/suggested-work */
"workspace.suggestedWork.list": {
limit?: number;
};
/** List saved searches (find-result lists) in the workspace, newest first. — `dench lists` — GET /api/v1/lists */
"lists.list": {
search?: string;
cursor?: string;
limit?: number;
};
/** Read saved rows with current enrichment results and original indices; cursor-paged, without repeating a search. — `dench lists rows` — GET /api/v1/lists/{artifactId}/rows */
"lists.rows": {
artifactId: string;
cursor?: string;
limit?: number;
enrichment?: "all" | "enriched" | "in_progress" | "no_contacts" | "failed" | "not_enriched";
};
/** Buy contact details for 1–100 saved profiles and save them to the list. Skips fields already found, looked up empty, or in flight. Requires a signed-in member or their personal agent. — `dench lists enrich` — POST /api/v1/lists/{artifactId}/enrich */
"lists.enrich": {
artifactId: string;
rows: Record[];
/** email is work email. */
fields: Array<"email" | "phone" | "personal_email">;
};
/** Explicitly import selected saved profiles, optionally filtered by enrichment, with a configured Lead status. Reuses CRM records; never sends outreach or triggers conversion. — `dench artifacts import` — POST /api/v1/lists/{artifactId}/import */
"lists.import": {
artifactId: string;
to: "lead" | "company" | "job_posting";
rowIndexes?: number[];
status?: string;
enrichment?: "all" | "enriched" | "in_progress" | "no_contacts" | "failed" | "not_enriched";
viewName?: string;
pin?: boolean;
};
/** Get one saved search / list by artifact id. — `dench lists show` — GET /api/v1/lists/{artifactId} */
"lists.get": Record;
/** Save find results as a named list (rows + filters become a workspace artifact). — `dench lists save` — POST /api/v1/lists */
"lists.save": {
/** Use find_people, find_companies or find_jobs for a prospect list. */
createdFrom: "find_people" | "find_companies" | "find_jobs" | "mixed" | "watch_fire" | "generate_image";
/** Name of the new list. */
title: string;
/** True makes the list visible in Find. Omitted saves an unlisted draft that can still be read or added to a campaign by id. */
saved?: boolean;
/** Original search filters to retain with the list. Saving does not run a search. */
filters?: unknown;
/** Complete person records from a search, including existing contact details and custom fields. */
people?: Record[];
/** Complete company records from a search. */
companies?: Record[];
/** Complete job records from a search. */
jobs?: Record[];
};
/** Update a saved list in place (title, filters, and/or replacement rows). — `dench lists update` — PATCH /api/v1/lists/{artifactId} */
"lists.update": {
/** The existing list id. */
artifactId: string;
title?: string;
/** Replacement filter metadata; does not run a new search. */
filters?: unknown;
/** Replacement people. Omit to preserve existing rows; an empty array clears them. */
people?: Record[];
/** Replacement companies. Omit to preserve existing rows. */
companies?: Record[];
/** Replacement jobs. Omit to preserve existing rows. */
jobs?: Record[];
};
/** Rename a saved search / list. — `dench lists rename` — POST /api/v1/lists/{artifactId}/rename */
"lists.rename": Record;
/** Delete a saved search / list, its exported CSV/JSONL files, and any linked watch. — `dench lists delete` — DELETE /api/v1/lists/{artifactId} */
"lists.delete": Record;
/** Read durable enrichment activity (legacy review metadata remains readable). Use lists rows for current saved profiles and contacts. — `dench lists reviews` — GET /api/v1/lists/{artifactId}/reviews */
"lists.prospects.reviews": {
artifactId: string;
limit?: number;
cursor?: string;
};
/** Suggest ICP fit for one profile. Never changes a human review decision. — `dench lists assess` — POST /api/v1/lists/{artifactId}/assess */
"lists.prospects.assess": {
artifactId: string;
row: Record;
suggestion: {
fit: "likely_fit" | "unlikely_fit" | "unknown";
/** AI relevance to the saved ICP, not conversion probability. */
score?: number;
reason: string;
};
};
/** Search durable workspace memory. — `dench memory search` — GET /api/v1/memory/search */
"memory.search": {
/** Full-text query. */
query: string;
limit?: number;
};
/** Save a durable workspace memory fact. — `dench memory save` — POST /api/v1/memory */
"memory.save": {
/** Stable memory key. */
key: string;
/** Memory classification. */
kind: "fact" | "decision" | "preference" | "goal" | "tool_note" | "other" | "convention" | "project_context" | "agent_lesson" | "docs_knowledge" | "open_goal";
/** Memory body. No secrets or scratch notes. */
text: string;
tags?: string[];
/** Sensitivity tier; `sensitive` memories land in needs-review. */
sensitivity?: "normal" | "broad" | "sensitive";
/** Who can see the memory. Defaults to `user`. */
visibility?: "user" | "org";
projectId?: string;
sourceQuote?: string;
confidence?: number;
importance?: number;
};
/** Request human approval. — `dench approval request` — POST /api/v1/approvals */
"approval.request": {
/** What needs human approval. */
title: string;
reason?: string;
/** Risk level. */
risk: "low" | "medium" | "high";
};
/** Record an approval decision. — `dench approval approve|reject` — POST /api/v1/approvals/{approvalId}/decision */
"approval.decide": {
approvalId: string;
/** Human decision for an approval request. */
decision: "approved" | "rejected";
/** Required when the requesting agent records its own decision. */
evidence?: string;
};
/** Get billing and AI credit status. — `dench billing status` — GET /api/v1/billing/status */
"billing.status": Record;
/** Create an AI credit top-up checkout link. — `dench billing topup` — POST /api/v1/billing/topup */
"billing.topup": {
/** Idempotency key (1-200 of [A-Za-z0-9:_-]). */
requestId: string;
/** USD amount, $5 to $1000. */
amountUsd: number;
};
/** Create a workspace upgrade checkout link. — `dench upgrade` — POST /api/v1/billing/upgrade */
"billing.upgrade": {
tier?: "pro" | "max" | "cloud" | "desktop" | "api";
billingCycle?: "monthly" | "yearly";
requestId?: string;
/** Internal return path beginning with /. */
returnPath?: string;
};
/** List past chat threads. — `dench chat list` — GET /api/v1/chat/threads */
"chat.list": {
titleQuery?: string;
limit?: number;
includeArchived?: boolean;
includeAutomated?: boolean;
};
/** Search past chat messages. — `dench chat search` — GET /api/v1/chat/search */
"chat.search": {
/** Full-text query across past messages. */
query: string;
limit?: number;
excludeThreadId?: string;
includeAutomated?: boolean;
};
/** Read a past chat thread. — `dench chat read` — GET /api/v1/chat/threads/{threadId} */
"chat.read": {
threadId: string;
limit?: number;
/** Message text as a person sees it: text parts only, without reasoning or tool calls. */
visibleTextOnly?: boolean;
};
/** Read the parent-child chat thread hierarchy. — `dench chat tree` — GET /api/v1/chat/tree */
"chat.tree": {
/** Filter threads by title. */
titleQuery?: string;
limit?: number;
includeArchived?: boolean;
/** Include routine-spawned threads. Defaults false. */
includeAutomated?: boolean;
};
/** Rename one or more chat threads. — `dench chat rename` — PATCH /api/v1/chat/threads */
"chat.rename": {
items: {
threadId: string;
title: string;
}[];
};
/** Delete one or more chat threads. — `dench chat delete` — DELETE /api/v1/chat/threads */
"chat.delete": {
threadIds: string[];
includeAutomated?: boolean;
};
/** Start a new chat-turn workflow. — `dench chat new` — POST /api/v1/chat/threads */
"chat.new": {
environment?: string;
/** The user message that starts the turn. */
prompt: string;
model?: string;
fileContextPath?: string;
visibility?: "private" | "shared";
/** Bypass approval gates for this turn. */
yolo?: boolean;
title?: string;
};
/** Send a message to an existing chat thread. — `dench chat send` — POST /api/v1/chat/threads/{threadId}/messages */
"chat.send": {
threadId: string;
/** The message to append. */
prompt: string;
model?: string;
yolo?: boolean;
};
/** Follow a live chat response stream. — `dench chat follow` — GET /api/v1/chat/threads/{threadId}/stream */
"chat.follow": {
threadId: string;
};
/** List CRM objects. — `dench crm objects list [--with-references]` — GET /api/v1/crm/objects */
"crm.objects.list": {
/** Include bounded navigation references. Array reads return {data, references, referenceCoverage}; existing result envelopes keep their fields. */
withReferences?: boolean;
};
/** List names, display labels, visibility and workspace counts without internal state. — `dench crm objects list --compact` — GET /api/v1/crm/catalog/objects */
"crm.objects.listCompact": Record;
/** Get a CRM object. — `dench crm objects get` — GET /api/v1/crm/objects/{name} */
"crm.objects.get": {
name: string;
};
/** Create a CRM object. — `dench crm objects create` — POST /api/v1/crm/objects */
"crm.objects.create": {
/** Object name (lowercased, singular). */
name: string;
description?: string;
/** Default view rendered for the object. */
defaultView?: "table" | "kanban" | "calendar" | "timeline" | "gallery" | "list";
/** Hugeicons (or legacy Lucide) icon name, e.g. user-multiple, target-01. */
icon?: string;
/** Fields to add after the built-in Notes, in column order. The object and its fields are created in one transaction: if any field is rejected, nothing is created. */
fields?: Array<{
name: string;
/** Defaults to text. */
type?: "text" | "email" | "phone" | "url" | "number" | "boolean" | "date" | "richtext" | "file" | "user" | "enum" | "relation" | "tags" | "action";
required?: boolean;
defaultValue?: string;
/** Target object for `relation` fields. */
relatedObjectName?: string;
/** Relation cardinality for a `relation` field. */
relationshipType?: "many_to_one" | "many_to_many";
/** Options for `enum` fields. */
enumValues?: string[];
/** Hex colors aligned to enumValues. */
enumColors?: string[];
/** Allow multi-select enum. */
enumMultiple?: boolean;
description?: string;
/** Index for filter/search/sort. */
indexed?: boolean;
}>;
};
/** Update a CRM object. — `dench crm objects update` — PATCH /api/v1/crm/objects/{name} */
"crm.objects.update": {
name: string;
description?: string;
/** Default view rendered for the object. */
defaultView?: "table" | "kanban" | "calendar" | "timeline" | "gallery" | "list";
icon?: string;
viewSettings?: Record;
savedViews?: Record[];
/** Who can see the object's records: every workspace member, only the record's owner, or the owner plus the members listed on the record. Admins only (a signed-in admin; an org-scoped key cannot change it). Records created before the switch have no owner and are hidden from everyone until one is set; switch back to workspace to see them again. */
visibility?: "workspace" | "owner_only" | "owner_plus_members";
};
/** Rename a CRM object. — `dench crm objects rename` — POST /api/v1/crm/objects/{from}/rename */
"crm.objects.rename": {
/** Current object name (path). */
from: string;
/** New object name. */
to: string;
};
/** Delete a CRM object. — `dench crm objects delete` — DELETE /api/v1/crm/objects/{name} */
"crm.objects.delete": {
name: string;
};
/** Hide a CRM object's workspace sidebar row for the whole workspace. Not a delete — the table keeps working. Admin-only. — `dench crm objects archive` — POST /api/v1/crm/objects/{name}/archive */
"crm.objects.archive": {
name: string;
};
/** Restore a CRM object's workspace sidebar row. Admin-only. — `dench crm objects unarchive` — POST /api/v1/crm/objects/{name}/unarchive */
"crm.objects.unarchive": {
name: string;
};
/** List the saved views on a CRM object (plus the active view). — `dench crm views list` — GET /api/v1/crm/objects/{objectName}/views */
"crm.views.list": {
objectName: string;
};
/** Get one saved view's definition by name. — `dench crm views get` — GET /api/v1/crm/objects/{objectName}/views/{viewName} */
"crm.views.get": {
objectName: string;
viewName: string;
};
/** Materialize a saved view's rows (applies its filters/sort/pins), paginated. — `dench crm views entries` — GET /api/v1/crm/objects/{objectName}/views/{viewName}/entries */
"crm.views.entries": {
objectName: string;
viewName: string;
page?: number;
pageSize?: number;
/** Personal scope: "me" (the signed-in member / the human behind an agent session), "unassigned", or a member userId or email. Adds one has_any / is_empty rule on the object's assignee user field; rows, totalCount, sums and analytics all follow it. Never saved onto the view. */
assignedTo?: string;
/** Which `user` field "assigned" means. Default: the analytics owner role, else the task `Assignee`, else a user field named like owner/assignee, else the first user field. */
assigneeField?: string;
};
/** Create a saved view (named filters/sort/columns combo) on a CRM object. — `dench crm views create` — POST /api/v1/crm/objects/{objectName}/views */
"crm.views.create": {
objectName: string;
view: {
/** View name (upsert key; shows in the view dropdown). */
name: string;
/** Default view rendered for the object. */
view_type?: "table" | "kanban" | "calendar" | "timeline" | "gallery" | "list";
/** FilterGroup, e.g. {"id":"g1","conjunction":"and","rules":[{"id":"r1","field":"Status","operator":"is","value":"Hot"}]}. Date rules support operator "date_preset" with value "today", "yesterday", "this_week", "last_week", "this_month", "last_month", "this_quarter", "last_quarter", "this_year", or "last_year", plus "relative_past"/"relative_next" with relativeAmount + relativeUnit (days|weeks|months|quarters|years). Rules may target the system timestamps "created_at" / "updated_at", e.g. {"field":"created_at","operator":"relative_past","relativeAmount":7,"relativeUnit":"days"}. */
filters?: Record;
sort?: Array<{
/** Field name to sort by. */
field: string;
direction: "asc" | "desc";
}>;
/** Visible-column ordering. Omit to keep the default set. */
columns?: string[];
/** View-type settings (kanbanField, calendarDateField, timelineStartField, …). */
settings?: Record;
/** Pinned-list mode: show EXACTLY these entry ids (mutually exclusive with filters). */
pinnedEntryIds?: string[];
[key: string]: unknown;
};
/** Overwrite an existing view with the same name. */
replaceExisting?: boolean;
/** Also switch the object's active view to this one. */
setActive?: boolean;
};
/** Replace a saved view's definition (upsert by name). — `dench crm views update` — PATCH /api/v1/crm/objects/{objectName}/views/{viewName} */
"crm.views.update": {
objectName: string;
/** Name of the view to replace (path). */
viewName: string;
/** Full replacement definition; the path viewName is used as the name. */
view?: {
/** View name (upsert key; shows in the view dropdown). */
name?: string;
/** Default view rendered for the object. */
view_type?: "table" | "kanban" | "calendar" | "timeline" | "gallery" | "list";
/** FilterGroup, e.g. {"id":"g1","conjunction":"and","rules":[{"id":"r1","field":"Status","operator":"is","value":"Hot"}]}. Date rules support operator "date_preset" with value "today", "yesterday", "this_week", "last_week", "this_month", "last_month", "this_quarter", "last_quarter", "this_year", or "last_year", plus "relative_past"/"relative_next" with relativeAmount + relativeUnit (days|weeks|months|quarters|years). Rules may target the system timestamps "created_at" / "updated_at", e.g. {"field":"created_at","operator":"relative_past","relativeAmount":7,"relativeUnit":"days"}. */
filters?: Record;
sort?: Array<{
/** Field name to sort by. */
field: string;
direction: "asc" | "desc";
}>;
/** Visible-column ordering. Omit to keep the default set. */
columns?: string[];
/** View-type settings (kanbanField, calendarDateField, timelineStartField, …). */
settings?: Record;
/** Pinned-list mode: show EXACTLY these entry ids (mutually exclusive with filters). */
pinnedEntryIds?: string[];
[key: string]: unknown;
};
setActive?: boolean;
};
/** Rename a saved view in place — keeps its filters, pins, and sidebar slot. — `dench crm views rename` — POST /api/v1/crm/objects/{objectName}/views/{viewName}/rename */
"crm.views.rename": Record;
/** Delete a saved view by name (idempotent). — `dench crm views delete` — DELETE /api/v1/crm/objects/{objectName}/views/{viewName} */
"crm.views.delete": {
objectName: string;
viewName: string;
};
/** Move a pinned view's sidebar row into the Archived flyout. Admin-only. — `dench crm views archive` — POST /api/v1/crm/objects/{objectName}/views/{viewName}/archive */
"crm.views.archive": {
objectName: string;
viewName: string;
};
/** Restore a view's sidebar row (re-asserts its pin). Admin-only. — `dench crm views unarchive` — POST /api/v1/crm/objects/{objectName}/views/{viewName}/unarchive */
"crm.views.unarchive": {
objectName: string;
viewName: string;
};
/** Describe the workspace sidebar: Library rows in tree order (with their groups), the home-level Pinned strip, what is hidden and why, and whether the caller is an admin. — `dench crm sidebar list` — GET /api/v1/crm/sidebar */
"crm.sidebar.list": Record;
/** Reposition one Library row (or group) among the Library's top-level slots. Does not add a home-level Pinned tab. Workspace-shared; admin-only. — `dench crm sidebar move` — POST /api/v1/crm/sidebar/move */
"crm.sidebar.move": {
/** Nav id from `sidebar list`, e.g. crm-object:task. */
navId: string;
/** Where to land among the visible rows; N is 1-based. */
position: "top" | "bottom" | number;
};
/** Create a named sidebar group (e.g. "Hiring") in the workspace-shared Library. Open to any member. — `dench crm sidebar group create` — POST /api/v1/crm/sidebar/groups */
"crm.sidebar.groups.create": Record;
/** Rename a sidebar group. Open to any member. — `dench crm sidebar group rename` — POST /api/v1/crm/sidebar/groups/rename */
"crm.sidebar.groups.rename": Record;
/** Dissolve a sidebar group; its rows return to the top level in its slot. Open to any member. — `dench crm sidebar group delete` — POST /api/v1/crm/sidebar/groups/delete */
"crm.sidebar.groups.delete": Record;
/** Move a sidebar row into a group (groupId set) or back to the top level (groupId null). Open to any member. — `dench crm sidebar group add` — POST /api/v1/crm/sidebar/groups/move */
"crm.sidebar.groups.move-item": Record;
/** Pin (pinned true) or unpin a Library row — or a whole group — on the home-level Pinned shortcut strip (the "tab on the left"). Does not move the row inside Library. Workspace-shared; open to any member. — `dench crm sidebar pin` — POST /api/v1/crm/sidebar/pins */
"crm.sidebar.pins.set": Record;
/** List fields for a CRM object, including read-only calculation metadata. — `dench crm fields list` — GET /api/v1/crm/objects/{objectName}/fields */
"crm.fields.list": {
objectName: string;
};
/** List field names, types and effective constraints without indexing or enrichment internals. — `dench crm fields list --compact` — GET /api/v1/crm/catalog/objects/{objectName}/fields */
"crm.fields.listCompact": {
objectName: string;
};
/** Create a CRM field or read-only campaign activity boolean column. — `dench crm fields create` — POST /api/v1/crm/objects/{objectName}/fields */
"crm.fields.create": {
objectName: string;
name: string;
/** Defaults to boolean with calculation, otherwise text. Calculations require boolean. */
type?: "text" | "email" | "phone" | "url" | "number" | "boolean" | "date" | "richtext" | "file" | "user" | "enum" | "relation" | "tags" | "action";
required?: boolean;
defaultValue?: string;
/** Target object for `relation` fields. */
relatedObjectName?: string;
/** Relation cardinality for a `relation` field. */
relationshipType?: "many_to_one" | "many_to_many";
/** Options for `enum` fields. */
enumValues?: string[];
/** Hex colors aligned to enumValues. */
enumColors?: string[];
/** Allow multi-select enum. */
enumMultiple?: boolean;
description?: string;
/** Index for filter/search/sort. */
indexed?: boolean;
/** Enrichment binding that lets the field auto-fill from a provider. */
enrichment?: {
category: "people" | "company";
key: string;
apolloPath: string;
inputFieldName?: string;
};
/** Read-only campaign activity boolean on people or lead (through Contact), not an expression. Cannot be required, indexed, enriched or have a default value. */
calculation?: {
kind: "campaign_activity";
metric: "email_opened" | "email_clicked" | "replied" | "linkedin_connected";
/** Same-workspace multichannel campaign ID readable by the viewer. Omit for any accessible multichannel campaign. */
campaignId?: string;
};
};
/** Update a CRM field or change/explicitly clear an existing campaign calculation. — `dench crm fields update` — PATCH /api/v1/crm/objects/{objectName}/fields/{fieldName} */
"crm.fields.update": {
objectName: string;
fieldName: string;
/** Rename the field. */
name?: string;
description?: string;
required?: boolean;
/** Full replacement of options. */
enumValues?: string[];
enumColors?: string[];
enumMultiple?: boolean;
indexed?: boolean;
/** Enrichment binding that lets the field auto-fill from a provider. */
enrichment?: {
category: "people" | "company";
key: string;
apolloPath: string;
inputFieldName?: string;
};
clearEnrichment?: boolean;
/** Replace an existing calculation's metric/source. Stored fields cannot be converted; create a new column instead. */
calculation?: {
kind: "campaign_activity";
metric: "email_opened" | "email_clicked" | "replied" | "linkedin_connected";
/** Same-workspace multichannel campaign ID readable by the viewer. Omit for any accessible multichannel campaign. */
campaignId?: string;
};
/** Explicitly remove an existing calculation, leaving an editable boolean. Does not snapshot calculated values. Mutually exclusive with calculation. */
clearCalculation?: boolean;
};
/** Delete a CRM field. — `dench crm fields delete` — DELETE /api/v1/crm/objects/{objectName}/fields/{fieldName} */
"crm.fields.delete": {
objectName: string;
fieldName: string;
};
/** Reorder CRM fields. — `dench crm fields reorder` — POST /api/v1/crm/objects/{objectName}/fields/reorder */
"crm.fields.reorder": {
objectName: string;
/** Field names in desired order. */
orderedFieldNames: string[];
};
/** Reorder enum values. — `dench crm fields enum-reorder` — POST /api/v1/crm/objects/{objectName}/fields/{fieldName}/enum/reorder */
"crm.fields.enum.reorder": {
objectName: string;
fieldName: string;
/** Exact permutation of existing options. */
orderedValues: string[];
};
/** Add an enum value. — `dench crm fields enum-add` — POST /api/v1/crm/objects/{objectName}/fields/{fieldName}/enum */
"crm.fields.enum.add": {
objectName: string;
fieldName: string;
/** Option value to add. */
value: string;
/** Hex color for the option. */
color?: string;
/** 0-based insert index; defaults to append. */
position?: number;
};
/** Remove an enum value. — `dench crm fields enum-remove` — DELETE /api/v1/crm/objects/{objectName}/fields/{fieldName}/enum/{value} */
"crm.fields.enum.remove": {
objectName: string;
fieldName: string;
value: string;
};
/** Rename an enum value. — `dench crm fields enum-rename` — POST /api/v1/crm/objects/{objectName}/fields/{fieldName}/enum/{oldValue}/rename */
"crm.fields.enum.rename": {
objectName: string;
fieldName: string;
/** Existing option value (path). */
oldValue: string;
/** New option value; migrates entries + statuses. */
newValue: string;
};
/** Set an enum color. — `dench crm fields enum-set-color` — POST /api/v1/crm/objects/{objectName}/fields/{fieldName}/enum/{value}/color */
"crm.fields.enum.color": {
objectName: string;
fieldName: string;
value: string;
/** Hex color, e.g. #22c55e. */
color: string;
};
/** List CRM entries. — `dench crm entries list [--with-references]` — GET /api/v1/crm/objects/{objectName}/entries */
"crm.entries.list": {
/** Include bounded navigation references. Array reads return {data, references, referenceCoverage}; existing result envelopes keep their fields. */
withReferences?: boolean;
objectName: string;
limit?: number;
/** Personal scope: "me" (the signed-in member / the human behind an agent session), "unassigned", or a member userId or email. Adds one has_any / is_empty rule on the object's assignee user field; rows, totalCount, sums and analytics all follow it. Never saved onto the view. */
assignedTo?: string;
/** Which `user` field "assigned" means. Default: the analytics owner role, else the task `Assignee`, else a user field named like owner/assignee, else the first user field. */
assigneeField?: string;
};
/** Get a CRM entry. — `dench crm entries get` — GET /api/v1/crm/objects/{objectName}/entries/{entryId} */
"crm.entries.get": {
objectName: string;
entryId: string;
};
/** Queue a full, paged pull of the record's email history from every connected mailbox; the ledger fills as pages land. — `dench crm entries sync-history` — POST /api/v1/crm/objects/{objectName}/entries/{entryId}/sync-history */
"crm.entries.syncHistory": Record;
/** Has Dench read this record's email history? One row per address per mailbox. — `dench crm entries sync-history --status` — GET /api/v1/crm/objects/{objectName}/entries/{entryId}/sync-history */
"crm.entries.syncHistoryStatus": Record;
/** Status of the account's research dossier — the delimited `dench:dossier` section Dench keeps in the record's Notes: status, when it was written, how many dated updates followed, the last error. — `dench crm entries dossier` — GET /api/v1/crm/entries/{entryId}/dossier */
"crm.entries.dossier.get": Record;
/** Research the account again and append a fresh dossier section to its Notes (needs a Website / Domain on the record). — `dench crm entries dossier --regenerate` — POST /api/v1/crm/entries/{entryId}/dossier/regenerate */
"crm.entries.dossier.regenerate": Record;
/** Whether the caller hides their own emails / messages with this record from the workspace (directly, or through its account), and whether they own a connection the toggle applies to. — `dench crm privacy status` — GET /api/v1/crm/entries/{entryId}/privacy */
"crm.privacy.get": Record;
/** Hide (`hidden: true`) or show the caller's own emails / messages with this record to the workspace. An account covers every contact and opportunity linked to it; existing ledger rows are restamped and the shared Brief regenerates. — `dench crm privacy hide|show` — PUT /api/v1/crm/entries/{entryId}/privacy */
"crm.privacy.set": {
entryId: string;
hidden: boolean;
};
/** List account-detection verdicts — why a domain (or personal address) did or did not become an account: verdict, counterparty type, probabilities, evidence threads, the rows created. `kind` picks the creator: `detections` (email + sweep), `linkedin` (the LinkedIn resolver's rows) or `all` (default). Newest first. — `dench crm detections list` — GET /api/v1/crm/detections */
"crm.detections.list": Record;
/** Undo what the ingestion jobs created since a timestamp (admin): `kind` = `detections` (accounts + contacts from the email detector and the sweep), `linkedin` (the LinkedIn resolver's contacts + accounts; their conversations are unlinked and parked), `email-tasks` (auto-created tasks and cases) or `all` (default). Deletes only rows no human has touched and marks the verdicts / decisions rolled_back so they are never re-created. `dryRun` lists what would go; per-kind counts in `kinds`. — `dench crm detections undo` — POST /api/v1/crm/detections/undo */
"crm.detections.undo": {
/** Only verdicts / decisions at or after this ms timestamp. */
since: number;
/** Which creator's rows to undo. Default `all`. */
kind?: "detections" | "linkedin" | "email-tasks" | "all";
dryRun?: boolean;
limit?: number;
};
/** List a CRM entry's audit history. — `dench crm history` — GET /api/v1/crm/objects/{objectName}/entries/{entryId}/history */
"crm.entries.history": Record;
/** List a CRM entry's activity ledger (comments, notes, field changes, emails, meetings, Slack, LinkedIn) in one timeline, newest first by default. — `dench crm activity` — GET /api/v1/crm/objects/{objectName}/entries/{entryId}/activity */
"crm.activity.list": Record;
/** The record's generated Brief: health, who owes the next move, open threads, commitments, risks, next step. Read this before the ledger. — `dench crm brief` — GET /api/v1/crm/objects/{objectName}/entries/{entryId}/brief */
"crm.brief.get": Record;
/** Read-only source diagnostics: accessible evidence, association reasons, last Brief source manifest, generation queue, and your mailbox delivery health. Bounded, cursor-paged snapshot; does not certify complete history. — `dench crm context` — GET /api/v1/crm/objects/{objectName}/entries/{entryId}/context */
"crm.context.get": Record;
/** Rebuild a record's Brief from the current timeline. — `dench crm brief --regenerate` — POST /api/v1/crm/objects/{objectName}/entries/{entryId}/brief/regenerate */
"crm.brief.regenerate": Record;
/** Bounded per-source counts of a CRM entry's activity ledger. — `dench crm activity` — GET /api/v1/crm/objects/{objectName}/entries/{entryId}/activity/count */
"crm.activity.count": Record;
/** List a CRM entry's notes (titled, document-style comments), newest first. — `dench crm notes list` — GET /api/v1/crm/objects/{objectName}/entries/{entryId}/notes */
"crm.notes.list": Record;
/** Add a titled note to a CRM entry's activity timeline (markdown body). Research goes here, never into the Notes document. — `dench crm notes create` — POST /api/v1/crm/objects/{objectName}/entries/{entryId}/notes */
"crm.notes.create": Record;
/** List comments on a CRM entry (roots newest first, with preview replies). — `dench crm comments list` — GET /api/v1/crm/objects/{objectName}/entries/{entryId}/comments */
"crm.comments.list": {
objectName: string;
entryId: string;
limit?: number;
/** continueCursor from the prior page. */
cursor?: string;
/** Preview replies per root (default 2). */
replies?: number;
};
/** List every reply under a root comment, oldest first. — `dench crm comments replies` — GET /api/v1/crm/comments/{commentId}/replies */
"crm.comments.replies": {
/** Root comment id. */
commentId: string;
limit?: number;
cursor?: string;
};
/** Get one comment with its entry label. — `dench crm comments get` — GET /api/v1/crm/comments/{commentId} */
"crm.comments.get": {
commentId: string;
};
/** Comment on a CRM entry (markdown). Mention members with [@Name](/?member=); mention Dench with [@Dench](/?member=dench) or mentionDench. — `dench crm comments create` — POST /api/v1/crm/objects/{objectName}/entries/{entryId}/comments */
"crm.comments.create": {
objectName: string;
entryId: string;
/** Markdown. Mention a member with [@Name](/?member=); mention Dench with [@Dench](/?member=dench). */
body: string;
/** Reply under this comment (any depth). */
parentCommentId?: string;
/** Extra members to notify without writing mention links. */
mentionUserIds?: string[];
/** Summon Dench to reply in the thread. */
mentionDench?: boolean;
};
/** Edit a comment you wrote. — `dench crm comments update` — PATCH /api/v1/crm/comments/{commentId} */
"crm.comments.update": {
commentId: string;
body: string;
};
/** Delete a comment (kept as a tombstone when it has replies). — `dench crm comments delete` — DELETE /api/v1/crm/comments/{commentId} */
"crm.comments.delete": {
commentId: string;
};
/** Toggle an emoji reaction on a comment. — `dench crm comments react` — PUT /api/v1/crm/comments/{commentId}/reactions/{emoji} */
"crm.comments.react": {
commentId: string;
/** A single emoji, e.g. 👍. */
emoji: string;
};
/** Whether you follow a CRM entry's activity. — `dench crm watch status` — GET /api/v1/crm/objects/{objectName}/entries/{entryId}/watch */
"crm.watch.get": {
objectName: string;
entryId: string;
};
/** Follow or unfollow a CRM entry's activity. — `dench crm watch` — PUT /api/v1/crm/objects/{objectName}/entries/{entryId}/watch */
"crm.watch.set": {
objectName: string;
entryId: string;
/** Defaults to true; false unfollows. */
watching?: boolean;
};
/** Page assigned tasks in priority order (or completed tasks by recency). — `dench crm tasks mine --page` — POST /api/v1/crm/tasks/assigned/page */
"crm.tasks.pageAssigned": Record;
/** Page all tasks linked to a record, including subtasks. — `dench crm tasks list --page` — POST /api/v1/crm/objects/{objectName}/entries/{entryId}/tasks/page */
"crm.tasks.pageForEntry": Record;
/** Start or resume preparation of exact task ordering and progress. — `dench crm tasks prepare` — POST /api/v1/crm/tasks/prepare */
"crm.tasks.prepare": Record;
/** Your open tasks (plus ones completed today). — `dench crm tasks mine` — GET /api/v1/crm/tasks/mine */
"crm.tasks.mine": {
limit?: number;
/** Only tasks at this priority — one of the workspace's Priority options ("high"; case-insensitive). */
priority?: string;
};
/** Tasks linked to a CRM entry. — `dench crm tasks list` — GET /api/v1/crm/objects/{objectName}/entries/{entryId}/tasks */
"crm.tasks.forEntry": {
objectName: string;
entryId: string;
};
/** Create a task linked to a CRM entry. — `dench crm tasks create` — POST /api/v1/crm/objects/{objectName}/entries/{entryId}/tasks */
"crm.tasks.createForEntry": {
objectName: string;
entryId: string;
name: string;
assigneeUserId?: string;
/** YYYY-MM-DD */
dueDateKey?: string;
/** One of the workspace's Priority options (default Low | Medium | High; case-insensitive). */
priority?: string;
};
/** Mark a task done. — `dench crm tasks complete` — POST /api/v1/crm/tasks/{entryId}/complete */
"crm.tasks.complete": {
entryId: string;
};
/** Reopen a completed task. — `dench crm tasks reopen` — POST /api/v1/crm/tasks/{entryId}/reopen */
"crm.tasks.reopen": {
entryId: string;
};
/** Set or clear a task's priority. — `dench crm tasks priority` — PUT /api/v1/crm/tasks/{entryId}/priority */
"crm.tasks.setPriority": {
entryId: string;
/** One of the workspace's Priority options ("high"; case-insensitive), or null to clear. */
priority: string | null;
};
/** A task's subtasks. — `dench crm tasks subtasks` — GET /api/v1/crm/tasks/{entryId}/subtasks */
"crm.tasks.subtasks.list": {
entryId: string;
};
/** Create a subtask under a task (inherits the parent's record relations). — `dench crm tasks create-subtask` — POST /api/v1/crm/tasks/{entryId}/subtasks */
"crm.tasks.subtasks.create": {
/** The parent task. */
entryId: string;
name: string;
assigneeUserId?: string;
/** YYYY-MM-DD */
dueDateKey?: string;
priority?: string;
};
/** Nest a task under a parent, or promote it (parentEntryId: null). — `dench crm tasks parent` — PUT /api/v1/crm/tasks/{entryId}/parent */
"crm.tasks.setParent": {
entryId: string;
/** The parent task, or null to promote to a top-level task. */
parentEntryId: string | null;
};
/** Delete a task and its subtasks, or keep them as tasks. — `dench crm tasks delete` — DELETE /api/v1/crm/tasks/{entryId} */
"crm.tasks.delete": {
entryId: string;
/** What happens to its subtasks: delete them too (default) or keep them as tasks. */
subtasks?: "delete" | "keep";
};
/** How a task repeats (rule, RRULE, trigger, ends, next due). — `dench crm tasks repeat` — GET /api/v1/crm/tasks/{entryId}/repeat */
"crm.tasks.repeat.get": {
entryId: string;
};
/** Make a task repeat, or change how it repeats (JSON rule or RRULE). An undated task gets its due date anchored on the first matching day from today. — `dench crm tasks repeat` — PUT /api/v1/crm/tasks/{entryId}/repeat */
"crm.tasks.repeat.set": {
entryId: string;
/** Structured rule; alternative to `rrule`. */
rule?: {
freq: "daily";
interval: number;
} | {
freq: "weekly";
interval: number;
byWeekday: Array<"mo" | "tu" | "we" | "th" | "fr" | "sa" | "su">;
} | {
freq: "monthly";
interval: number;
/** 1–31, or -1 for the last day of the month. */
byMonthDay?: number;
bySetPos?: {
/** 1–4, or -1 for the last week. */
ordinal: number;
weekday: "mo" | "tu" | "we" | "th" | "fr" | "sa" | "su";
};
} | {
freq: "yearly";
interval: number;
month: number;
day: number;
};
/** RFC 5545 subset: FREQ, INTERVAL, BYDAY (ordinal allowed for MONTHLY, e.g. 2TU / -1FR), BYMONTHDAY, BYMONTH, COUNT, UNTIL. Bare FREQ=MONTHLY / YEARLY / WEEKLY take the day from the task's due date. */
rrule?: string;
/** When the next occurrence is created: after this one is done (default) or on schedule regardless. */
trigger?: "after_completion" | "on_schedule";
ends?: {
kind: "never";
} | {
kind: "count";
count: number;
} | {
kind: "until";
dateKey: string;
};
options?: {
skipWeekends?: boolean;
keepAssignee?: boolean;
copyNotes?: boolean;
copySubtasks?: boolean;
};
};
/** Stop a task repeating (past occurrences are kept). — `dench crm tasks repeat --off` — DELETE /api/v1/crm/tasks/{entryId}/repeat */
"crm.tasks.repeat.clear": {
entryId: string;
};
/** Skip the current occurrence: the next one is created now. — `dench crm tasks repeat --skip` — POST /api/v1/crm/tasks/{entryId}/repeat/skip */
"crm.tasks.repeat.skip": {
entryId: string;
};
/** Pause a repeating task. — `dench crm tasks repeat --pause` — POST /api/v1/crm/tasks/{entryId}/repeat/pause */
"crm.tasks.repeat.pause": {
entryId: string;
};
/** Resume a paused repeating task. — `dench crm tasks repeat --resume` — POST /api/v1/crm/tasks/{entryId}/repeat/resume */
"crm.tasks.repeat.resume": {
entryId: string;
};
/** Every repeating-task series in the workspace. — `dench crm tasks series` — GET /api/v1/crm/tasks/series */
"crm.tasks.series.list": {
status?: "active" | "paused" | "ended";
};
/** Every occurrence (task) of one series. — `dench crm tasks series --occurrences` — GET /api/v1/crm/tasks/series/{seriesId}/occurrences */
"crm.tasks.series.occurrences": {
seriesId: string;
};
/** Create a CRM entry. — `dench crm entries create` — POST /api/v1/crm/objects/{objectName}/entries */
"crm.entries.create": {
objectName: string;
/** Field-name -> value map. For `user` fields use one member userId or a userId array; preserve every assignee. */
data: Record;
};
/** Create many CRM entries. — `dench crm entries create-many` — POST /api/v1/crm/objects/{objectName}/entries/batch */
"crm.entries.createMany": {
objectName: string;
/** Up to 200 field maps. */
entries: Record[];
};
/** Update a CRM entry. — `dench crm entries update` — PATCH /api/v1/crm/objects/{objectName}/entries/{entryId} */
"crm.entries.update": {
objectName: string;
entryId: string;
/** Field-name -> value map. For `user` fields use one member userId or a userId array; preserve every assignee. */
data: Record;
};
/** Update many CRM entries. — `dench crm entries update-many` — PATCH /api/v1/crm/objects/{objectName}/entries/batch */
"crm.entries.updateMany": {
/** Object the entries belong to. */
objectName: string;
/** Per-entry partial updates (entryId or id + data). */
updates: {
entryId?: string;
id?: string;
/** Field-name -> value map. For `user` fields use one member userId or a userId array; preserve every assignee. */
data: Record;
}[];
};
/** Delete a CRM entry. — `dench crm entries delete` — DELETE /api/v1/crm/objects/{objectName}/entries/{entryId} */
"crm.entries.delete": {
objectName?: string;
entryId: string;
};
/** Bulk delete CRM entries. — `dench crm entries bulk-delete` — POST /api/v1/crm/objects/{objectName}/entries/delete */
"crm.entries.bulkDelete": {
objectName?: string;
entryIds: string[];
};
/** Get a CRM cell value. — `dench crm cells get` — GET /api/v1/crm/objects/{objectName}/entries/{entryId}/fields/{fieldName} */
"crm.cells.get": {
objectName: string;
entryId: string;
fieldName: string;
};
/** Set a CRM cell value. — `dench crm cells set` — PUT /api/v1/crm/objects/{objectName}/entries/{entryId}/fields/{fieldName} */
"crm.cells.set": {
objectName: string;
entryId: string;
fieldName: string;
/** Cell value. Enum=option string, tags=string array, user=one member userId or a userId array, relation=related entry id, boolean=true/false. */
value: unknown;
};
/** Set many CRM cell values. — `dench crm cells set-many` — POST /api/v1/crm/cells/batch */
"crm.cells.setMany": {
cells: {
objectName: string;
entryId: string;
fieldName: string;
/** Cell value. Enum=option string, tags=string array, user=one member userId or a userId array, relation=related entry id, boolean=true/false. */
value: unknown;
}[];
};
/** Append to a CRM cell. — `dench crm cells append` — POST /api/v1/crm/objects/{objectName}/entries/{entryId}/fields/{fieldName}/append */
"crm.cells.append": {
objectName: string;
entryId: string;
fieldName: string;
/** Cell value. Enum=option string, tags=string array, user=one member userId or a userId array, relation=related entry id, boolean=true/false. */
value: unknown;
};
/** Query CRM entries. — `dench crm query [--with-references]` — POST /api/v1/crm/query */
"crm.query": {
/** Include bounded navigation references. Array reads return {data, references, referenceCoverage}; existing result envelopes keep their fields. */
withReferences?: boolean;
objectName: string;
/** Query DSL. Bare equality only; use dslJson for $-operators. */
dsl?: {
filter?: Record;
select?: string[];
/** e.g. -Score,Name (- = desc). */
sort?: string;
limit?: number;
};
/** JSON string of the DSL when filters use operators like $gt/$in/$contains/$and/$or. Date fields and the system timestamps created_at/updated_at also accept $datePreset ("today", "yesterday", "this_week", "last_week", "this_month", "last_month", "this_quarter", "last_quarter", "this_year", "last_year"), and $inLast/$inNext rolling windows (a day count like 7, or {"amount":2,"unit":"weeks"} with unit days|weeks|months|quarters|years). Example: {"filter":{"updated_at":{"$datePreset":"this_month"}}}. */
dslJson?: string;
/** Personal scope: "me" (the signed-in member / the human behind an agent session), "unassigned", or a member userId or email. Adds one has_any / is_empty rule on the object's assignee user field; rows, totalCount, sums and analytics all follow it. Never saved onto the view. */
assignedTo?: string;
/** Which `user` field "assigned" means. Default: the analytics owner role, else the task `Assignee`, else a user field named like owner/assignee, else the first user field. */
assigneeField?: string;
};
/** Scan CRM entries with a resumable cursor. Empty filtered pages can have more results. — `dench crm query --page` — POST /api/v1/crm/query/page */
"crm.queryPage": {
objectName: string;
dsl?: Record;
dslJson?: string;
/** Personal scope: "me" (the signed-in member / the human behind an agent session), "unassigned", or a member userId or email. Adds one has_any / is_empty rule on the object's assignee user field; rows, totalCount, sums and analytics all follow it. Never saved onto the view. */
assignedTo?: string;
/** Which `user` field "assigned" means. Default: the analytics owner role, else the task `Assignee`, else a user field named like owner/assignee, else the first user field. */
assigneeField?: string;
/** Include bounded navigation references. Array reads return {data, references, referenceCoverage}; existing result envelopes keep their fields. */
withReferences?: boolean;
paginationOpts: {
numItems: number;
cursor: string | null;
};
};
/** Run CRM SQL-compatible query. — `dench crm sql` — POST /api/v1/crm/sql */
"crm.sql": {
objectName: string;
dsl?: Record;
dslJson?: string;
};
/** Aggregate CRM entries. — `dench crm aggregate` — POST /api/v1/crm/aggregate */
"crm.aggregate": {
objectName: string;
/** Field to bucket by. */
fieldName: string;
/** Number field to total per bucket (e.g. Amount grouped by Stage). Exact at any table size. */
sumField?: string;
};
/** Search CRM entries. — `dench crm search [--with-references]` — GET /api/v1/crm/search */
"crm.search": {
/** Include bounded navigation references. Array reads return {data, references, referenceCoverage}; existing result envelopes keep their fields. */
withReferences?: boolean;
/** Full-text query. */
text: string;
/** Omit to search org-wide. */
objectName?: string;
limit?: number;
};
/** Search bounded pages of readable CRM entries, optionally projecting cells. Always follow continueCursor until isDone. — `dench crm search --page` — GET /api/v1/crm/search/page */
"crm.search.page": {
/** Include bounded navigation references. Array reads return {data, references, referenceCoverage}; existing result envelopes keep their fields. */
withReferences?: boolean;
/** Full-text query over readable entries. */
text: string;
objectName?: string;
select?: string[];
/** Defaults to strict. Explicit modes require objectName and select. Compact recovers unknown selected names with bounded canonical columns and explicit selectionFallback metadata in the same response. Does not guess aliases. */
selectionMode?: "strict" | "compact";
limit?: number;
/** Use the preceding continueCursor, with the same query and projection. */
cursor?: string;
};
/** List CRM statuses. — `dench crm statuses list` — GET /api/v1/crm/objects/{objectName}/statuses */
"crm.statuses.list": {
objectName: string;
};
/** Set CRM statuses. — `dench crm statuses set` — PUT /api/v1/crm/objects/{objectName}/statuses */
"crm.statuses.set": {
objectName: string;
statuses: {
name: string;
/** Hex color. */
color: string;
sortOrder?: number;
isDefault?: boolean;
}[];
};
/** Apply a CRM batch. — `dench crm batch` — POST /api/v1/crm/batch */
"crm.batch": {
/** Include bounded navigation references. Array reads return {data, references, referenceCoverage}; existing result envelopes keep their fields. */
withReferences?: boolean;
ops: Array<{
type: "entries.create";
objectName: string;
/** Field-name -> value map. For `user` fields use one member userId or a userId array; preserve every assignee. */
data: Record;
} | {
type: "entries.update";
entryId: string;
/** Field-name -> value map. For `user` fields use one member userId or a userId array; preserve every assignee. */
data: Record;
} | {
type: "entries.delete";
entryId: string;
}>;
idempotencyKey?: string;
};
/** Begin a CRM transaction. — `dench crm transaction begin` — POST /api/v1/crm/transactions */
"crm.transaction.begin": Record;
/** Add an operation to a CRM transaction. — `dench crm transaction add` — POST /api/v1/crm/transactions/{txnId}/operations */
"crm.transaction.add": {
txnId: string;
/** A single CRM batch/transaction operation. */
op: {
type: "entries.create";
objectName: string;
/** Field-name -> value map. For `user` fields use one member userId or a userId array; preserve every assignee. */
data: Record;
} | {
type: "entries.update";
entryId: string;
/** Field-name -> value map. For `user` fields use one member userId or a userId array; preserve every assignee. */
data: Record;
} | {
type: "entries.delete";
entryId: string;
};
};
/** Commit a CRM transaction. — `dench crm transaction commit` — POST /api/v1/crm/transactions/{txnId}/commit */
"crm.transaction.commit": {
txnId: string;
};
/** Abort a CRM transaction. — `dench crm transaction abort` — POST /api/v1/crm/transactions/{txnId}/abort */
"crm.transaction.abort": {
txnId: string;
};
/** Inspect a CRM transaction. — `dench crm transaction inspect` — GET /api/v1/crm/transactions/{txnId} */
"crm.transaction.inspect": {
txnId: string;
};
/** List CRM documents. — `dench crm docs list` — GET /api/v1/crm/docs */
"crm.docs.list": Record;
/** Create a CRM document. — `dench crm docs create` — POST /api/v1/crm/docs */
"crm.docs.create": {
title: string;
filePath: string;
icon?: string;
coverImage?: string;
parentId?: string;
parentObjectName?: string;
entryId?: string;
};
/** Link a CRM document. — `dench crm docs link` — POST /api/v1/crm/docs/link */
"crm.docs.link": {
filePath: string;
objectName?: string;
entryId?: string;
};
/** Delete a CRM document page; its workspace file stays and nested pages move up. — `dench crm docs delete` — DELETE /api/v1/crm/docs/{documentId} */
"crm.docs.delete": {
/** The id crm docs create returned. */
documentId: string;
};
/** List CRM actions. — `dench crm actions list` — GET /api/v1/crm/actions */
"crm.actions.list": {
entryId: string;
};
/** Create or upsert an action button on a named action column (fieldName is the column header; a column can hold multiple buttons — reuse fieldName to add more). — `dench crm actions create` — POST /api/v1/crm/actions */
"crm.actions.create": {
objectName: string;
fieldName: string;
actionId?: string;
label?: string;
variant?: string;
icon?: string;
confirmMessage?: string;
recipe?: unknown[];
script?: string;
scriptPath?: string;
[key: string]: unknown;
};
/** Update an action button on a CRM field. — `dench crm actions edit` — PATCH /api/v1/crm/actions/{actionId} */
"crm.actions.update": {
actionId: string;
objectName: string;
fieldName: string;
label?: string;
variant?: string;
icon?: string;
confirmMessage?: string;
recipe?: unknown[];
script?: string;
scriptPath?: string;
[key: string]: unknown;
};
/** Delete an action button from a CRM field. — `dench crm actions delete` — DELETE /api/v1/crm/actions/{actionId} */
"crm.actions.delete": {
actionId: string;
objectName: string;
fieldName?: string;
fieldId?: string;
deleteFieldIfEmpty?: boolean;
[key: string]: unknown;
};
/** Run a CRM action on one or more entries. — `dench crm actions run` — POST /api/v1/crm/actions/{actionId}/runs */
"crm.actions.run": {
actionId: string;
objectName: string;
fieldId: string;
entryIds: string[];
prompts?: Record;
};
/** List workspace members for CRM user fields. — `dench members list` — GET /api/v1/members */
"crm.members.list": Record;
/** Live presence (online / away / offline) for workspace members. — `dench members presence` — GET /api/v1/members/presence */
"members.presence": {
/** Comma-separated user ids; omit for every member. */
userIds?: string;
};
/** A member's profile: name, email, tagline, role, timezone, presence, open task count. — `dench members get` — GET /api/v1/members/{userId} */
"members.get": {
userId: string;
};
/** A member's recent comments and record changes across the CRM, newest first. — `dench members activity` — GET /api/v1/members/{userId}/activity */
"members.activity": {
userId: string;
limit?: number;
/** Comma-separated subset of comments,changes. */
kinds?: string;
};
/** Open tasks assigned to a member (plus ones completed today). — `dench members tasks` — GET /api/v1/members/{userId}/tasks */
"members.tasks": {
userId: string;
limit?: number;
todayStartMs?: number;
};
/** Your own member profile. — `dench members me` — GET /api/v1/me/profile */
"me.profile.get": Record;
/** Update your name, tagline, avatar URL, or timezone. — `dench members me --name … --tagline … --timezone …` — PATCH /api/v1/me/profile */
"me.profile.update": {
name?: string;
/** Empty string clears. */
tagline?: string;
/** http(s) image URL; empty string clears the avatar. */
imageUrl?: string;
/** IANA zone; null returns to browser detection. */
timezone?: string | null;
};
/** Get a one-time URL to POST an avatar image to; then PUT /me/avatar with the returned storageId. — `dench members me --avatar ` — POST /api/v1/me/avatar/upload-url */
"me.avatar.uploadUrl": Record;
/** Set your avatar from an uploaded storageId. — `dench members me --avatar ` — PUT /api/v1/me/avatar */
"me.avatar.set": {
/** storageId returned by the upload POST. */
storageId: string;
};
/** Remove your avatar. — `dench members me --clear-avatar` — DELETE /api/v1/me/avatar */
"me.avatar.clear": Record;
/** Generate a CRM report. — `dench crm reports generate` — POST /api/v1/crm/reports */
"crm.reports.generate": {
objectName: string;
/** Chart type for the generated report. */
type: "pie" | "bar" | "line" | "table";
groupByField: string;
/** Aggregation metric. Defaults to `count`. */
metric?: "count" | "sum" | "avg" | "min" | "max";
/** Number field to sum/avg per bucket (e.g. Amount grouped by Stage). Defaults to the group field itself. */
metricField?: string;
};
/** Read an object's analytics dashboard: panels, field roles, the strip's collapsed flag, the named view's own dashboard (if any), and the fields panels may bucket on (synthetic $createdAt / $location last). — `dench crm analytics get` — GET /api/v1/crm/objects/{objectName}/analytics */
"crm.analytics.get": {
objectName: string;
/** Also return this saved view's own dashboard (viewAnalytics) when it has one. */
viewName?: string;
};
/** Compute chart-ready results (buckets / series / values) for the stored dashboard, a subset of its panels, or ad-hoc panel definitions — optionally scoped to a saved view, a period preset (with the previous interval as previousValue) and extra filters. Same query the strip renders from. — `dench crm analytics compute` — POST /api/v1/crm/objects/{objectName}/analytics/compute */
"crm.analytics.compute": {
objectName: string;
/** Compute the saved view's own dashboard instead of the object's (ignored when the view inherits). */
viewName?: string;
/** Only these stored panels. */
panelIds?: string[];
/** Ad-hoc panel definitions to compute instead of the stored dashboard (nothing is persisted). id/title/metric/sortOrder are filled in when omitted. */
panels?: Array<{
/** Stable id (template panels use `tpl::`). */
id?: string;
title?: string;
type: "stat" | "bar" | "line" | "area" | "donut" | "funnel" | "leaderboard" | "forecast" | "map";
metric?: "count" | "sum" | "avg" | "min" | "max";
/** Numeric field for sum/avg/min/max. Ignored for count. */
metricField?: string;
/** Bucketing field (enum/user/relation/boolean/text, or $location). On a time-series panel it splits the series into one line per value. */
groupBy?: string;
/** Date field for line/area/forecast bucketing (or $createdAt). */
dateField?: string;
dateBucket?: "day" | "week" | "month" | "quarter";
/** Panel-scoped FilterGroup, same shape as saved-view filters. */
filters?: Record;
/** Deprecated; ignored for layout. Use `span`. */
size?: "sm" | "md" | "lg";
/** Card width on the 6-track dashboard grid: 2 = a third, 3 = half, 4 = two thirds, 6 = full row. Defaults: funnel 4; map and leaderboard 2; bar/donut grouped by a user field 2, by anything else 4; trends 3. */
span?: 2 | 3 | 4 | 6;
/** Hide from the strip above the table; still shown in the full analytics view. */
hidden?: boolean;
sortOrder?: number;
/** Role-driven derived metric; when set the panel computes from fieldRoles instead of metric/groupBy. */
derived?: "conversion_rate" | "win_rate" | "won_amount" | "lost_amount" | "open_pipeline" | "pipeline_forecast" | "mrr" | "arr";
}>;
/** Period preset. Scopes every panel by a date_preset on the object's closeDate role (else $createdAt) and fills previousValue from the previous calendar interval. "all" = no scope. */
period?: "all" | "this_week" | "this_month" | "last_month" | "this_quarter" | "last_quarter" | "this_year";
/** FilterGroup applied to every panel (e.g. the view's rules). Forces the bounded scan path. */
extraFilters?: Record;
/** FilterGroup for the comparison window; every panel's value over it comes back as previousValue. Derived automatically from `period`. */
compareFilters?: Record;
/** Preview an unsaved role mapping. */
fieldRolesOverride?: {
/** Numeric field holding the deal value ("Amount") — the "Value field" in the setup sheet. */
amount?: string;
/** Enum field driving the pipeline ("Stage" / "Status") — the "Progress field". */
stage?: string;
/** `stage` options that count as won (closed, successful). */
wonValues?: string[];
/** `stage` options that count as lost (closed, gone). */
lostValues?: string[];
/** `stage` options that count as converted (a lead reached "Qualified"). */
convertedValues?: string[];
/** Date field for time bucketing, forecasts and the period pill (falls back to $createdAt). */
closeDate?: string;
/** User field naming who owns each record ("Owner"). Rankings on it default to a narrow card and draw member faces. */
owner?: string;
/** Place-name field maps plot. May be a relation path like "Account.HQ Location" (one hop through a relation). */
location?: string;
/** How a won amount recurs: annual treats Amount as ARR, monthly as MRR, one_time disables MRR/ARR panels. */
recurrence?: "monthly" | "annual" | "one_time";
/** Chance of closing per OPEN stage, 0..1, for the weighted forecast. Unlisted stages are spaced evenly; won = 1, lost = 0. */
stageProbabilities?: Record;
};
/** Personal scope: "me" (the signed-in member / the human behind an agent session), "unassigned", or a member userId or email. Adds one has_any / is_empty rule on the object's assignee user field; rows, totalCount, sums and analytics all follow it. Never saved onto the view. */
assignedTo?: string;
/** Which `user` field "assigned" means. Default: the analytics owner role, else the task `Assignee`, else a user field named like owner/assignee, else the first user field. */
assigneeField?: string;
/** Click-to-filter chips, same as the strip: each scopes every panel EXCEPT those whose dimensions include `dimension`. */
drillFilters?: {
/** The bare field the click came from — a panel's groupBy or dateField, or `$location` for a map. Panels drawn along it keep their full picture; every other panel is scoped. */
dimension: string;
/** FilterGroup selecting the clicked bucket. */
filter: Record;
}[];
};
/** Create or update one panel (upsert by id). With viewName the write lands on that saved view's own dashboard, forking a copy if it was still inheriting the object's. — `dench crm analytics save` — PUT /api/v1/crm/objects/{objectName}/analytics/panels/{panelId} */
"crm.analytics.save": {
objectName: string;
/** Panel id (path); becomes panel.id. */
panelId: string;
/** The panel definition to upsert. */
panel: {
title: string;
type: "stat" | "bar" | "line" | "area" | "donut" | "funnel" | "leaderboard" | "forecast" | "map";
metric: "count" | "sum" | "avg" | "min" | "max";
/** Numeric field for sum/avg/min/max. Ignored for count. */
metricField?: string;
/** Bucketing field (enum/user/relation/boolean/text, or $location). On a time-series panel it splits the series into one line per value. */
groupBy?: string;
/** Date field for line/area/forecast bucketing (or $createdAt). */
dateField?: string;
dateBucket?: "day" | "week" | "month" | "quarter";
/** Panel-scoped FilterGroup, same shape as saved-view filters. */
filters?: Record;
/** Deprecated; ignored for layout. Use `span`. */
size?: "sm" | "md" | "lg";
/** Card width on the 6-track dashboard grid: 2 = a third, 3 = half, 4 = two thirds, 6 = full row. Defaults: funnel 4; map and leaderboard 2; bar/donut grouped by a user field 2, by anything else 4; trends 3. */
span?: 2 | 3 | 4 | 6;
/** Hide from the strip above the table; still shown in the full analytics view. */
hidden?: boolean;
sortOrder: number;
/** Role-driven derived metric; when set the panel computes from fieldRoles instead of metric/groupBy. */
derived?: "conversion_rate" | "win_rate" | "won_amount" | "lost_amount" | "open_pipeline" | "pipeline_forecast" | "mrr" | "arr";
id?: string;
};
/** Write the saved view's own dashboard (forks a copy if it was inheriting). */
viewName?: string;
};
/** Edit part of one stored panel: only the keys in `patch` change (title, type, metric, metricField, groupBy, dateField, dateBucket, filters, derived, span, hidden, sortOrder); null clears an optional key. What the card's ⋮ menu writes. — `dench crm analytics update` — PATCH /api/v1/crm/objects/{objectName}/analytics/panels/{panelId} */
"crm.analytics.update": {
objectName: string;
/** Stored panel id (path). */
panelId: string;
/** Partial panel edit: only the keys present change; null clears an optional key; hidden: false is stored as absent. */
patch: {
title?: string;
type?: "stat" | "bar" | "line" | "area" | "donut" | "funnel" | "leaderboard" | "forecast" | "map";
metric?: "count" | "sum" | "avg" | "min" | "max";
metricField?: string | null;
groupBy?: string | null;
dateField?: string | null;
dateBucket?: "day" | "week" | "month" | "quarter" | null;
filters?: Record | null;
/** Width in tracks; null restores the chart kind's default. */
span?: 2 | 3 | 4 | 6 | null;
/** Hide from the strip above the table; still shown in the full analytics view. */
hidden?: boolean;
sortOrder?: number;
derived?: "conversion_rate" | "win_rate" | "won_amount" | "lost_amount" | "open_pipeline" | "pipeline_forecast" | "mrr" | "arr" | null;
};
/** Edit the saved view's own dashboard (forks a copy if it was inheriting). */
viewName?: string;
};
/** Take a chart off the strip above the table (hidden: true). It stays in the full analytics view, counted by the strip's “+N more”. — `dench crm analytics hide` — POST /api/v1/crm/objects/{objectName}/analytics/panels/{panelId}/hide */
"crm.analytics.hide": {
objectName: string;
panelId: string;
viewName?: string;
};
/** Bring a hidden chart back onto the strip (hidden: false). — `dench crm analytics show` — POST /api/v1/crm/objects/{objectName}/analytics/panels/{panelId}/show */
"crm.analytics.show": {
objectName: string;
panelId: string;
viewName?: string;
};
/** Set a card's width on the 6-track dashboard grid: span 2 (third), 3 (half), 4 (two thirds), 6 (full), or null for the chart kind's default. — `dench crm analytics resize` — POST /api/v1/crm/objects/{objectName}/analytics/panels/{panelId}/resize */
"crm.analytics.resize": {
objectName: string;
panelId: string;
/** 2 = third, 3 = half, 4 = two thirds, 6 = full row; null = the chart kind's default. */
span: 2 | 3 | 4 | 6 | null;
viewName?: string;
};
/** Delete one panel (idempotent). Template panels are tombstoned so a re-install never resurrects them. — `dench crm analytics delete` — DELETE /api/v1/crm/objects/{objectName}/analytics/panels/{panelId} */
"crm.analytics.delete": {
objectName: string;
panelId: string;
viewName?: string;
};
/** Persist the full panel order. panelIds must name every stored panel of the targeted dashboard. — `dench crm analytics reorder` — POST /api/v1/crm/objects/{objectName}/analytics/reorder */
"crm.analytics.reorder": {
objectName: string;
/** Every stored panel id of the targeted dashboard, in order. */
panelIds: string[];
viewName?: string;
};
/** Set the object's analytics setup — the field roles shared by every chart and view (amount, stage, won/lost/converted values, stageProbabilities, closeDate, recurrence, owner, location). Replaces the mapping; with merge: true only the keys given change and null clears one. — `dench crm analytics roles` — PUT /api/v1/crm/objects/{objectName}/analytics/roles */
"crm.analytics.roles": {
objectName: string;
/** Field roles as an edit: the same keys; null clears one. Applied onto the stored mapping with merge: true, otherwise replaces it. */
fieldRoles: {
/** Numeric field holding the deal value ("Amount") — the "Value field" in the setup sheet. */
amount?: string | null;
/** Enum field driving the pipeline ("Stage" / "Status") — the "Progress field". */
stage?: string | null;
/** `stage` options that count as won (closed, successful). */
wonValues?: string[] | null;
/** `stage` options that count as lost (closed, gone). */
lostValues?: string[] | null;
/** `stage` options that count as converted (a lead reached "Qualified"). */
convertedValues?: string[] | null;
/** Date field for time bucketing, forecasts and the period pill (falls back to $createdAt). */
closeDate?: string | null;
/** User field naming who owns each record ("Owner"). Rankings on it default to a narrow card and draw member faces. */
owner?: string | null;
/** Place-name field maps plot. May be a relation path like "Account.HQ Location" (one hop through a relation). */
location?: string | null;
/** How a won amount recurs: annual treats Amount as ARR, monthly as MRR, one_time disables MRR/ARR panels. */
recurrence?: "monthly" | "annual" | "one_time" | null;
/** Chance of closing per OPEN stage, 0..1, for the weighted forecast. Unlisted stages are spaced evenly; won = 1, lost = 0. */
stageProbabilities?: Record | null;
};
/** true: change only the keys given (null clears one). Default replaces the whole mapping. */
merge?: boolean;
};
/** Collapse the analytics strip above the object's table (team-shared, like saved views). — `dench crm analytics collapse` — POST /api/v1/crm/objects/{objectName}/analytics/collapse */
"crm.analytics.collapse": {
objectName: string;
};
/** Expand the analytics strip above the object's table. — `dench crm analytics expand` — POST /api/v1/crm/objects/{objectName}/analytics/expand */
"crm.analytics.expand": {
objectName: string;
};
/** Give a saved view its own dashboard (mode "own": starts as a copy of the object's panels) or hand it back to the object's shared one (mode "shared"). — `dench crm analytics scope` — POST /api/v1/crm/objects/{objectName}/views/{viewName}/analytics/scope */
"crm.analytics.scope": {
objectName: string;
viewName: string;
/** "own": the view gets its own dashboard (a copy of the object's panels); "shared": it shows the object's again. */
mode: "own" | "shared";
};
/** Request enrichment for one CRM cell. — `dench crm enrich cell` — POST /api/v1/crm/enrich/cell */
"crm.enrich.cell": {
objectName: string;
entryId: string;
fieldName: string;
};
/** Request enrichment for a CRM object. — `dench crm enrich object` — POST /api/v1/crm/enrich/object */
"crm.enrich.object": {
objectName: string;
fieldName: string;
/** Only enrich empty cells. Defaults true. */
missingOnly?: boolean;
};
/** Search for people through the enrichment gateway. `companyDomains` takes up to 1000 domains (e.g. a resolved company list; `dench leads find --company-list ` resolves one for you) — the gateway fans large lists out as sequential vendor-safe chunks behind one `nextPageToken` stream. — `dench crm people search` — POST /api/v1/enrichment/people/search */
"crm.people.search": {
linkedinUrl?: string;
fullName?: string;
firstName?: string;
lastName?: string;
/** Current-title text only. For profile or work descriptions, use profileKeywords. Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
keywords?: string;
companyDomain?: string;
companyName?: string;
/** Current job titles (free text). Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
titles?: Array;
/** Past job titles (free text). */
pastTitles?: Array;
personNames?: Array;
/** Seniority in any past role, independent of pastCompanyNames/pastTitles. Current seniority vocabulary plus Founding Team. Founder includes Co-Founder; C-level and Founding Team do not imply founder. Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
pastSeniorities?: Array;
/** Text in headline/about or any current/past work description. OR includes, global exclusions. Always loose text matching; missing evidence does not match. */
profileKeywords?: Array;
/** Current employer business focus/model (e.g. SaaS, B2B, Cloud), from description/tagline/business tags; distinct from legal companyTypes. Same employer as other current-job filters. Always loose text matching. May need another filter to retrieve candidates. */
companyKeywords?: Array;
/** Exact enum: Owner, Founder, C-level, Partner, VP, Head, Director, Manager, Senior. */
seniorities?: Array;
/** Exact enum, e.g. Sales, Marketing, Software. Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
jobFunctions?: Array;
/** Exact enum, e.g. SDR/BDR, Demand Generation. Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
subFunctions?: Array;
/** Exact enum, e.g. Software Development. Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
industries?: Array;
/** Person locations — cities, states, countries, or metros (free text). Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
locations?: Array;
/** Exact buckets: 1-10, 11-50, 51-200, 201-500, 501-1000, 1001-5000, 5001-10000, 10001+. */
headcountRanges?: Array;
companyNames?: Array;
pastCompanyNames?: Array;
companyDomains?: Array;
/** Company HQ locations — cities, states, countries, or metros (free text). Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
companyHeadquarters?: Array;
/** Exact enum: Partnership, Nonprofit, Educational, Privately Held, Public Company, Self-Owned, Self-Employed, Government Agency. */
companyTypes?: Array;
/** Universities attended (free text). Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
universities?: Array;
/** Profile skills (free text). Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
skills?: Array;
/** Spoken languages, e.g. English, German. Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
languages?: Array;
yearsInRole?: {
min?: number;
max?: number;
};
yearsAtCompany?: {
min?: number;
max?: number;
};
daysSinceJobChange?: {
min?: number;
max?: number;
};
/** Company founded-year range, e.g. {min: 2015, max: 2024}. */
foundedYears?: {
min?: number;
max?: number;
};
/** Match free-text filters as whole strings. Default false: titles ['Founder'] also returns 'Co-Founder' and 'Founder & CEO'. Set true to narrow. Domains, LinkedIn URLs, enum filters and numeric ranges are always exact; keywords is always a loose text probe. */
exactMatch?: boolean;
limit?: number;
offset?: number;
/** Opaque token from a previous response's metadata.nextPageToken. */
pageToken?: string;
preferCache?: boolean;
};
/** Search for companies through the enrichment gateway (also `dench companies find`). — `dench crm companies search` — POST /api/v1/enrichment/company/search */
"crm.companies.search": {
domain?: string;
name?: string;
linkedinUrl?: string;
keywords?: string;
/** Exact enum, e.g. Software Development. Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
industries?: string[];
/** Cities, states, countries, or metros (free text). Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
locations?: string[];
/** Exact buckets: 1-10, 11-50, 51-200, 201-500, 501-1000, 1001-5000, 5001-10000, 10001+. */
headcountRanges?: string[];
/** Match free-text filters (name, locations, keywords) as whole strings. Default false. Domain, linkedinUrl and enum filters are always exact. */
exactMatch?: boolean;
limit?: number;
offset?: number;
/** Opaque token from a previous response's metadata.nextPageToken. */
pageToken?: string;
preferCache?: boolean;
};
/** Search job postings through Dench find. `companyDomains` takes up to 1000 domains (chunked automatically); `dench jobs find --company-list ` resolves a saved company list into it. — `dench jobs find` — POST /api/v1/enrichment/jobs/search */
"enrichment.jobs.search": {
/** Job titles (free text). Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
titles?: string[];
/** Cities, states, countries, or metros (free text). Accepted values: see `dench leads filters` or https://dench.com/references/find-filter-options.json. */
locations?: string[];
companyDomain?: string;
companyName?: string;
companyLinkedinUrl?: string;
keywords?: string;
postedAfter?: string;
/** Inclusive posting end date (YYYY-MM-DD); includes the whole UTC day. */
postedBefore?: string;
limit?: number;
/** Opaque token from a previous response's metadata.nextPageToken. */
pageToken?: string;
preferCache?: boolean;
};
/** Look up one person profile. — `dench crm enrich cell` — POST /api/v1/enrichment/person */
"enrichment.person": {
linkedinURL?: string;
linkedinID?: string;
email?: string;
[key: string]: unknown;
};
/** Look up one company profile. — `dench crm enrich cell` — POST /api/v1/enrichment/company */
"enrichment.company": {
website?: string;
linkedinURL?: string;
linkedinID?: string;
[key: string]: unknown;
};
/** Look up verified emails and phones for people. — `dench crm enrich cell` — POST /api/v1/enrichment/person/contact */
"enrichment.person.contact": {
contacts: Record[];
[key: string]: unknown;
};
/** Look up up to 100 people or company profiles without buying emails or phones. Pass kind (people|companies) and lookups; results preserve inputIndex. People require linkedinUrl or email; companies require domain or linkedinUrl. — `dench crm enrich profiles` — POST /api/v1/enrichment/profiles/bulk */
"enrichment.profiles.bulk": {
kind: "people" | "companies";
/** Input order is preserved. Does not purchase contact data or write CRM records. */
lookups: Array<{
linkedinUrl: string;
} | {
email: string;
} | {
domain: string;
}>;
};
/** Read company employees, founders, funding, investors, acquisitions, or investments. Pass domain or linkedinUrl, kind, limit (1–100), and optional pageToken. Does not import CRM records or buy contacts. — `dench companies related` — POST /api/v1/enrichment/company/related */
"enrichment.company.related": {
/** Company domain. Provide exactly one of domain or linkedinUrl. */
domain?: string;
/** Company LinkedIn URL. Omit when domain is provided. */
linkedinUrl?: string;
kind: "employees" | "founders" | "funding" | "investors" | "acquisitions" | "investments";
/** Records per page; defaults to 25. Keep unchanged when using pageToken. */
limit?: number;
/** Opaque metadata.nextPageToken from this same company, kind, and page size. */
pageToken?: string;
};
/** Preview how many records a find would return. — `dench leads find --limit 1` — POST /api/v1/enrichment/search/preview */
"enrichment.search.preview": {
kind?: "people" | "companies" | "jobs";
[key: string]: unknown;
};
/** List one-click CRM templates (objects + automations + sample rows) with per-workspace installed flags. — `dench crm templates list` — GET /api/v1/crm/templates */
"crm.templates.list": Record;
/** Install a CRM template. Idempotent: re-installs top up missing pieces and never duplicate objects, automations, or sample rows. — `dench crm templates install` — POST /api/v1/crm/templates/{templateId}/install */
"crm.templates.install": Record;
/** Delete a CRM template's sample rows (tagged "Sample"); schema, views, and automations stay. — `dench crm templates remove-sample-data` — POST /api/v1/crm/templates/{templateId}/remove-sample-data */
"crm.templates.removeSampleData": Record;
/** List deterministic CRM automations (rules that fire inside entry writes, e.g. qualified prospect → opportunity). — `dench crm automations list` — GET /api/v1/crm/automations */
"crm.automations.list": Record;
/** Enable or disable a CRM automation. — `dench crm automations enable|disable` — PATCH /api/v1/crm/automations/{automationId} */
"crm.automations.setEnabled": Record;
/** List meetings, newest first, with status and participants. — `dench meetings list` — GET /api/v1/meetings */
"meetings.list": {
limit?: number;
/** continueCursor from the prior page. */
cursor?: string;
};
/** Read a meeting's transcript, in order, one page at a time. — `dench meetings transcript get` — GET /api/v1/meetings/{meetingEntryId}/transcript */
"meetings.transcript.list": {
meetingEntryId: string;
limit?: number;
cursor?: string;
};
/** Append up to 500 transcript segments to a meeting (speakers with an email link to existing people). — `dench meetings transcript set` — POST /api/v1/meetings/{meetingEntryId}/transcript */
"meetings.transcript.append": {
meetingEntryId: string;
segments: Array<{
/** Ordinal; omitted continues after the last row. */
seq?: number;
speakerName?: string | null;
/** Links the speaker to an existing person by exact email. */
speakerEmail?: string | null;
startMs?: number;
endMs?: number;
text: string;
}>;
};
/** Delete a batch of a meeting's transcript segments; repeat until `done` before re-appending. — `dench meetings transcript clear` — DELETE /api/v1/meetings/{meetingEntryId}/transcript */
"meetings.transcript.clear": {
meetingEntryId: string;
};
/** List a meeting's action items. — `dench meetings action-items list` — GET /api/v1/meetings/{meetingEntryId}/action-items */
"meetings.actionItems.list": {
meetingEntryId: string;
};
/** Add action items to a meeting; `replace: true` swaps the whole list. — `dench meetings action-items set` — POST /api/v1/meetings/{meetingEntryId}/action-items */
"meetings.actionItems.add": {
meetingEntryId: string;
items: Array<{
text: string;
ownerName?: string | null;
done?: boolean;
}>;
/** Delete the existing list first. */
replace?: boolean;
};
/** Tick or untick one action item. — `dench meetings action-items done` — PATCH /api/v1/meetings/action-items/{itemId} */
"meetings.actionItems.setDone": {
itemId: string;
done: boolean;
};
/** Delete one action item. — `dench meetings action-items delete` — DELETE /api/v1/meetings/action-items/{itemId} */
"meetings.actionItems.delete": {
itemId: string;
};
/** Who was in the meeting, merged with the linked calendar invite. — `dench meetings participants list` — GET /api/v1/meetings/{meetingEntryId}/participants */
"meetings.participants.list": {
meetingEntryId: string;
};
/** Record attendees by name/email; known members and existing people are linked, nobody is created. — `dench meetings participants set` — PUT /api/v1/meetings/{meetingEntryId}/participants */
"meetings.participants.set": {
meetingEntryId: string;
participants: Array<{
name?: string | null;
email?: string | null;
isHost?: boolean;
}>;
/** Drop attendees not in this list. Default merges. */
replace?: boolean;
};
/** Whether a meeting is shared with the whole workspace, and whether you may change that. — `dench meetings share` — GET /api/v1/meetings/{meetingEntryId}/sharing */
"meetings.sharing.get": {
meetingEntryId: string;
};
/** Share one meeting with the workspace, or take it back to owner + invitees. — `dench meetings share --workspace` — PUT /api/v1/meetings/{meetingEntryId}/sharing */
"meetings.sharing.set": {
meetingEntryId: string;
/** true opens the meeting to every workspace member; false returns it to the owner plus the members who were on the invite. */
shared: boolean;
};
/** Start Dench Emailing Service sender verification. — `dench email identity verify` — POST /api/v1/email/identities/verify */
"email.identity.verify": {
/** Identity scope. `domain` verifies via DNS (DKIM); `email` verifies a single mailbox via a confirmation email. */
type: "domain" | "email";
/** Domain (example.com) or mailbox (founder@example.com). */
identity: string;
/** Optional provider configuration set override. */
configurationSetName?: string;
};
/** Create a durable Dench email sending identity record. — `dench email identity verify` — POST /api/v1/email/sending-identities */
"email.identity.create": {
/** Identity scope. `domain` verifies via DNS (DKIM); `email` verifies a single mailbox via a confirmation email. */
type: "domain" | "email";
/** Lowercase domain; required when type is `domain`. */
domain?: string;
/** Sender mailbox, e.g. founder@example.com. */
fromEmail: string;
/** Default display name for the sender. */
fromName?: string;
replyToEmail?: string;
configurationSetName?: string;
};
/** Refresh a Dench Emailing Service sender verification status. — `dench email identity status --identity-id` — POST /api/v1/email/sending-identities/{identityId}/status */
"email.identity.refresh": {
/** Sending identity id. */
identityId: string;
};
/** Begin the full Dench sender verification flow for a sending identity. — `dench email identity verify` — POST /api/v1/email/sending-identities/{identityId}/verify */
"email.identity.beginVerification": {
/** Sending identity id from `POST /email/sending-identities`. */
identityId: string;
};
/** Read Dench Emailing Service sender verification status. — `dench email identity status` — GET /api/v1/email/identities/{identity}/status */
"email.identity.status": {
/** Email address or domain to check. */
identity: string;
};
/** List the workspace's email sending identities. — `dench email identity list` — GET /api/v1/email/sending-identities */
"email.identity.list": {
/** Filter identities by domain. */
domain?: string;
};
/** Fetch DNS records (DKIM/SPF/DMARC) for a pending domain sender verification. — `dench email identity dns` — GET /api/v1/email/sending-identities/{identityId}/dns */
"email.identity.dns": {
/** Sending identity id. */
identityId: string;
};
/** Soft-disable a sender (or every sender on a domain) for the workspace; sends are blocked until restored. — `dench email identity disable` — POST /api/v1/email/sending-identities/{identity}/disable */
"email.identity.disable": {
/** Identity id, sender email, or domain (disables every sender on the domain). */
identity: string;
};
/** Restore a previously disabled sender for the workspace. — `dench email identity restore` — POST /api/v1/email/sending-identities/{identity}/restore */
"email.identity.restore": {
/** Identity id, sender email, or domain. */
identity: string;
};
/** Permanently delete a sender (or every sender on a domain) from the workspace; re-adding it requires verification from scratch. — `dench email identity delete` — DELETE /api/v1/email/sending-identities/{identity} */
"email.identity.delete": {
/** Identity id, sender email, or domain (deletes every sender on the domain). Permanent: re-adding requires re-verification. */
identity: string;
};
/** Search authorized synced mailbox headers with bounded cursor pagination. isDone covers stored records, not provider completeness. — `dench email inbox search` — GET /api/v1/email/inbox/search */
"email.inbox.search": {
limit?: number;
cursor?: string;
mailbox?: string;
from?: string;
to?: string;
subject?: string;
/** Words that must all appear in the subject, sender or recipients (case-insensitive). */
query?: string;
/** Inclusive Unix milliseconds; missing sentAt falls back to createdAt. */
after?: number;
/** Exclusive Unix milliseconds. */
before?: number;
direction?: "inbound" | "outbound";
};
/** Read a bounded page of synced headers using an exact thread handle from inbox search; bodies remain on demand. — `dench email inbox thread` — GET /api/v1/email/inbox/threads/{threadHandle} */
"email.inbox.thread": {
limit?: number;
cursor?: string;
threadHandle: string;
};
/** Read a bounded stored body using an exact message handle from inbox search; rechecks mailbox and record privacy. — `dench email inbox message` — GET /api/v1/email/inbox/messages/{messageHandle} */
"email.inbox.message": {
messageHandle: string;
format?: "text" | "html";
maxBytes?: number;
};
/** Create an editable Dench email draft and synchronize its selected native mailbox asynchronously. Use a stable requestId for retries. — `dench email draft create` — POST /api/v1/email/drafts */
"email.drafts.create": {
organizationId?: string;
threadId?: string;
mailbox?: string;
requestId: string;
to?: {
email: string;
name?: string;
}[];
cc?: {
email: string;
name?: string;
}[];
bcc?: {
email: string;
name?: string;
}[];
subject?: string;
htmlBody?: string;
textBody?: string;
attachments?: {
storageId: string;
name: string;
contentType: string;
size: number;
}[];
};
/** Read an email draft, eligible senders and provider sync status. — `dench email draft show` — GET /api/v1/email/drafts/{draftId} */
"email.drafts.get": {
draftId: string;
};
/** List native mailboxes this caller may use. — `dench email draft senders` — GET /api/v1/email/drafts/senders */
"email.drafts.senders": {
organizationId?: string;
};
/** Save draft content using expectedRevision; sender changes require a new draft after provider binding. — `dench email draft update` — PATCH /api/v1/email/drafts/{draftId} */
"email.drafts.update": {
draftId: string;
expectedRevision: number;
patch: {
mailboxAccountId?: string;
to?: {
email: string;
name?: string;
}[];
cc?: {
email: string;
name?: string;
}[];
bcc?: {
email: string;
name?: string;
}[];
subject?: string;
htmlBody?: string;
textBody?: string;
attachments?: {
storageId: string;
name: string;
contentType: string;
size: number;
}[];
};
};
/** Send the current Dench draft revision through its native mailbox. Repeated requests share a durable receipt. — `dench email draft send` — POST /api/v1/email/drafts/{draftId}/send */
"email.drafts.send": {
draftId: string;
expectedRevision: number;
requestId?: string;
};
/** Upload attachment bytes authorized against this draft before adding its storage reference. — `dench email draft attach` — POST /api/v1/email/drafts/{draftId}/attachments */
"email.drafts.upload": {
draftId: string;
name: string;
contentType: string;
contentBase64: string;
};
/** Load the selected mailbox signature for explicit visible insertion. — `dench email draft signature` — GET /api/v1/email/drafts/{draftId}/signature */
"email.drafts.signature": {
draftId: string;
};
/** Send through a native Gmail or Outlook mailbox with a durable request receipt. Only use for an explicit send request. — `dench email send [--mailbox ]` — POST /api/v1/email/native/send */
"email.native.send": {
organizationId?: string;
threadId?: string;
mailbox?: string;
requestId: string;
to: {
email: string;
name?: string;
}[];
cc?: {
email: string;
name?: string;
}[];
bcc?: {
email: string;
name?: string;
}[];
subject?: string;
htmlBody?: string;
textBody?: string;
attachments?: {
storageId: string;
name: string;
contentType: string;
size: number;
}[];
};
/** Send one raw email (no campaign): to/cc/bcc, custom from-name and reply-to, optional reply threading via replyToMessageId. — `dench email send --from ` — POST /api/v1/email/send */
"email.send": {
/** Verified sender mailbox. */
fromEmail: string;
/** Display name override. */
fromName?: string;
/** Primary recipients. to+cc+bcc is capped at 50. */
to: {
/** Recipient email address. */
email: string;
/** Optional display name. */
name?: string;
}[];
cc?: {
/** Recipient email address. */
email: string;
/** Optional display name. */
name?: string;
}[];
bcc?: {
/** Recipient email address. */
email: string;
/** Optional display name. */
name?: string;
}[];
replyToEmail?: string;
subject: string;
/** HTML body. Provide htmlBody and/or textBody. */
htmlBody?: string;
textBody?: string;
/** Dench messageId or RFC 5322 Message-ID to thread this send as a reply. */
replyToMessageId?: string;
configurationSetName?: string;
};
/** Read a raw email message's status and open/click counters. — `dench email message status` — GET /api/v1/email/messages/{messageId} */
"email.message.get": {
/** Raw email message id. */
messageId: string;
};
/** List recent raw email messages for the workspace. — `dench email message list` — GET /api/v1/email/messages */
"email.message.list": {
limit?: number;
/** Raw email message lifecycle status. */
status?: "queued" | "sent" | "failed" | "delivered" | "opened" | "clicked" | "bounced" | "complained";
};
/** Create a managed email campaign template. — `dench email template create` — POST /api/v1/email/templates */
"email.template.create": {
name: string;
/** Supports {{variable}} placeholders. */
subject: string;
/** HTML body with {{variable}} placeholders. */
htmlBody: string;
textBody?: string;
/** Template lifecycle status. Defaults to `active`. */
status?: "draft" | "active" | "archived";
};
/** Preview a managed email campaign template. — `dench email template preview` — POST /api/v1/email/templates/{templateId}/preview */
"email.template.preview": {
templateId: string;
/** Personalization data for {{variable}} placeholders. */
data?: Record;
};
/** Create a managed email campaign draft from a connected inbox or verified sender. — `dench email campaign create` — POST /api/v1/email/campaigns */
"email.campaign.create": {
name: string;
/** Sending identity id OR a verified from-email. */
identityId: string;
templateId: string;
/** Epoch ms to start sending; omit to send on submit. */
scheduledAt?: number;
maxRecipients?: number;
batchSize?: number;
metadata?: Record;
};
/** Read campaign status and counters. — `dench email campaign status` — GET /api/v1/email/campaigns/{campaignId} */
"email.campaign.get": {
campaignId: string;
};
/** Add recipients to a managed email campaign. — `dench email campaign recipients add` — POST /api/v1/email/campaigns/{campaignId}/recipients */
"email.campaign.recipients.add": {
campaignId: string;
/** Up to 500 recipients per call. */
recipients: {
/** Recipient email address. */
email: string;
displayName?: string;
/** Optional CRM people entry to link the send to. */
personEntryId?: string;
companyEntryId?: string;
/** Values for {{variable}} placeholders in the template. */
personalization?: Record;
}[];
};
/** Preview rendered messages for a campaign sample. — `dench email campaign preview` — POST /api/v1/email/campaigns/{campaignId}/preview */
"email.campaign.preview": {
campaignId: string;
/** Number of rendered previews to return (default 5). */
sample?: number;
};
/** Submit a campaign for approval or sending. — `dench email campaign submit` — POST /api/v1/email/campaigns/{campaignId}/submit */
"email.campaign.submit": {
campaignId: string;
/** Approval request id when workspace policy requires human sign-off. */
approvalId?: string;
};
/** Pause a sending email campaign. — `dench email campaign pause` — POST /api/v1/email/campaigns/{campaignId}/pause */
"email.campaign.pause": {
campaignId: string;
};
/** Resume a paused email campaign. — `dench email campaign resume` — POST /api/v1/email/campaigns/{campaignId}/resume */
"email.campaign.resume": {
campaignId: string;
};
/** Cancel an email campaign. — `dench email campaign cancel` — POST /api/v1/email/campaigns/{campaignId}/cancel */
"email.campaign.cancel": {
campaignId: string;
};
/** List managed email campaigns (paginated, optional status filter). — `dench email campaign list` — GET /api/v1/email/campaigns */
"email.campaign.list": {
limit?: number;
cursor?: string;
status?: string;
};
/** List a campaign's recipients (paginated, optional status filter). — `dench email campaign recipients list` — GET /api/v1/email/campaigns/{campaignId}/recipients */
"email.campaign.recipients.list": {
campaignId: string;
limit?: number;
cursor?: string;
status?: string;
/** Default true: attach each recipient's CRM record. */
includePeople?: boolean;
};
/** Add campaign recipients from a CRM object: pick the object, the email field, and the entry ids. — `dench email campaign recipients add-from-object` — POST /api/v1/email/campaigns/{campaignId}/recipients/from-object */
"email.campaign.recipients.addFromObject": Record;
/** Create a follow-up sequence from a connected inbox or verified sender. — `dench email sequence create` — POST /api/v1/email/sequences */
"email.sequence.create": Record;
/** Update a sequence's name, sender, or reply/stop settings. — `dench email sequence update` — POST /api/v1/email/sequences/{sequenceId} */
"email.sequence.update": Record;
/** Activate a sequence so its enrollments begin sending. — `dench email sequence activate` — POST /api/v1/email/sequences/{sequenceId}/activate */
"email.sequence.activate": Record;
/** Deactivate a sequence (pause sending, revert to draft). — `dench email sequence deactivate` — POST /api/v1/email/sequences/{sequenceId}/deactivate */
"email.sequence.deactivate": Record;
/** Archive a sequence (stops all sending). — `dench email sequence archive` — POST /api/v1/email/sequences/{sequenceId}/archive */
"email.sequence.archive": Record;
/** List the workspace's follow-up sequences. — `dench email sequence list` — GET /api/v1/email/sequences */
"email.sequence.list": Record;
/** Read a sequence with its ordered steps. — `dench email sequence status` — GET /api/v1/email/sequences/{sequenceId} */
"email.sequence.get": Record;
/** Add a step (subject, body, and wait delay) to a sequence. — `dench email sequence steps add` — POST /api/v1/email/sequences/{sequenceId}/steps */
"email.sequence.step.add": Record;
/** Update a sequence step's subject, body, or delay. — `dench email sequence steps update` — POST /api/v1/email/sequences/steps/{stepId} */
"email.sequence.step.update": Record;
/** Remove a step from a sequence. — `dench email sequence steps remove` — POST /api/v1/email/sequences/steps/{stepId}/remove */
"email.sequence.step.remove": Record;
/** Reorder a sequence's steps. — `dench email sequence steps reorder` — POST /api/v1/email/sequences/{sequenceId}/steps/reorder */
"email.sequence.step.reorder": Record;
/** Enroll explicit recipients into a sequence. — `dench email sequence enroll` — POST /api/v1/email/sequences/{sequenceId}/enroll */
"email.sequence.enroll": Record;
/** Enroll people from a CRM object: pick the object, the email field, and the entry ids. — `dench email sequence enroll-from-object` — POST /api/v1/email/sequences/{sequenceId}/enroll/from-object */
"email.sequence.enrollFromObject": Record;
/** List a sequence's enrollments (paginated, optional status filter). — `dench email sequence enrollments list` — GET /api/v1/email/sequences/{sequenceId}/enrollments */
"email.sequence.enrollments.list": {
sequenceId: string;
limit?: number;
cursor?: string;
status?: string;
};
/** Pause one enrollment. — `dench email sequence enrollment pause` — POST /api/v1/email/sequences/enrollments/{enrollmentId}/pause */
"email.sequence.enrollment.pause": Record;
/** Resume one paused enrollment. — `dench email sequence enrollment resume` — POST /api/v1/email/sequences/enrollments/{enrollmentId}/resume */
"email.sequence.enrollment.resume": Record;
/** Stop one enrollment (no further steps send). — `dench email sequence enrollment stop` — POST /api/v1/email/sequences/enrollments/{enrollmentId}/stop */
"email.sequence.enrollment.stop": Record;
/** Discover LinkedIn sender ids, labels, availability and remaining capacity visible to the acting workspace member. Never returns provider credentials. — `dench campaign accounts` — GET /api/v1/campaigns/accounts/linkedin */
"campaigns.accounts.linkedin": Record;
/** Discover the mailboxes the acting workspace member can send campaign email from: ids for emailMailboxAccountIds, status, warm-up state and when it is done, the daily cold-email cap and what is left today. Never returns provider credentials. — `dench campaign accounts` — GET /api/v1/campaigns/accounts/email */
"campaigns.accounts.email": Record;
/** List outreach campaigns (archived ones on request). — `dench campaign list [--with-references]` — GET /api/v1/campaigns */
"campaigns.list": {
/** Include bounded navigation references. Array reads return {data, references, referenceCoverage}; existing result envelopes keep their fields. */
withReferences?: boolean;
/** Include archived campaigns. */
includeArchived?: boolean;
};
/** Create a draft outreach campaign with email and LinkedIn sender pools and a business-hours send window in the given timezone. — `dench campaign create` — POST /api/v1/campaigns */
"campaigns.create": {
name: string;
/** Campaign email sender pool: up to 50 emailMailboxAccounts ids both you and the campaign owner may use. Omit to preserve existing configuration; [] clears the pool. A person's sender stays fixed for their conversation. Launch validates sender availability. */
emailMailboxAccountIds?: string[];
/** Campaign LinkedIn sender pool: up to 50 linkedinAccounts ids both you and the campaign owner may use. Omit to preserve existing configuration; [] clears the pool. Legacy step senders remain until a pool is configured. */
linkedinAccountIds?: string[];
/** IANA timezone for the default Mon-Fri 9-18 send window. */
timezone?: string;
};
/** Copy a campaign into a new draft named " copy": the same steps, schedule, sender pools and personalized lines, its lead source paused, and no people. To reach more than 300 people, duplicate and add the next page of the search to the copy. — `dench campaign duplicate` — POST /api/v1/campaigns/{sequenceId}/duplicate */
"campaigns.duplicate": {
/** The campaign to copy; any status, including archived. */
sequenceId: string;
};
/** Read a campaign: settings, steps, people, their current step and hold, last touches, linked leads. — `dench campaign get` — GET /api/v1/campaigns/{sequenceId} */
"campaigns.get": {
sequenceId: string;
};
/** Campaign funnel: enrolled, contacted, invited, accepted, messaged, replied, opens and clicks, and for a workflow how many people took each If / else and Wait for them to accept path (branchStats). — `dench campaign get` — GET /api/v1/campaigns/{sequenceId}/analytics */
"campaigns.analytics": {
sequenceId: string;
};
/** What the campaign is doing now, what comes next and when, and why it is holding. — `dench campaign get` — GET /api/v1/campaigns/{sequenceId}/live */
"campaigns.live": {
sequenceId: string;
};
/** Where a campaign's people sit in the workspace's lead pipeline right now: each lead Status in order with how many enrolled leads are at it, and how many have no lead or a status outside the pipeline. — `dench campaign pipeline` — GET /api/v1/campaigns/{sequenceId}/pipeline */
"campaigns.pipeline": {
sequenceId: string;
};
/** Change a campaign's name, sender pools, send windows, daily email cap, stop-on-reply, start or end. Works until archived; existing conversations keep their assigned sender. — `dench campaign set` — PATCH /api/v1/campaigns/{sequenceId} */
"campaigns.update": {
sequenceId: string;
name?: string;
/** Campaign email sender pool: up to 50 emailMailboxAccounts ids both you and the campaign owner may use. Omit to preserve existing configuration; [] clears the pool. A person's sender stays fixed for their conversation. Launch validates sender availability. */
emailMailboxAccountIds?: string[];
/** Campaign LinkedIn sender pool: up to 50 linkedinAccounts ids both you and the campaign owner may use. Omit to preserve existing configuration; [] clears the pool. Legacy step senders remain until a pool is configured. */
linkedinAccountIds?: string[];
/** Stop a person's remaining steps when they reply. */
stopOnReply?: boolean;
/** Epoch ms; null clears the start. */
startsAt?: number | null;
/** Epoch ms; null clears the end. */
endsAt?: number | null;
sendSchedules?: {
/** Label, e.g. "Mon-Fri 9-18". */
name: string;
/** IANA timezone, e.g. America/Los_Angeles. */
timezone: string;
/** First hour sends may go out (0-23). */
startHour: number;
/** Hour sends stop (1-24); must be after startHour. */
endHour: number;
/** Days sends may go out: 0 = Sunday ... 6 = Saturday. */
weekdays: number[];
}[];
/** Legacy single-window timezone. Prefer sendSchedules. */
timezone?: string;
/** Legacy single-window start hour. Prefer sendSchedules. */
sendWindowStartHour?: number;
/** Legacy single-window end hour. Prefer sendSchedules. */
sendWindowEndHour?: number;
/** Legacy single-window weekdays, 0 = Sunday. Prefer sendSchedules. */
sendWeekdays?: number[];
/** Cold emails per day, 1-100. */
dailySendCap?: number;
};
/** Replace every step of a draft campaign (email, wait, LinkedIn visit, connection request, message, post reaction, InMail, If / else, Wait for them to accept). A workflow (stepModel workflow, or steps with key, next, yesNext or noNext) branches: condition and wait_for_connection steps continue on yesNext or noNext, other steps on next; each points at another step's key or end. Steps are checked first: a field the step type would drop, missing text, {TOKEN} single braces or paths that don't hold together are rejected, not saved. Configure sender pools on the campaign; step sender ids are legacy-only. — `dench campaign steps set` — PUT /api/v1/campaigns/{sequenceId}/steps */
"campaigns.steps.replace": {
sequenceId: string;
/** Every step, in send order (a workflow may list them in any order; they are stored in path order). Rejected, not trimmed: a field the step type would drop (onlyIf off email/post-like steps, a body on a visit, reply-in-thread on the first email, condition fields its check ignores), a missing subject or required body, an overlength note/message/InMail subject, out-of-range wait/withdrawal/post-age/acceptance values, {TOKEN} single braces, which would be sent as typed, and workflow paths that don't hold together (duplicate keys, a target that isn't a key or end, a condition or wait_for_connection without both yesNext and noNext, or any other step with them). */
steps: Array<{
/** Step type. condition (If / else) and wait_for_connection (Wait for them to accept) send nothing; they continue on yesNext or noNext and make the campaign a workflow. */
type: "wait" | "email" | "linkedin_connection" | "linkedin_message" | "linkedin_visit" | "linkedin_like_post" | "linkedin_inmail" | "condition" | "wait_for_connection";
/** wait steps: hours to wait before the next step. */
waitHours?: number;
/** Legacy email step sender. Pool campaigns choose senders in emailMailboxAccountIds on the campaign, not on individual steps. */
emailMailboxAccountId?: string;
/** email steps: subject; {{Column}} merge tokens allowed. */
emailSubject?: string;
/** email steps: HTML body. Sent instead of emailTextBody when both are set, so when you edit the text, update or drop this too. */
emailHtmlBody?: string;
/** email steps: plain-text body; used as the email when there is no emailHtmlBody. */
emailTextBody?: string;
/** email steps: thread under the previous email step instead of a new subject. */
emailReplyToPrevious?: boolean;
/** email and linkedin_like_post only: always (default), connected or not_connected. InMail automatically skips connected people; do not set a condition on it. */
onlyIf?: "always" | "connected" | "not_connected";
/** Legacy LinkedIn step sender (also on wait_for_connection, and on a condition that checks linkedin_connected). Pool campaigns use the campaign's linkedinAccountIds instead. */
linkedinAccountId?: string;
/** linkedin_connection: the invite note (optional, up to 300 chars; sent without it if LinkedIn refuses the note); linkedin_message: the required message (up to 3000 chars); linkedin_inmail: the required message (up to 1900 chars). */
linkedinMessageBody?: string;
/** linkedin_message: what to do when the invite is still pending. */
linkedinMessageMode?: "skip_if_not_connected" | "wait_until_connected";
/** linkedin_message with linkedinMessageMode=wait_until_connected: wait 1-2160 hours for acceptance (default 720); after a connection step this mode is the default in a linear campaign. wait_for_connection: wait 24-2160 hours (1-90 days, default 720) before taking the Not accepted path. */
connectionTimeoutHours?: number;
/** linkedin_like_post only: the reaction to leave (default like). */
likeReaction?: "like" | "celebrate" | "support" | "love" | "insightful" | "funny";
/** linkedin_like_post only: select the newest eligible original post from the last 1-365 days (default 30). */
likeMaxPostAgeDays?: number;
/** linkedin_like_post only: before the immediately preceding wait starts, check for eligible posts. If none exist, skip that wait and the like step, keeping following waits. Defaults to false when omitted. */
likeSkipPreviousWait?: boolean;
/** linkedin_like_post only: skip consecutive following wait steps whenever the reaction step is skipped (default true). False keeps those waits. */
likeSkipNextWait?: boolean;
/** linkedin_like_post only: how many recent posts to react to, 1-3 (default 1). */
likeCount?: number;
/** linkedin_inmail only: optional subject, up to 200 characters; merge tokens allowed. Clear with an empty string. */
inmailSubject?: string;
/** linkedin_connection only: withdraw a still-pending invitation sent by this campaign after 168-2160 hours (7-90 days). Omitted means off on creation; null turns it off on edit. */
withdrawAfterHours?: number | null;
/** Workflow steps: this step's id, unique in the campaign, which next, yesNext and noNext point at (not "end"). Left out, one is made. */
key?: string;
/** Workflow steps: where a step that isn't a condition or wait_for_connection goes; left out, the following step: another step's key, or "end" to stop there. Fixed once the campaign launches. */
next?: string;
/** Workflow steps: condition and wait_for_connection only, required: where the Yes (Accepted) path goes: another step's key, or "end" to stop there. Fixed once the campaign launches. */
yesNext?: string;
/** Workflow steps: condition and wait_for_connection only, required: where the No (Not accepted) path goes: another step's key, or "end" to stop there. Fixed once the campaign launches. */
noNext?: string;
/** condition only, required: linkedin_connected, has_email, opened_last_email (needs an email before it on every path) or field_value. Fixed once the campaign launches. */
check?: "linkedin_connected" | "has_email" | "opened_last_email" | "field_value";
/** condition with check=field_value: the lead field to check, like "Job Title" or "Source" (the campaign itself changes Status, Last touch and LinkedIn connection). Fixed once the campaign launches. */
conditionField?: string;
/** condition with check=field_value: is, is_not, contains, is_empty or is_not_empty (all case-insensitive). Fixed once the campaign launches. */
conditionOperator?: "is" | "is_not" | "is_empty" | "is_not_empty" | "contains";
/** condition with check=field_value and is, is_not or contains: the text to compare with. Editable after launch. */
conditionValue?: string;
}>;
/** linear: steps run in order. workflow: steps have keys and paths (next, yesNext, noNext) and may include condition and wait_for_connection. Left out, a workflow stays one and steps with keys, paths or those types make one; nothing turns a workflow back into a list. When replacing a workflow's steps, send workflow, so steps without keys or paths are checked here the way they will be saved. */
stepModel?: "linear" | "workflow";
/** Send {single braces} exactly as typed instead of rejecting them. Merge tokens always use {{double braces}}. */
allowSingleBraces?: boolean;
};
/** Edit the text, waits and conditions of a launched (or paused) campaign's steps in place. Each step keeps its id, type and place; pass the stepId and the updatedAt you read, and only the fields you change. A workflow keeps its paths and what each If / else checks (key, next, yesNext, noNext, check, conditionField, conditionOperator); conditionValue and acceptance waits stay editable. Messages already sent or being prepared keep their text; drafts use campaigns.steps.replace. — `dench campaign steps edit` — PATCH /api/v1/campaigns/{sequenceId}/steps */
"campaigns.steps.update": {
sequenceId: string;
/** The steps to change. Rejected, not trimmed: a field the step type would drop, an overlength note/message/InMail subject, out-of-range wait/withdrawal/post-age/acceptance values, {TOKEN} single braces, and a changed path or If / else check. */
steps: Array<{
/** The step's current type. A launched step's type can't change. */
type: "wait" | "email" | "linkedin_connection" | "linkedin_message" | "linkedin_visit" | "linkedin_like_post" | "linkedin_inmail" | "condition" | "wait_for_connection";
/** wait steps: hours to wait before the next step. */
waitHours?: number;
/** Legacy email step sender. Pool campaigns choose senders in emailMailboxAccountIds on the campaign, not on individual steps. */
emailMailboxAccountId?: string;
/** email steps: subject; {{Column}} merge tokens allowed. */
emailSubject?: string;
/** email steps: HTML body. Sent instead of emailTextBody when both are set, so when you edit the text, update or drop this too. */
emailHtmlBody?: string;
/** email steps: plain-text body; used as the email when there is no emailHtmlBody. */
emailTextBody?: string;
/** email steps: thread under the previous email step instead of a new subject. */
emailReplyToPrevious?: boolean;
/** email and linkedin_like_post only: always (default), connected or not_connected. InMail automatically skips connected people; do not set a condition on it. */
onlyIf?: "always" | "connected" | "not_connected";
/** Legacy LinkedIn step sender (also on wait_for_connection, and on a condition that checks linkedin_connected). Pool campaigns use the campaign's linkedinAccountIds instead. */
linkedinAccountId?: string;
/** linkedin_connection: the invite note (optional, up to 300 chars; sent without it if LinkedIn refuses the note); linkedin_message: the required message (up to 3000 chars); linkedin_inmail: the required message (up to 1900 chars). */
linkedinMessageBody?: string;
/** linkedin_message: what to do when the invite is still pending. */
linkedinMessageMode?: "skip_if_not_connected" | "wait_until_connected";
/** linkedin_message with linkedinMessageMode=wait_until_connected: wait 1-2160 hours for acceptance (default 720); after a connection step this mode is the default in a linear campaign. wait_for_connection: wait 24-2160 hours (1-90 days, default 720) before taking the Not accepted path. */
connectionTimeoutHours?: number;
/** linkedin_like_post only: the reaction to leave (default like). */
likeReaction?: "like" | "celebrate" | "support" | "love" | "insightful" | "funny";
/** linkedin_like_post only: select the newest eligible original post from the last 1-365 days (default 30). */
likeMaxPostAgeDays?: number;
/** linkedin_like_post only: before the immediately preceding wait starts, check for eligible posts. If none exist, skip that wait and the like step, keeping following waits. Defaults to false when omitted. */
likeSkipPreviousWait?: boolean;
/** linkedin_like_post only: skip consecutive following wait steps whenever the reaction step is skipped (default true). False keeps those waits. */
likeSkipNextWait?: boolean;
/** linkedin_like_post only: how many recent posts to react to, 1-3 (default 1). */
likeCount?: number;
/** linkedin_inmail only: optional subject, up to 200 characters; merge tokens allowed. Clear with an empty string. */
inmailSubject?: string;
/** linkedin_connection only: withdraw a still-pending invitation sent by this campaign after 168-2160 hours (7-90 days). Omitted means off on creation; null turns it off on edit. */
withdrawAfterHours?: number | null;
/** Workflow steps: this step's id, unique in the campaign, which next, yesNext and noNext point at (not "end"). Left out, one is made. */
key?: string;
/** Workflow steps: where a step that isn't a condition or wait_for_connection goes; left out, the following step: another step's key, or "end" to stop there. Fixed once the campaign launches. */
next?: string;
/** Workflow steps: condition and wait_for_connection only, required: where the Yes (Accepted) path goes: another step's key, or "end" to stop there. Fixed once the campaign launches. */
yesNext?: string;
/** Workflow steps: condition and wait_for_connection only, required: where the No (Not accepted) path goes: another step's key, or "end" to stop there. Fixed once the campaign launches. */
noNext?: string;
/** condition only, required: linkedin_connected, has_email, opened_last_email (needs an email before it on every path) or field_value. Fixed once the campaign launches. */
check?: "linkedin_connected" | "has_email" | "opened_last_email" | "field_value";
/** condition with check=field_value: the lead field to check, like "Job Title" or "Source" (the campaign itself changes Status, Last touch and LinkedIn connection). Fixed once the campaign launches. */
conditionField?: string;
/** condition with check=field_value: is, is_not, contains, is_empty or is_not_empty (all case-insensitive). Fixed once the campaign launches. */
conditionOperator?: "is" | "is_not" | "is_empty" | "is_not_empty" | "contains";
/** condition with check=field_value and is, is_not or contains: the text to compare with. Editable after launch. */
conditionValue?: string;
/** The step's _id from campaigns.get steps[]. */
stepId: string;
/** The step's updatedAt when you read it. If someone saved it since, the edit is refused instead of overwriting theirs. */
expectedUpdatedAt: number;
}>;
/** Send {single braces} exactly as typed instead of rejecting them. Merge tokens always use {{double braces}}. */
allowSingleBraces?: boolean;
};
/** Read a campaign's personalized lines (written for each person): their {{keys}} for steps, instructions and fallbacks, how many people have them, and the cost per person. — `dench campaign personalize get` — GET /api/v1/campaigns/{sequenceId}/personalization */
"campaigns.personalization.get": {
sequenceId: string;
};
/** Define up to 5 personalized lines (each used as {{key}} in steps) and what you sell. Saving starts writing missing lines for up to 100 people, about 3 cents a person; for more it returns needsConfirm and writes nothing until campaigns.personalization.write is confirmed. — `dench campaign personalize set` — PUT /api/v1/campaigns/{sequenceId}/personalization */
"campaigns.personalization.set": {
sequenceId: string;
/** The lines and context; null turns personalized lines off. */
template: {
variables: {
/** The {{key}} used in steps: a letter, then letters, digits or _ (up to 40). Not a built-in field like firstName or company. */
key: string;
/** The line's name, up to 60 characters. */
label: string;
/** What the writer should say, up to 600 characters. */
prompt: string;
/** Sent when there is nothing specific to say; up to 300 characters, no {{variables}}. */
fallback: string;
/** A field to use before the fallback, e.g. Company. */
fallbackFrom?: string;
/** The longest line, default 200 characters. */
maxChars?: number;
}[];
context: {
/** What you sell, up to 1000 characters. */
sells: string;
/** The offer or call to action, up to 300 characters. */
offer?: string;
};
} | null;
};
/** Turn off a campaign's personalized lines and cancel the ones not yet written. Refused while a step still uses one of the {{keys}}. — `dench campaign personalize clear` — DELETE /api/v1/campaigns/{sequenceId}/personalization */
"campaigns.personalization.clear": {
sequenceId: string;
};
/** Write personalized lines for the people missing them (mode missing) or rewrite everyone's (mode all). Rewriting everyone or more than 100 people returns needsConfirm and the estimate until called with confirmed: true. — `dench campaign personalize write` — POST /api/v1/campaigns/{sequenceId}/personalization/write */
"campaigns.personalization.write": {
sequenceId: string;
/** missing (default): only people without lines. all: rewrite everyone's. */
mode?: "missing" | "all";
/** true after the user accepts the estimate from needsConfirm. */
confirmed?: boolean;
};
/** Set one person's personalized line by hand; an empty value undoes the edit. Braces are removed and text past the line's length is cut. — `dench campaign personalize edit` — PUT /api/v1/campaigns/people/{enrollmentId}/personalization */
"campaigns.personalization.edit": {
/** From dench campaign people. */
enrollmentId: string;
/** The line's key, as in {{key}}. */
key: string;
/** The line to send; an empty string undoes the edit. */
value: string;
};
/** Write a fresh personalized line for one person: one key, or all of their lines when key is left out. About 3 cents. — `dench campaign personalize rewrite` — POST /api/v1/campaigns/people/{enrollmentId}/personalization/rewrite */
"campaigns.personalization.rewrite": {
/** From dench campaign people. */
enrollmentId: string;
/** One line's key; leave out to rewrite all of their lines. */
key?: string;
};
/** Add CRM records (up to 100 ids per call) to a campaign; returns how many were added and why others were skipped. — `dench campaign add` — POST /api/v1/campaigns/{sequenceId}/people */
"campaigns.people.addFromCrm": {
sequenceId: string;
/** CRM object the ids belong to, e.g. lead or people. */
objectName: string;
entryIds: string[];
/** Column holding the email when it is not "Email". */
emailFieldName?: string;
/** Column holding the LinkedIn profile URL. */
linkedinUrlFieldName?: string;
nameFieldName?: string;
/** Follow this relation to the person record first. */
relationFieldName?: string;
relationDirection?: "reverse";
/** Default true: skip people active in another campaign. */
checkDuplicatesAcrossWorkspace?: boolean;
};
/** Add one person by email and/or LinkedIn profile URL. — `dench campaign add --name` — POST /api/v1/campaigns/{sequenceId}/person */
"campaigns.people.add": {
sequenceId: string;
displayName?: string;
email?: string;
linkedinProfileUrl?: string;
/** Existing CRM people entry to link. */
entryId?: string;
sourceEntryId?: string;
sourceObjectName?: string;
emailIdentityId?: string;
linkedinIdentityId?: string;
linkedinProviderUserId?: string;
/** CRM object a new record is created in when none matches. */
newRecordsObject?: "lead" | "people";
personalization?: Record;
checkDuplicatesAcrossWorkspace?: boolean;
};
/** Show the message one person gets at one email or LinkedIn step, with their fields and personalized lines filled in: subject, body, sender, recipient, merge fields still missing, warnings, and why the step would be skipped. Nothing is sent, prepared or charged. — `dench campaign preview` — GET /api/v1/campaigns/{sequenceId}/preview */
"campaigns.preview": {
sequenceId: string;
/** The person to preview, from dench campaign people. */
enrollmentId: string;
/** An email or LinkedIn step, its _id from campaigns.get steps[]. A wait step sends nothing. */
stepId: string;
};
/** Launch readiness without launching: blockers grouped by problem, warnings, and the fingerprint `launch` requires. — `dench campaign launch --check` — GET /api/v1/campaigns/{sequenceId}/launch */
"campaigns.launch.preview": {
sequenceId: string;
};
/** Launch a ready draft. Pass the fingerprint from the preview and acknowledge the LinkedIn notice when LinkedIn steps exist. — `dench campaign launch` — POST /api/v1/campaigns/{sequenceId}/launch */
"campaigns.launch": {
sequenceId: string;
/** From the launch preview. */
fingerprint: string;
/** Required when the campaign has LinkedIn steps. */
linkedinRiskAcknowledged?: boolean;
};
/** Pause an active campaign. — `dench campaign pause` — POST /api/v1/campaigns/{sequenceId}/pause */
"campaigns.pause": {
sequenceId: string;
};
/** Resume a paused campaign. — `dench campaign resume` — POST /api/v1/campaigns/{sequenceId}/resume */
"campaigns.resume": {
sequenceId: string;
};
/** Archive a campaign and stop everyone still in flight. This is the app's Delete: campaigns are never erased, and their people, leads and activity stay. — `dench campaign archive` — POST /api/v1/campaigns/{sequenceId}/archive */
"campaigns.archive": {
sequenceId: string;
};
/** The campaign's activity log, newest first (sent, accepted, replied, skipped, failed, paused, stopped). — `dench campaign events` — GET /api/v1/campaigns/{sequenceId}/events */
"campaigns.events": {
sequenceId: string;
/** Page size, default 50. */
numItems?: number;
/** continueCursor from the previous page. */
cursor?: string;
};
/** Stop one person: nothing more is sent to them from this campaign. — `dench campaign stop` — POST /api/v1/campaigns/people/{enrollmentId}/stop */
"campaigns.people.stop": {
enrollmentId: string;
};
/** Resume one paused person in an active campaign. — `dench campaign resume-person` — POST /api/v1/campaigns/people/{enrollmentId}/resume */
"campaigns.people.resume": {
enrollmentId: string;
};
/** Give a person in a draft campaign a new or corrected email address; their linked CRM record gets it too. Only before launch; another person in the campaign can't share it. — `dench campaign person set --email` — PUT /api/v1/campaigns/people/{enrollmentId}/email */
"campaigns.people.setEmail": {
/** From dench campaign people. */
enrollmentId: string;
/** The address email steps should use. */
email: string;
};
/** Give a person in a draft campaign a new or corrected LinkedIn profile URL; their linked CRM record gets it too. Only before launch. — `dench campaign person set --linkedin-url` — PUT /api/v1/campaigns/people/{enrollmentId}/linkedin */
"campaigns.people.setLinkedin": {
/** From dench campaign people. */
enrollmentId: string;
/** A LinkedIn member profile URL, e.g. https://linkedin.com/in/ada. */
linkedinProfileUrl: string;
};
/** Fill or fix one {{merge field}} for a person in a draft campaign, e.g. a missing Company that blocks launch; a matching column on their CRM record is updated too. Only before launch; written personalized lines are not merge fields and can't be set here. — `dench campaign person set --field` — PUT /api/v1/campaigns/people/{enrollmentId}/fields */
"campaigns.people.setField": {
/** From dench campaign people. */
enrollmentId: string;
/** The merge token name without braces, e.g. Company for {{Company}} or fund for {{fund}}. */
key: string;
/** Up to 2000 characters. */
value: string;
};
/** Resume every paused person in an active campaign at once, e.g. after a sending account is reconnected. Returns how many were resumed. — `dench campaign resume-person --all` — POST /api/v1/campaigns/{sequenceId}/people/resume */
"campaigns.people.resumeAll": {
/** An active campaign. */
sequenceId: string;
};
/** Fill each unfinished person's empty email, LinkedIn URL, name and merge fields from their linked CRM record, e.g. after enriching the records. Never overwrites a value the campaign already has; returns how many people changed. — `dench campaign sync` — POST /api/v1/campaigns/{sequenceId}/people/sync */
"campaigns.people.sync": {
sequenceId: string;
};
/** The campaigns a CRM record (a person or a lead) is in, newest first, with the person's status in each and the accounts that send to them. — `dench campaign where` — GET /api/v1/campaigns/by-record/{entryId} */
"campaigns.people.forRecord": {
/** A CRM record's _id: a person, or a lead created from one. */
entryId: string;
};
/** Everything a campaign did for one person, step by step: what was sent (subject and text), whether it was opened or clicked, what was skipped and why, waits, failures and retries. — `dench campaign person show` — GET /api/v1/campaigns/people/{enrollmentId}/timeline */
"campaigns.people.timeline": {
/** From dench campaign people. */
enrollmentId: string;
};
/** Remove a person who was never contacted from a draft campaign. Frees their place in the 300-person cap and keeps the draft launchable; stop anyone already contacted instead. — `dench campaign remove` — DELETE /api/v1/campaigns/people/{enrollmentId} */
"campaigns.people.remove": {
/** From dench campaign people; the person must never have been contacted. */
enrollmentId: string;
};
/** List routines. — `dench cron list` — GET /api/v1/routines */
"cron.list": {
enabledOnly?: boolean;
};
/** List routine run history. — `dench cron history` — GET /api/v1/routines/history */
"cron.history": {
limit?: number;
};
/** Get a routine. — `dench cron get` — GET /api/v1/routines/{jobId} */
"cron.get": {
jobId: string;
};
/** Create a routine. — `dench cron create` — POST /api/v1/routines */
"cron.create": {
name: string;
/** Agent goal/message for each run. */
prompt: string;
/** Schedule: recurring (`every`), cron expression, or one-shot (`at`). */
schedule: {
kind: "every";
/** Interval in milliseconds (1s to 365d). */
everyMs: number;
/** Optional anchor timestamp for the recurring fire. */
anchorMs?: number;
} | {
kind: "cron";
/** 5-field POSIX cron expression. */
expr: string;
/** IANA timezone, e.g. America/New_York. */
tz?: string;
} | {
kind: "at";
/** ISO 8601 timestamp for a one-shot run. */
at: string;
};
description?: string;
/** What to do when a previous run is still active. Defaults to `skip`. */
overlapPolicy?: "skip" | "queue" | "kill_old";
enabled?: boolean;
deleteAfterRun?: boolean;
/** Routine run target. */
target?: "chat_turn";
};
/** Update a routine. — `dench cron update` — PATCH /api/v1/routines/{jobId} */
"cron.update": {
jobId: string;
name?: string;
prompt?: string;
/** Schedule: recurring (`every`), cron expression, or one-shot (`at`). */
schedule?: {
kind: "every";
/** Interval in milliseconds (1s to 365d). */
everyMs: number;
/** Optional anchor timestamp for the recurring fire. */
anchorMs?: number;
} | {
kind: "cron";
/** 5-field POSIX cron expression. */
expr: string;
/** IANA timezone, e.g. America/New_York. */
tz?: string;
} | {
kind: "at";
/** ISO 8601 timestamp for a one-shot run. */
at: string;
};
description?: string;
/** What to do when a previous run is still active. Defaults to `skip`. */
overlapPolicy?: "skip" | "queue" | "kill_old";
enabled?: boolean;
deleteAfterRun?: boolean;
/** Routine run target. */
target?: "chat_turn";
};
/** Delete a routine. — `dench cron delete` — DELETE /api/v1/routines/{jobId} */
"cron.delete": {
jobId: string;
};
/** Enable a routine. — `dench cron enable` — POST /api/v1/routines/{jobId}/enable */
"cron.enable": {
jobId: string;
};
/** Disable a routine. — `dench cron disable` — POST /api/v1/routines/{jobId}/disable */
"cron.disable": {
jobId: string;
};
/** Run a routine now. — `dench cron run` — POST /api/v1/routines/{jobId}/run */
"cron.run": {
jobId: string;
};
/** List runs for a routine. — `dench cron runs` — GET /api/v1/routines/{jobId}/runs */
"cron.runs": {
jobId: string;
limit?: number;
};
/** List workspace files. — `dench files ls` — GET /api/v1/files */
"files.list": {
/** Directory path, defaults to /. */
prefix?: string;
slug?: string;
};
/** Read a workspace file's text. — `dench files cat` — GET /api/v1/files/content */
"files.content": {
/** Workspace file path, e.g. /reports/q2.md. A leading /workspace/ mount prefix is stripped. */
path: string;
slug?: string;
};
/** Write a workspace file's text. — `dench files write` — PUT /api/v1/files/content */
"files.write": {
/** Workspace file path, e.g. /reports/q2.md. A leading /workspace/ mount prefix is stripped. */
path: string;
/** Full replacement text (UTF-8, <= 400 KB). */
content: string;
/** contentHash from the last read; the write is rejected with version_mismatch if the file changed since. */
expectedPreviousHash?: string;
};
/** Get a signed file download URL. — `dench files download` — GET /api/v1/files/download-url */
"files.downloadUrl": {
path: string;
};
/** Remove a workspace file or directory. — `dench files rm` — DELETE /api/v1/files */
"files.delete": {
path: string;
};
/** Move a workspace file or directory. — `dench files mv` — POST /api/v1/files/move */
"files.move": {
/** Source workspace path. */
from: string;
/** Destination workspace path. */
to: string;
};
/** Create a workspace directory (and any missing parents). Idempotent. — `dench files mkdir` — POST /api/v1/files/mkdir */
"files.mkdir": {
path: string;
};
/** Stage uploaded content into the workspace. — `dench stage` — POST /api/v1/files/stage */
"files.stage": {
/** Files to write. Each needs text or base64. */
files?: {
path: string;
text?: string;
base64?: string;
}[];
/** Single-file shortcut. */
path?: string;
text?: string;
base64?: string;
lastModifiedBy?: string;
};
/** Search the web through the Dench gateway. — `dench search` — POST /api/v1/search */
"search.web": {
/** Search query. */
query: string;
type?: "auto" | "neural" | "fast" | "deep" | "deep-reasoning" | "instant";
numResults?: number;
/** Entity categories return structured entities. Domain filters are unsupported for people; published-date filters are unsupported for people and company. */
category?: "company" | "research paper" | "news" | "personal site" | "financial report" | "people";
/** Unsupported for category=people. */
includeDomains?: string[];
/** Unsupported for category=people. */
excludeDomains?: string[];
/** Unsupported for category=people and category=company. */
startPublishedDate?: string;
/** Unsupported for category=people and category=company. */
endPublishedDate?: string;
includeText?: string[];
excludeText?: string[];
userLocation?: string;
moderation?: boolean;
};
/** Fetch page contents through the Dench gateway. — `dench search contents` — POST /api/v1/search/contents */
"search.contents": {
/** URLs to fetch (max 10). */
urls: string[];
text?: boolean;
/** Optional per-URL summary. */
summary?: {
query?: string;
};
};
/** Answer a question with web search. — `dench search answer` — POST /api/v1/search/answer */
"search.answer": {
/** Question to answer with web search. */
query: string;
text?: boolean;
stream?: boolean;
outputSchema?: Record;
};
/** Read a page as clean Markdown through Dench web. — `dench web scrape` — POST /api/v1/context/web-scrape-markdown */
"web.scrape": Record;
/** Look up a brand kit from a domain or email. — `dench web brand` — POST /api/v1/context/brand-retrieve */
"web.brand": Record;
/** List sitemap URLs for a domain. — `dench web sitemap` — POST /api/v1/context/web-sitemap */
"web.sitemap": Record;
/** Crawl a site to Markdown (capped). — `dench web crawl` — POST /api/v1/context/web-crawl */
"web.crawl": Record;
/** Capture a rendered page screenshot. — `dench web screenshot` — POST /api/v1/context/web-screenshot */
"web.screenshot": Record;
/** Extract typed fields from a page using a JSON Schema. — `dench web extract` — POST /api/v1/context/web-extract */
"web.extract": Record;
/** Extract a product listing from a URL. — `dench web product` — POST /api/v1/context/web-product */
"web.product": Record;
/** Find competitors of a domain. — `dench web competitors` — POST /api/v1/context/web-competitors */
"web.competitors": Record;
/** Read a brand styleguide from a domain. — `dench web styleguide` — POST /api/v1/context/brand-styleguide */
"web.styleguide": Record;
/** Read site fonts from a domain. — `dench web fonts` — POST /api/v1/context/brand-fonts */
"web.fonts": Record;
/** Classify a domain with NAICS or SIC. — `dench web classify` — POST /api/v1/context/classify-naics */
"web.classify": Record;
/** Autocomplete brand names. — `dench web brand-search` — POST /api/v1/context/brand-search */
"web.brandSearch": Record;
/** Generate an image through the Dench gateway. — `dench image generate` — POST /api/v1/images/generations */
"image.generate": {
/** Image prompt. */
prompt: string;
/** e.g. 1024x1024, 1536x1024, auto. */
size?: string;
quality?: "auto" | "low" | "medium" | "high";
format?: "png" | "jpeg" | "webp";
output_compression?: number;
background?: "auto" | "opaque" | "transparent";
/** Workspace path to write the image. */
savePath?: string;
};
/** Edit an image through the Dench gateway. — `dench image edit` — POST /api/v1/images/edits */
"image.edit": {
/** Image prompt. */
prompt: string;
/** e.g. 1024x1024, 1536x1024, auto. */
size?: string;
quality?: "auto" | "low" | "medium" | "high";
format?: "png" | "jpeg" | "webp";
output_compression?: number;
background?: "auto" | "opaque" | "transparent";
/** Workspace path to write the image. */
savePath?: string;
/** Base64-encoded source images (1-8). */
input_images_base64: string[];
/** Optional base64 mask image. */
mask_base64?: string;
};
/** List every connected integration — connected apps plus the Gmail, Outlook, Calendar, LinkedIn and Slack connections Dench provides itself (Slack rows report source: dench). — `dench integrations` — GET /api/v1/integrations */
"integrations.list": Record;
/** List installed App Store apps in the workspace. — `dench apps list` — GET /api/v1/apps */
"apps.list": Record;
/** List Discover listings — Official (Dench) + Community shelves. — `dench apps gallery` — GET /api/v1/apps/gallery */
"apps.gallery": Record;
/** Scaffold and register a new App Store app. — `dench apps create` — POST /api/v1/apps */
"apps.create": {
/** Folder slug; becomes /apps/.dench.app. */
slug: string;
name: string;
/** Lucide icon name. */
icon?: string;
description?: string;
};
/** Install an App Store listing into the workspace. — `dench apps install` — POST /api/v1/apps/install */
"apps.install": {
listingId: string;
};
/** Publish an app's files as a versioned App Store listing. — `dench apps publish` — POST /api/v1/apps/publish */
"apps.publish": {
/** Workspace path of the .dench.app folder. */
appPath: string;
slug: string;
name: string;
description?: string;
icon?: string;
visibility?: "org" | "public" | "official" | "unlisted";
version?: string;
permissions?: string[];
};
/** Uninstall an app from the workspace. — `dench apps uninstall` — DELETE /api/v1/apps/{slug} */
"apps.uninstall": {
slug: string;
};
/** Enable or disable an installed app. — `dench apps enable` — PATCH /api/v1/apps/{slug} */
"apps.setEnabled": {
slug: string;
enabled: boolean;
};
/** Pin or unpin an installed app in the rail. — `dench apps pin` — POST /api/v1/apps/{slug}/pin */
"apps.setPinned": {
slug: string;
pinned: boolean;
};
/** Register an inbound webhook for an app. Returns the URL and signing secret ONCE. — `dench apps webhooks register` — POST /api/v1/apps/{slug}/webhooks */
"apps.webhooks.register": {
/** Installed app slug. */
slug: string;
/** Hook name, e.g. `stripe` or `github-push`. */
hookName: string;
};
/** List an app's registered webhooks (URLs, never secrets). — `dench apps webhooks list` — GET /api/v1/apps/{slug}/webhooks */
"apps.webhooks.list": {
slug: string;
};
/** Read events an app webhook received since a timestamp. — `dench apps webhooks poll` — GET /api/v1/apps/{slug}/webhooks/{hookName}/events */
"apps.webhooks.poll": {
slug: string;
hookName: string;
/** Only events received after this epoch ms. */
since?: number;
limit?: number;
};
/** Remove an app webhook and every event it stored. — `dench apps webhooks delete` — DELETE /api/v1/apps/{slug}/webhooks/{hookName} */
"apps.webhooks.delete": {
slug: string;
hookName: string;
};
/** List connected tool accounts. — `dench tool status` — GET /api/v1/tools/status */
"tool.status": Record;
/** Create a tool OAuth connection link. Slack is in-house, never a third-party connection: for toolkit slack the CLI prints the first-party install URL (/api/slack/install?slug=, org admin) instead of calling this endpoint. — `dench tool connect` — POST /api/v1/tools/connections */
"tool.connect": {
/** Toolkit slug, e.g. github, gmail, stripe. */
toolkit?: string;
toolkit_slug?: string;
/** Optional OAuth callback URL. */
callback_url?: string;
/** Native OAuth return path within the workspace. */
returnTo?: string;
};
/** Search for external tool actions. — `dench tool search` — POST /api/v1/tools/search */
"tool.search": {
queries?: {
/** Natural-language use case. */
use_case: string;
known_fields?: string;
}[];
query?: string;
toolkit?: string;
toolkit_slug?: string;
limit?: number;
important?: boolean;
session?: {
id?: string;
generate_id?: boolean;
};
account?: string;
model?: string;
};
/** Run an external tool action. SLACK_* slugs (SLACK_SEND_MESSAGE with Block Kit blocks/buttons, SLACK_FETCH_HISTORY, SLACK_LIST_CHANNELS, …) run through Dench's in-house Slack app install — any channel in the connected workspace — and are always preferred over any third-party Slack connection. — `dench tool run` — POST /api/v1/tools/run */
"tool.run": {
/** Execution ref from a prior search, OR pass session_id+tool_slug. */
execution_ref?: string;
session_id?: string;
/** Tool slug, e.g. SLACK_LIST_CHANNELS. */
tool_slug?: string;
/** Tool arguments object. */
arguments?: Record;
account?: string;
connected_account_id?: string;
approvalId?: string;
};
/** Disconnect an external tool account. — `dench tool disconnect` — DELETE /api/v1/tools/connections/{connectionId} */
"tool.disconnect": {
connectionId: string;
};
/** List human names given to connected accounts. — `dench tool labels` — GET /api/v1/tools/labels */
"tool.labels.list": Record;
/** Name a connected account, or clear its name with an empty string. — `dench tool label` — PUT /api/v1/tools/connections/{connectionId}/label */
"tool.label.set": {
connectionId: string;
/** Human name for the account. Empty clears it. */
displayName: string;
};
/** Drive Dench Browser with a single interactive action (open, inspect, click, fill, type, screenshot, stop). Pass the step in `action` plus its arguments. — `dench browser` — POST /api/v1/browser/cli */
"browser.action": Record;
/** Create a Dench Browser session and return a CDP connect URL for Playwright (chromium.connectOverCDP) plus a live-view URL. Use for durable scripts run from the sandbox. — `dench browser session create` — POST /api/v1/browser/sessions */
"browser.session.create": Record;
/** Get the status, connect URL, and live-view URL for a Dench Browser session. — `dench browser session status` — GET /api/v1/browser/sessions/{sessionId} */
"browser.session.status": Record;
/** Stop a Dench Browser session and release the remote browser. — `dench browser session stop` — DELETE /api/v1/browser/sessions/{sessionId} */
"browser.session.stop": Record;
/** Read IDENTITY.md. — `dench context --sections identity` — GET /api/v1/agent-config/identity */
"agentConfig.identity.show": Record;
/** Update IDENTITY.md. — `dench context set identity` — PUT /api/v1/agent-config/identity */
"agentConfig.identity.edit": {
content: string;
};
/** Read ORGANISATION.md. — `dench context --sections organisation` — GET /api/v1/agent-config/organisation */
"agentConfig.organisation.show": Record;
/** Update ORGANISATION.md. — `dench context set organisation` — PUT /api/v1/agent-config/organisation */
"agentConfig.organisation.edit": {
content: string;
};
/** Read USER.md. — `dench context --sections user` — GET /api/v1/agent-config/user */
"agentConfig.user.show": Record;
/** Update USER.md. — `dench context set user` — PUT /api/v1/agent-config/user */
"agentConfig.user.edit": {
content: string;
};
/** Read tool notes. — `dench context --sections tools` — GET /api/v1/agent-config/tools */
"agentConfig.tools.show": Record;
/** Update tool notes. — `dench context set tools` — PUT /api/v1/agent-config/tools */
"agentConfig.tools.edit": {
content: string;
};
/** Update standing workspace rules; credentials belong in secrets and runnable setup in environments. — `dench context set rules` — PUT /api/v1/context/rules */
"workspace.context.rules.set": {
content: string;
};
/** Read aggregate memory. — `dench mem show` — GET /api/v1/agent-config/mem */
"agentConfig.mem.show": Record;
/** Update aggregate memory. — `dench mem set` — PUT /api/v1/agent-config/mem */
"agentConfig.mem.edit": {
content: string;
append?: boolean;
};
/** Read bootstrap state. — `dench bootstrap status` — GET /api/v1/agent-config/bootstrap */
"agentConfig.bootstrap.show": Record;
/** Complete bootstrap. — `dench bootstrap complete` — POST /api/v1/agent-config/bootstrap/complete */
"agentConfig.bootstrap.complete": {
completed?: boolean;
};
/** Update bootstrap template. — `dench bootstrap template` — PUT /api/v1/agent-config/bootstrap/template */
"agentConfig.bootstrap.template": {
content: string;
};
/** Set the default chat model. — `dench model default` — PUT /api/v1/agent-config/model/default */
"agentConfig.model.default": {
/** Model id, or null to clear. */
modelId: string | null;
};
/** Set a thread model override. — `dench model thread` — PUT /api/v1/agent-config/model/threads/{threadId} */
"agentConfig.model.thread": {
threadId: string;
modelId: string | null;
};
/** Read today's daily activity path. — `dench daily today` — GET /api/v1/agent-config/daily/today */
"agentConfig.daily.today": Record;
/** Read a daily activity path. — `dench daily show` — GET /api/v1/agent-config/daily/{date} */
"agentConfig.daily.show": Record;
/** List your notifications (inbox, unread, archived, or all). — `dench notifications list` — GET /api/v1/notifications */
"notifications.list": {
filter?: "inbox" | "unread" | "archived" | "all";
limit?: number;
};
/** How many unread notifications you have. — `dench notifications count` — GET /api/v1/notifications/unread-count */
"notifications.unreadCount": Record;
/** Mark a notification read (or unread). — `dench notifications read` — PATCH /api/v1/notifications/{notificationId} */
"notifications.read": {
notificationId: string;
/** Defaults to true; false marks unread. */
read?: boolean;
};
/** Mark every inbox notification read. — `dench notifications read-all` — POST /api/v1/notifications/read-all */
"notifications.readAll": Record;
/** Archive a notification. — `dench notifications archive` — POST /api/v1/notifications/{notificationId}/archive */
"notifications.archive": {
notificationId: string;
};
/** Read your notification preferences. — `dench notifications prefs` — GET /api/v1/notifications/preferences */
"notifications.preferences.get": Record;
/** Update your notification preferences. — `dench notifications prefs --set` — PATCH /api/v1/notifications/preferences */
"notifications.preferences.update": {
inAppEnabled?: boolean;
emailEnabled?: boolean;
perKind?: Record;
timezone?: string;
digestWindowMinutes?: number;
maxEmailsPerDay?: number;
};
/** Read the workspace's Sync switches (accounts from email, contacts from LinkedIn, Slack Connect, calendar, dossier, tasks from email, lead auto-qualify, summaries and Brief). All on by default. — `dench settings sync` — GET /api/v1/settings/sync */
"settings.sync.get": Record;
/** Flip Sync switches (admin). Partial patch: only the keys sent change. Off stops new work; nothing already created is removed. — `dench settings sync set` — PATCH /api/v1/settings/sync */
"settings.sync.update": {
/** Accounts from email — new prospects and customers who email you become accounts and contacts. */
detectAccounts?: boolean;
/** Contacts from LinkedIn — everyone you message on LinkedIn becomes a contact, with their company. */
linkedinContacts?: boolean;
/** Accounts from Slack Connect — a shared channel binds to the company on the other side. */
slackConnectAccounts?: boolean;
/** Contacts from calendar — external invitees on upcoming meetings become contacts. */
calendarContacts?: boolean;
/** Research dossier — Dench researches each new account into its Notes. */
dossier?: boolean;
/** Tasks from email — replies and follow-ups you owe become tasks; support issues become cases. */
emailTasks?: boolean;
/** Qualify leads that book — a lead who books a meeting moves to Qualified and converts. */
leadAutoQualify?: boolean;
/** Summaries and Brief — conversations are summarised and each record keeps a Brief. */
intelligence?: boolean;
};
}
export interface ApiResponses {
"environments.list": unknown;
"environments.get": unknown;
"environments.upload": unknown;
"environments.save": unknown;
"environments.delete": unknown;
"secrets.list": unknown;
"secrets.set": {
name: string;
kind: "env" | "file";
version: number;
revision: number;
status: "stored";
};
"secrets.delete": unknown;
"campaigns.drafts.create": {
sequenceId: string;
references?: Array<{
kind: "crm-entry";
objectName: string;
entryId: string;
label: string;
href: string;
url?: string;
imageUrl?: string;
objectIcon?: string;
} | {
kind: "crm-object";
objectName: string;
label: string;
href: string;
url?: string;
imageUrl?: string;
objectIcon?: string;
} | {
kind: "campaign";
sequenceId: string;
label: string;
href: string;
url?: string;
imageUrl?: string;
objectIcon?: string;
}>;
referenceCoverage?: {
returned: number;
available: number;
truncated: boolean;
};
};
"campaigns.audience.search": {
rows: Record[];
people: unknown[];
recipients: {
email?: string;
linkedinProfileUrl?: string;
displayName?: string;
/** Merge values keyed by name, e.g. {"Company":"Acme"} for {{Company}}. */
personalization?: Record;
}[];
metadata: Record;
/** The search cost ceiling reported by Dench billing, in cents. Null means the receipt is unavailable. */
estimatedCents: number | null;
/** The actual search charge reported by Dench billing, in cents. Null means the receipt is unavailable; results remain valid. */
chargedCents: number | null;
};
"campaigns.people.addFromSearch": {
rows: Record[];
people: unknown[];
recipients: {
email?: string;
linkedinProfileUrl?: string;
displayName?: string;
/** Merge values keyed by name, e.g. {"Company":"Acme"} for {{Company}}. */
personalization?: Record;
}[];
metadata: Record;
/** The search cost ceiling reported by Dench billing, in cents. Null means the receipt is unavailable. */
estimatedCents: number | null;
/** The actual search charge reported by Dench billing, in cents. Null means the receipt is unavailable; results remain valid. */
chargedCents: number | null;
added: number;
requested: number;
cappedAtLimit: number | null;
duplicates: number;
duplicatesElsewhere: number;
suppressed: number;
invalid: number;
skippedNoIdentity: number;
skippedMissingInfo: number;
missingInfoSample?: unknown;
found: number;
skippedSearchProfiles: number;
};
"campaigns.people.addBatch": {
added: number;
requested: number;
cappedAtLimit: number | null;
duplicates: number;
duplicatesElsewhere: number;
suppressed: number;
invalid: number;
skippedNoIdentity: number;
skippedMissingInfo: number;
missingInfoSample?: unknown;
};
"campaigns.sources.get": Record | null;
"campaigns.sources.signals": Record | null;
"campaigns.sources.update": null;
"campaigns.sources.preview": {
ok: boolean;
[key: string]: unknown;
};
"campaigns.sources.run": null;
"campaigns.sources.overview": Record;
"campaigns.sources.pending": Record[];
"campaigns.sources.review": Record;
"campaigns.queue": Record | null;
"campaigns.builder.profile": {
hasProfile: boolean;
targeting: {
titles: string[];
locations: string[];
companySizes: string[];
industries: string[];
excludes: string[];
companies?: string[];
companyTypes?: string[];
};
[key: string]: unknown;
};
"campaigns.builder.save": {
draftId: string;
};
"campaigns.builder.get": {
status: string;
targeting: {
titles: string[];
locations: string[];
companySizes: string[];
industries: string[];
excludes: string[];
companies?: string[];
companyTypes?: string[];
};
signals: Array<"post_engagement" | "keyword" | "hiring" | "funding" | "launch" | "new_leader" | "voice_engagement" | "company_engagement" | "brand_engagement" | "lookalike">;
people: Record[];
[key: string]: unknown;
};
"campaigns.builder.build": {
ok: boolean;
started: boolean;
};
"campaigns.builder.create": {
sequenceId: string;
enrolled: number;
};
"campaigns.builder.opener": null;
"campaigns.builder.drafts": Record[];
"campaigns.builder.resume": {
clientRequestId: string;
status: string;
targeting: {
titles: string[];
locations: string[];
companySizes: string[];
industries: string[];
excludes: string[];
companies?: string[];
companyTypes?: string[];
};
signals: Array<"post_engagement" | "keyword" | "hiring" | "funding" | "launch" | "new_leader" | "voice_engagement" | "company_engagement" | "brand_engagement" | "lookalike">;
[key: string]: unknown;
} | null;
"campaigns.builder.discard": null;
"campaigns.builder.outreach": null;
"campaigns.builder.sequence": Record;
"campaigns.builder.lookalike": Record;
"campaigns.builder.voices": Record;
"campaigns.builder.autofill": {
targeting: {
titles: string[];
locations: string[];
companySizes: string[];
industries: string[];
excludes: string[];
companies?: string[];
companyTypes?: string[];
};
[key: string]: unknown;
};
"campaigns.builder.estimate": {
count: number | null;
[key: string]: unknown;
};
"commands.list": {
operations: Array<{
id: string;
group: string;
summary: string;
cli: string;
aliases: string[];
method: string;
path: string;
backend: "api" | "gateway" | "custom" | "local";
deprecated: boolean;
}>;
};
"auth.signin.create": {
status: "pending";
/** Epoch ms when the sign-in code expires. */
expiresAt: number;
};
"auth.signin.poll": {
status: "pending" | "approved" | "rejected" | "expired";
/** Present once approved. */
organization?: {
id: string;
name: string;
slug: string;
};
agent?: {
id: string;
name: string;
kind: string;
};
sessionExpiresAt?: number;
};
"auth.otp.start": {
ok: boolean;
};
"auth.otp.verify": {
ok: boolean;
result: {
kind: "completed";
organization?: {
id: string;
name: string;
slug: string;
};
sessionExpiresAt?: number;
} | {
kind: "requiresOrgChoice";
/** Pass to /auth/otp/finalize with the chosen org. */
authToken: string;
organizations: {
id: string;
name: string;
slug: string;
}[];
expiresAt: number;
};
};
"auth.otp.finalize": {
ok: boolean;
result: {
organization?: {
id: string;
name: string;
slug: string;
};
sessionExpiresAt?: number;
};
};
"workspace.context.sections": Record;
"workspace.context": {
workspace: {
id: string;
name: string;
slug?: string;
};
agent: {
id: string;
name: string;
kind: string;
};
rules: Array<{
id: string;
title: string;
body: string;
scope: "organization" | "project";
}>;
activeAgents: {
id: string;
name: string;
kind: string;
lastSeenAt?: number;
}[];
pendingApprovals: Array<{
id: string;
title: string;
reason: string;
kind?: string;
/** Risk level. */
risk: "low" | "medium" | "high";
agentId?: string;
createdAt: number;
[key: string]: unknown;
}>;
recentArtifacts: Array<{
id: string;
runId: string;
title: string;
type: "note" | "draft" | "report" | "task_suggestion" | "patch" | "other";
status: "draft" | "approved" | "rejected" | "applied" | "archived";
content: string;
createdAt: number;
updatedAt: number;
}>;
suggestedWork: Array<{
id: string;
runId: string;
title: string;
type: "note" | "draft" | "report" | "task_suggestion" | "patch" | "other";
status: "draft" | "approved" | "rejected" | "applied" | "archived";
content: string;
createdAt: number;
updatedAt: number;
nextCommands: string[];
}>;
memory: Array<{
id: string;
key: string;
/** Memory classification. */
kind: "fact" | "decision" | "preference" | "goal" | "tool_note" | "other" | "convention" | "project_context" | "agent_lesson" | "docs_knowledge" | "open_goal";
text: string;
tags: string[];
status: "active" | "needs_review" | "archived";
updatedAt: number;
}>;
chatAgents: {
nextCommands: string[];
activeRuns: {
id: string;
goal: string;
status: string;
model: string;
createdAt: number;
[key: string]: unknown;
}[];
templates: string[];
};
tools: {
discovery: string[];
cli: string[];
approvalPolicy: string;
};
nextActions: string[];
};
"workspace.instructions": {
/** Markdown standing instructions for external agents connected to this workspace. */
instructions: string;
workspace: {
name: string;
slug?: string;
};
};
"workspace.status": {
workspace: {
id: string;
name: string;
slug?: string;
};
agent: {
id: string;
name: string;
kind: string;
};
rules: Array<{
id: string;
title: string;
body: string;
scope: "organization" | "project";
}>;
activeAgents: {
id: string;
name: string;
kind: string;
lastSeenAt?: number;
}[];
pendingApprovals: Array<{
id: string;
title: string;
reason: string;
kind?: string;
/** Risk level. */
risk: "low" | "medium" | "high";
agentId?: string;
createdAt: number;
[key: string]: unknown;
}>;
recentArtifacts: Array<{
id: string;
runId: string;
title: string;
type: "note" | "draft" | "report" | "task_suggestion" | "patch" | "other";
status: "draft" | "approved" | "rejected" | "applied" | "archived";
content: string;
createdAt: number;
updatedAt: number;
}>;
suggestedWork: Array<{
id: string;
runId: string;
title: string;
type: "note" | "draft" | "report" | "task_suggestion" | "patch" | "other";
status: "draft" | "approved" | "rejected" | "applied" | "archived";
content: string;
createdAt: number;
updatedAt: number;
nextCommands: string[];
}>;
memory: Array<{
id: string;
key: string;
/** Memory classification. */
kind: "fact" | "decision" | "preference" | "goal" | "tool_note" | "other" | "convention" | "project_context" | "agent_lesson" | "docs_knowledge" | "open_goal";
text: string;
tags: string[];
status: "active" | "needs_review" | "archived";
updatedAt: number;
}>;
chatAgents: {
nextCommands: string[];
activeRuns: {
id: string;
goal: string;
status: string;
model: string;
createdAt: number;
[key: string]: unknown;
}[];
templates: string[];
};
tools: {
discovery: string[];
cli: string[];
approvalPolicy: string;
};
nextActions: string[];
};
"workspace.agents": {
workspace: {
id: string;
name: string;
slug?: string;
};
agent: {
id: string;
name: string;
kind: string;
};
rules: Array<{
id: string;
title: string;
body: string;
scope: "organization" | "project";
}>;
activeAgents: {
id: string;
name: string;
kind: string;
lastSeenAt?: number;
}[];
pendingApprovals: Array<{
id: string;
title: string;
reason: string;
kind?: string;
/** Risk level. */
risk: "low" | "medium" | "high";
agentId?: string;
createdAt: number;
[key: string]: unknown;
}>;
recentArtifacts: Array<{
id: string;
runId: string;
title: string;
type: "note" | "draft" | "report" | "task_suggestion" | "patch" | "other";
status: "draft" | "approved" | "rejected" | "applied" | "archived";
content: string;
createdAt: number;
updatedAt: number;
}>;
suggestedWork: Array<{
id: string;
runId: string;
title: string;
type: "note" | "draft" | "report" | "task_suggestion" | "patch" | "other";
status: "draft" | "approved" | "rejected" | "applied" | "archived";
content: string;
createdAt: number;
updatedAt: number;
nextCommands: string[];
}>;
memory: Array<{
id: string;
key: string;
/** Memory classification. */
kind: "fact" | "decision" | "preference" | "goal" | "tool_note" | "other" | "convention" | "project_context" | "agent_lesson" | "docs_knowledge" | "open_goal";
text: string;
tags: string[];
status: "active" | "needs_review" | "archived";
updatedAt: number;
}>;
chatAgents: {
nextCommands: string[];
activeRuns: {
id: string;
goal: string;
status: string;
model: string;
createdAt: number;
[key: string]: unknown;
}[];
templates: string[];
};
tools: {
discovery: string[];
cli: string[];
approvalPolicy: string;
};
nextActions: string[];
};
"workspace.approvals.list": {
workspace: {
id: string;
name: string;
slug?: string;
};
agent: {
id: string;
name: string;
kind: string;
};
rules: Array<{
id: string;
title: string;
body: string;
scope: "organization" | "project";
}>;
activeAgents: {
id: string;
name: string;
kind: string;
lastSeenAt?: number;
}[];
pendingApprovals: Array<{
id: string;
title: string;
reason: string;
kind?: string;
/** Risk level. */
risk: "low" | "medium" | "high";
agentId?: string;
createdAt: number;
[key: string]: unknown;
}>;
recentArtifacts: Array<{
id: string;
runId: string;
title: string;
type: "note" | "draft" | "report" | "task_suggestion" | "patch" | "other";
status: "draft" | "approved" | "rejected" | "applied" | "archived";
content: string;
createdAt: number;
updatedAt: number;
}>;
suggestedWork: Array<{
id: string;
runId: string;
title: string;
type: "note" | "draft" | "report" | "task_suggestion" | "patch" | "other";
status: "draft" | "approved" | "rejected" | "applied" | "archived";
content: string;
createdAt: number;
updatedAt: number;
nextCommands: string[];
}>;
memory: Array<{
id: string;
key: string;
/** Memory classification. */
kind: "fact" | "decision" | "preference" | "goal" | "tool_note" | "other" | "convention" | "project_context" | "agent_lesson" | "docs_knowledge" | "open_goal";
text: string;
tags: string[];
status: "active" | "needs_review" | "archived";
updatedAt: number;
}>;
chatAgents: {
nextCommands: string[];
activeRuns: {
id: string;
goal: string;
status: string;
model: string;
createdAt: number;
[key: string]: unknown;
}[];
templates: string[];
};
tools: {
discovery: string[];
cli: string[];
approvalPolicy: string;
};
nextActions: string[];
};
"workspace.artifacts.list": Array<{
id: string;
runId: string;
title: string;
type: "note" | "draft" | "report" | "task_suggestion" | "patch" | "other";
status: "draft" | "approved" | "rejected" | "applied" | "archived";
content: string;
createdAt: number;
updatedAt: number;
}>;
"workspace.suggestedWork.list": Array<{
id: string;
runId: string;
title: string;
type: "note" | "draft" | "report" | "task_suggestion" | "patch" | "other";
status: "draft" | "approved" | "rejected" | "applied" | "archived";
content: string;
createdAt: number;
updatedAt: number;
}>;
"lists.list": unknown;
"lists.rows": unknown;
"lists.enrich": unknown;
"lists.import": unknown;
"lists.get": unknown;
"lists.save": {
artifactId: string;
filePath: string;
rowCount: number;
previewRows: unknown[];
title: string;
createdFrom: string;
};
"lists.update": {
artifactId: string;
rowCount: number;
previewRows: unknown[];
title: string;
};
"lists.rename": unknown;
"lists.delete": unknown;
"lists.prospects.reviews": unknown;
"lists.prospects.assess": unknown;
"memory.search": Array<{
_id: string;
_creationTime: number;
key: string;
/** Memory classification. */
kind: "fact" | "decision" | "preference" | "goal" | "tool_note" | "other" | "convention" | "project_context" | "agent_lesson" | "docs_knowledge" | "open_goal";
text: string;
tags: string[];
status: "active" | "needs_review" | "archived";
source: string;
/** Who can see the memory. Defaults to `user`. */
visibility?: "user" | "org";
confidence?: number;
importance?: number;
createdAt: number;
updatedAt: number;
[key: string]: unknown;
}>;
"memory.save": {
ok: boolean;
memoryId: string;
status: "active" | "needs_review" | "archived";
};
"approval.request": {
ok: boolean;
approvalId: string;
};
"approval.decide": {
ok: true;
/** Human decision for an approval request. */
status: "approved" | "rejected";
} | {
ok: false;
/** e.g. approval_not_pending. */
code: string;
error: string;
};
"billing.status": {
workspace: {
id: string;
name: string;
slug: string;
};
availableCreditsCents: number;
creditConsumedCents: number;
billedCents: number;
onDemandSpendCents: number;
monthlySpendLimitCents: number;
spendLimitRemainingCents: number;
subscriptionStatus: string | null;
stripeCustomerId: string | null;
grants: Array<{
id: string;
source: "starter" | "trial" | "monthly" | "topup" | "manual" | "referral";
category: "promotional" | "paid";
status: "pending" | "granted" | "depleted" | "expired" | "voided";
amountCents: number;
usedCents: number;
remainingCents: number;
effectiveAt: number;
expiresAt: number | null;
}>;
nextCommands: string[];
[key: string]: unknown;
};
"billing.topup": {
charged: true;
amountCents: number;
} | {
url: string | null;
};
"billing.upgrade": {
updated: true;
tier: string;
billingCycle?: string;
stripeSubscriptionId: string;
} | {
url: string | null;
};
"chat.list": Array<{
threadId: string;
title: string;
lastMessageAt: number;
createdAt: number;
status: "idle" | "streaming" | "error";
visibility: "private" | "shared";
createdByUserId: string;
archivedAt: number | null;
fileContextPath: string | null;
/** Parent thread when this is a spawned sub-agent. */
parentThreadId: string | null;
nestingDepth: number | null;
summary: string | null;
activeRunId: string | null;
}>;
"chat.search": Array<{
threadId: string;
threadTitle: string;
messageId: string;
role: "user" | "assistant" | "system";
snippet: string;
createdAt: number;
runId: string | null;
}>;
"chat.read": {
thread: {
threadId: string;
title: string;
lastMessageAt: number;
createdAt: number;
status: "idle" | "streaming" | "error";
visibility: "private" | "shared";
createdByUserId: string;
archivedAt: number | null;
fileContextPath: string | null;
/** Parent thread when this is a spawned sub-agent. */
parentThreadId: string | null;
nestingDepth: number | null;
summary: string | null;
activeRunId: string | null;
};
messages: Array<{
id: string;
role: "user" | "assistant" | "system";
createdAt: number;
updatedAt: number;
text: string;
runId: string | null;
status: "streaming" | "complete" | "error" | "canceled" | null;
revision: number;
isDraft: boolean;
}>;
};
"chat.tree": Array<{
threadId: string;
title: string;
lastMessageAt: number;
createdAt: number;
status: "idle" | "streaming" | "error";
visibility: "private" | "shared";
createdByUserId: string;
archivedAt: number | null;
fileContextPath: string | null;
/** Parent thread when this is a spawned sub-agent. */
parentThreadId: string | null;
nestingDepth: number | null;
summary: string | null;
activeRunId: string | null;
}>;
"chat.rename": {
results: {
threadId: string;
ok: boolean;
error?: string;
}[];
succeeded: number;
failed: number;
};
"chat.delete": {
scheduled: number;
rejected: {
threadId: string;
/** e.g. automated_thread_protected. */
reason: string;
message?: string;
}[];
total: number;
};
"chat.new": {
threadId: string;
runId: string;
workflowRunId: string | null;
/** Web URL of the thread. */
threadUrl: string;
/** SSE stream URL for the live response. */
streamUrl: string;
yolo: boolean;
};
"chat.send": {
threadId: string;
runId: string;
workflowRunId: string | null;
/** Web URL of the thread. */
threadUrl: string;
/** SSE stream URL for the live response. */
streamUrl: string;
yolo: boolean;
};
"chat.follow": string;
"crm.objects.list": Array<{
_id: string;
_creationTime: number;
organizationId: string;
name: string;
description?: string;
/** Default view rendered for the object. */
defaultView: "table" | "kanban" | "calendar" | "timeline" | "gallery" | "list";
sortOrder: number;
immutable: boolean;
/** Who can read the object's records. Always reported as the value the object behaves as: `meeting` defaults to owner_plus_members, `calendar_event` to owner_only, anything else to workspace. */
visibility?: "workspace" | "owner_only" | "owner_plus_members";
icon?: string;
viewSettings?: Record;
savedViews?: Record[];
entryCount?: number;
createdAt: number;
updatedAt: number;
[key: string]: unknown;
}> | {
data: Array<{
_id: string;
_creationTime: number;
organizationId: string;
name: string;
description?: string;
/** Default view rendered for the object. */
defaultView: "table" | "kanban" | "calendar" | "timeline" | "gallery" | "list";
sortOrder: number;
immutable: boolean;
/** Who can read the object's records. Always reported as the value the object behaves as: `meeting` defaults to owner_plus_members, `calendar_event` to owner_only, anything else to workspace. */
visibility?: "workspace" | "owner_only" | "owner_plus_members";
icon?: string;
viewSettings?: Record;
savedViews?: Record[];
entryCount?: number;
createdAt: number;
updatedAt: number;
[key: string]: unknown;
}>;
references: Array<{
kind: "crm-entry";
objectName: string;
entryId: string;
label: string;
href: string;
url?: string;
imageUrl?: string;
objectIcon?: string;
} | {
kind: "crm-object";
objectName: string;
label: string;
href: string;
url?: string;
imageUrl?: string;
objectIcon?: string;
} | {
kind: "campaign";
sequenceId: string;
label: string;
href: string;
url?: string;
imageUrl?: string;
objectIcon?: string;
}>;
referenceCoverage: {
returned: number;
available: number;
truncated: boolean;
};
};
"crm.objects.listCompact": Array<{
_id: string;
name: string;
displayName: string;
description?: string;
entryCount: number | null;
countScope: "workspace" | "private_unknown";
visibility: "workspace" | "owner_only" | "owner_plus_members";
archived: boolean;
}>;
"crm.objects.get": {
_id: string;
_creationTime: number;
organizationId: string;
name: string;
description?: string;
/** Default view rendered for the object. */
defaultView: "table" | "kanban" | "calendar" | "timeline" | "gallery" | "list";
sortOrder: number;
immutable: boolean;
/** Who can read the object's records. Always reported as the value the object behaves as: `meeting` defaults to owner_plus_members, `calendar_event` to owner_only, anything else to workspace. */
visibility?: "workspace" | "owner_only" | "owner_plus_members";
icon?: string;
viewSettings?: Record;
savedViews?: Record[];
entryCount?: number;
createdAt: number;
updatedAt: number;
references?: Array<{
kind: "crm-entry";
objectName: string;
entryId: string;
label: string;
href: string;
url?: string;
imageUrl?: string;
objectIcon?: string;
} | {
kind: "crm-object";
objectName: string;
label: string;
href: string;
url?: string;
imageUrl?: string;
objectIcon?: string;
} | {
kind: "campaign";
sequenceId: string;
label: string;
href: string;
url?: string;
imageUrl?: string;
objectIcon?: string;
}>;
referenceCoverage?: {
returned: number;
available: number;
truncated: boolean;
};
[key: string]: unknown;
} | null;
"crm.objects.create": {
id: string;
/** The fields created from `fields`, in order. */
fields?: {
id: string;
name: string;
}[];
references?: Array<{
kind: "crm-entry";
objectName: string;
entryId: string;
label: string;
href: string;
url?: string;
imageUrl?: string;
objectIcon?: string;
} | {
kind: "crm-object";
objectName: string;
label: string;
href: string;
url?: string;
imageUrl?: string;
objectIcon?: string;
} | {
kind: "campaign";
sequenceId: string;
label: string;
href: string;
url?: string;
imageUrl?: string;
objectIcon?: string;
}>;
referenceCoverage?: {
returned: number;
available: number;
truncated: boolean;
};
};
"crm.objects.update": {
ok: boolean;
};
"crm.objects.rename": {
ok: boolean;
};
"crm.objects.delete": {
ok: boolean;
};
"crm.objects.archive": {
ok: boolean;
};
"crm.objects.unarchive": {
ok: boolean;
};
"crm.views.list": {
objectName: string;
activeView: string | null;
views: Record[];
};
"crm.views.get": Record;
"crm.views.entries": {
/** Materialized rows: id, entry_id, created_at, updated_at plus one key per field. */
entries: Record[];
totalCount: number;
page: number;
pageSize: number;
/** True when the read hit a server budget and rows may be missing. */
truncated: boolean;
/** The personal assignee scope that was applied, or null. */
assigneeScope: {
/** The user field the scope keyed on. */
field: string;
target: {
kind: "user";
userId: string;
} | {
kind: "unassigned";
};
} | null;
[key: string]: unknown;
};
"crm.views.create": {
ok: boolean;
objectName: string;
viewName: string;
replaced: boolean;
activeView: string | null;
};
"crm.views.update": {
ok: boolean;
objectName: string;
viewName: string;
replaced: boolean;
activeView: string | null;
};
"crm.views.rename": unknown;
"crm.views.delete": {
ok: boolean;
objectName: string;
viewName: string;
removed: boolean;
};
"crm.views.archive": {
ok: boolean;
};
"crm.views.unarchive": {
ok: boolean;
};
"crm.sidebar.list": {
visible: Record[];
hidden: Record[];
};
"crm.sidebar.move": {
ok: boolean;
navId: string;
position: number | null;
visibleOrder?: string[];
error?: string;
};
"crm.sidebar.groups.create": unknown;
"crm.sidebar.groups.rename": unknown;
"crm.sidebar.groups.delete": unknown;
"crm.sidebar.groups.move-item": unknown;
"crm.sidebar.pins.set": unknown;
"crm.fields.list": Array<{
_id: string;
_creationTime: number;
organizationId: string;
objectId: string;
name: string;
description?: string;
/** Field data type. `relation` needs relatedObjectName + relationshipType; `enum` uses enumValues/enumColors; `user` cells store one member userId or an array of member userIds; long prose belongs in the protected `Notes` richtext field. */
type: "text" | "email" | "phone" | "url" | "number" | "boolean" | "date" | "richtext" | "file" | "user" | "enum" | "relation" | "tags" | "action";
required: boolean;
defaultValue?: string;
/** Read-only campaign activity boolean on people or lead (through Contact), not an expression. Cannot be required, indexed, enriched or have a default value. */
calculation?: {
kind: "campaign_activity";
metric: "email_opened" | "email_clicked" | "replied" | "linkedin_connected";
/** Same-workspace multichannel campaign ID readable by the viewer. Omit for any accessible multichannel campaign. */
campaignId?: string;
};
relatedObjectId?: string;
/** Relation cardinality for a `relation` field. */
relationshipType?: "many_to_one" | "many_to_many";
enumValues?: string[];
enumColors?: string[];
enumMultiple?: boolean;
sortOrder: number;
indexed?: boolean;
createdAt: number;
updatedAt: number;
[key: string]: unknown;
}>;
"crm.fields.listCompact": {
_id: string;
name: string;
type: string;
required: boolean;
readOnly: boolean;
defaultValue?: string;
enumValues?: string[];
enumMultiple: boolean;
relatedObjectId?: string;
relationshipType?: string;
}[];
"crm.fields.create": {
id: string;
};
"crm.fields.update": {
ok: boolean;
};
"crm.fields.delete": {
ok: boolean;
};
"crm.fields.reorder": {
ok: boolean;
};
"crm.fields.enum.reorder": {
ok: boolean;
enumValues: string[];
enumColors: string[] | null;
};
"crm.fields.enum.add": {
ok: boolean;
enumValues: string[];
enumColors: string[];
inserted: boolean;
};
"crm.fields.enum.remove": {
ok: boolean;
enumValues: string[];
enumColors: string[] | null;
removed: boolean;
};
"crm.fields.enum.rename": {
ok: boolean;
enumValues: string[];
enumColors: string[];
entriesUpdated: number;
};
"crm.fields.enum.color": {
ok: boolean;
enumColors: string[];
};
"crm.entries.list": {
id: string;
objectId: string;
/** Field-name -> cell value map. */
fields: Record;
createdAt: number;
updatedAt: number;
updatedBy: string;
sortOrder: number;
}[] | {
data: {
id: string;
objectId: string;
/** Field-name -> cell value map. */
fields: Record;
createdAt: number;
updatedAt: number;
updatedBy: string;
sortOrder: number;
}[];
references: Array<{
kind: "crm-entry";
objectName: string;
entryId: string;
label: string;
href: string;
url?: string;
imageUrl?: string;
objectIcon?: string;
} | {
kind: "crm-object";
objectName: string;
label: string;
href: string;
url?: string;
imageUrl?: string;
objectIcon?: string;
} | {
kind: "campaign";
sequenceId: string;
label: string;
href: string;
url?: string;
imageUrl?: string;
objectIcon?: string;
}>;
referenceCoverage: {
returned: number;
available: number;
truncated: boolean;
};
};
"crm.entries.get": {
id: string;
objectId: string;
/** Field-name -> cell value map. */
fields: Record;
createdAt: number;
updatedAt: number;
updatedBy: string;
sortOrder: number;
references?: Array<{
kind: "crm-entry";
objectName: string;
entryId: string;
label: string;
href: string;
url?: string;
imageUrl?: string;
objectIcon?: string;
} | {
kind: "crm-object";
objectName: string;
label: string;
href: string;
url?: string;
imageUrl?: string;
objectIcon?: string;
} | {
kind: "campaign";
sequenceId: string;
label: string;
href: string;
url?: string;
imageUrl?: string;
objectIcon?: string;
}>;
referenceCoverage?: {
returned: number;
available: number;
truncated: boolean;
};
} | null;
"crm.entries.syncHistory": unknown;
"crm.entries.syncHistoryStatus": unknown;
"crm.entries.dossier.get": unknown;
"crm.entries.dossier.regenerate": unknown;
"crm.privacy.get": unknown;
"crm.privacy.set": unknown;
"crm.detections.list": unknown;
"crm.detections.undo": unknown;
"crm.entries.history": unknown;
"crm.activity.list": unknown;
"crm.brief.get": unknown;
"crm.context.get": unknown;
"crm.brief.regenerate": unknown;
"crm.activity.count": unknown;
"crm.notes.list": unknown;
"crm.notes.create": unknown;
"crm.comments.list": {
page: Array<{
_id: string;
entryId: string;
parentCommentId?: string;
rootCommentId?: string;
depth: number;
/** Markdown. */
body: string;
actor: {
kind: string;
label: string;
displayName?: string;
imageUrl?: string;
userId?: string;
isDench: boolean;
[key: string]: unknown;
};
mentionedUserIds: string[];
mentionsDench: boolean;
replyCount: number;
reactionSummary: {
emoji: string;
count: number;
}[];
viewerReactions: string[];
editedAt?: number;
deletedAt?: number;
denchStatus?: "pending" | "failed" | "done";
canEdit: boolean;
createdAt: number;
replies: Array<{
_id: string;
entryId: string;
parentCommentId?: string;
rootCommentId?: string;
depth: number;
/** Markdown. */
body: string;
actor: {
kind: string;
label: string;
displayName?: string;
imageUrl?: string;
userId?: string;
isDench: boolean;
[key: string]: unknown;
};
mentionedUserIds: string[];
mentionsDench: boolean;
replyCount: number;
reactionSummary: {
emoji: string;
count: number;
}[];
viewerReactions: string[];
editedAt?: number;
deletedAt?: number;
denchStatus?: "pending" | "failed" | "done";
canEdit: boolean;
createdAt: number;
[key: string]: unknown;
}>;
[key: string]: unknown;
}>;
isDone: boolean;
continueCursor: string;
};
"crm.comments.replies": {
page: Array<{
_id: string;
entryId: string;
parentCommentId?: string;
rootCommentId?: string;
depth: number;
/** Markdown. */
body: string;
actor: {
kind: string;
label: string;
displayName?: string;
imageUrl?: string;
userId?: string;
isDench: boolean;
[key: string]: unknown;
};
mentionedUserIds: string[];
mentionsDench: boolean;
replyCount: number;
reactionSummary: {
emoji: string;
count: number;
}[];
viewerReactions: string[];
editedAt?: number;
deletedAt?: number;
denchStatus?: "pending" | "failed" | "done";
canEdit: boolean;
createdAt: number;
[key: string]: unknown;
}>;
isDone: boolean;
continueCursor: string;
};
"crm.comments.get": {
_id: string;
entryId: string;
parentCommentId?: string;
rootCommentId?: string;
depth: number;
/** Markdown. */
body: string;
actor: {
kind: string;
label: string;
displayName?: string;
imageUrl?: string;
userId?: string;
isDench: boolean;
[key: string]: unknown;
};
mentionedUserIds: string[];
mentionsDench: boolean;
replyCount: number;
reactionSummary: {
emoji: string;
count: number;
}[];
viewerReactions: string[];
editedAt?: number;
deletedAt?: number;
denchStatus?: "pending" | "failed" | "done";
canEdit: boolean;
createdAt: number;
objectName: string;
entryLabel: string;
[key: string]: unknown;
};
"crm.comments.create": {
id: string;
rootCommentId?: string;
depth: number;
mentionedUserIds: string[];
mentionsDench: boolean;
};
"crm.comments.update": {
ok: boolean;
newlyMentioned: string[];
};
"crm.comments.delete": {
ok: boolean;
tombstoned: boolean;
};
"crm.comments.react": {
reacted: boolean;
reactionSummary: {
emoji: string;
count: number;
}[];
};
"crm.watch.get": {
watching: boolean;
reason?: string;
watcherCount: number;
};
"crm.watch.set": {
watching: boolean;
};
"crm.tasks.pageAssigned": unknown;
"crm.tasks.pageForEntry": unknown;
"crm.tasks.prepare": unknown;
"crm.tasks.mine": Record[];
"crm.tasks.forEntry": Record[];
"crm.tasks.createForEntry": Record;
"crm.tasks.complete": Record;
"crm.tasks.reopen": Record