Skip to content

Contacts

Markdown

A contact is one person the business deals with: a client, a lead, a supplier. All the owner’s agents share one list, so a contact Client desk adds is there for Bookkeeper too.

A contact holds:

  • Fields: name, emails, phone, job title, labels, and named fields such as birthday.
  • Context: what everyone should know before writing to them, such as “Prefers a call to email; signs off quotes herself.” Set it with context and say why.
  • Open items: who owes the next move. you (the business owes them), them (they owe the business), or a follow_up at a time, which wakes the agent that set it.
  • Log: dated notes and every change, newest first. Changes to a contact show on its log, not on Home.
  • Companies and projects: where they work, with a role, and the projects they’re part of. Companies have their own page.

Before you write to someone, find their address and read their Context.

What find_tools sends with these tools, word for word:

The people the business deals with, shared by all the owner’s agents. Add and update the ones your work touches on your own judgement: no yes is needed, and every change can be undone. Use add_many for several at once; to fill Contacts from mail, search mail the owner sent (dir sent, bulk false): its counts.people says who they write to most; read their latest messages for names, roles and phones, then save them. Read someone’s Context with find before you write to them. The businesses they work at are in companies.

Tool What it does Owner’s yes
contacts.list Compact contacts; company is a name or id No
contacts.find The contact with that address, their Context, company Context and open items, or null No
contacts.read Fields, Context, open items, newest 20 log entries, companies and projects No
contacts.timeline Live mail threads and meetings this agent may read No
contacts.create Add a contact No
contacts.add_many Add up to 200 people at once; company is a name (the workspace’s own, made if new). Addresses already on a contact are reported, not added twice No
contacts.update Change fields and labels Checked
contacts.context Set Context and record why it changed; can be undone Checked
contacts.note Add a dated note No
contacts.follow_up Add a follow-up for this agent; at is an ISO date and time No
contacts.link_project Link a contact to a project No

contacts.list · Compact contacts; company is a name or id.

Owner’s yes: No. Nothing of the owner’s changes.

Input Type Required Limits
label text no 1–40 characters
company text no 1–200 characters
query text no up to 200 characters
open one of you, them, follow_up no
quietDays whole number no 0 or more
offset whole number no 0 or more

Returns:

{
items: {
id: string;
name: string;
company: string | null;
labels: string[];
last: ContactLast;
open: {
id: string;
kind: "you" | "them" | "follow_up";
text: string;
at: string;
thread?: string;
link?: string;
by?: {
kind: "agent" | "user" | "system";
id: string;
};
} | null;
}[];
nextOffset: number | null;
total: number;
}

contacts.find · The contact with that address, their Context, company Context and open items, or null.

Owner’s yes: No. Nothing of the owner’s changes.

Input Type Required Limits
email text yes

Returns:

{
companies: {
context: string | null;
id: string;
name: string;
ticker: string | null;
handle: string;
role?: string | null;
since?: string | null;
ended?: string | null;
labels?: string[];
}[];
id: string;
organizationId: string;
name: string;
emails: string[];
phone: string | null;
jobTitle: string | null;
labels: string[];
context: string | null;
open: {
id: string;
kind: "you" | "them" | "follow_up";
text: string;
at: string;
thread?: string;
link?: string;
by?: {
kind: "agent" | "user" | "system";
id: string;
};
}[];
last: ContactLast;
fields: ContactFreeFields;
status: "active" | "suggested" | "ignored";
projects: ProjectRef[];
createdAt: string;
updatedAt: string;
} | null

contacts.read · Fields, Context, open items, newest 20 log entries, companies and projects.

Owner’s yes: No. Nothing of the owner’s changes.

Input Type Required Limits
contactId id yes

Returns:

{
log: {
id: string;
at: string;
by: {
kind: "agent" | "user" | "system";
id: string;
};
kind: "removed" | "note" | "ask" | "context" | "labels" | "created" | "sent" | "drafted" | "triage" | "looked" | "merged" | "restored";
text?: string;
before?: string | string[] | null;
after?: string | string[] | null;
why?: string;
subject?: string;
to?: string[];
thread?: string;
link?: string;
hold?: number;
sentAt?: string;
approval?: {
kind: "rule" | "allowed" | "waiting" | "yes" | "undone" | "declined";
at?: string;
rule?: string;
};
}[];
id: string;
organizationId: string;
name: string;
emails: string[];
phone: string | null;
jobTitle: string | null;
labels: string[];
context: string | null;
open: {
id: string;
kind: "you" | "them" | "follow_up";
text: string;
at: string;
thread?: string;
link?: string;
by?: {
kind: "agent" | "user" | "system";
id: string;
};
}[];
last: ContactLast;
fields: ContactFreeFields;
status: "active" | "suggested" | "ignored";
companies: CompanyRef[];
projects: ProjectRef[];
createdAt: string;
updatedAt: string;
}

contacts.timeline · Live mail threads and meetings this agent may read.

Owner’s yes: No. Nothing of the owner’s changes.

Input Type Required Limits
contactId id yes
limit whole number no 1–100, default 20

Returns:

{
items: ContactTimelineItem[];
failures: Failure[];
}

contacts.create · Add a contact.

Owner’s yes: No. It makes something new.
Your work: listed on Home.

Input Type Required Limits
name text yes 1–200 characters
emails list of text no each up to 254 characters
phone text or null no up to 80 characters
jobTitle text or null no up to 200 characters
labels list of text no each 1–40 characters
companyIds list of id no
fields named values of anything no default {}

Returns:

ContactRecord | {
id: unknown;
message: string;
}

contacts.add_many · Add up to 200 people at once; company is a name (the workspace’s own, made if new). Addresses already on a contact are reported, not added twice.

Owner’s yes: No. It makes something new.
Your work: listed on Home.

Input Type Required Limits
people list of named values of anything yes 1–200 items

Returns:

{
added: {
id: string;
name: string;
}[];
existing: {
id: string;
name: string;
email: string;
}[];
invalid: {
row: number;
name: string | null;
error: string;
}[];
}

contacts.update · Change fields and labels.

Owner’s yes: Checked. It changes what’s there.
Your work: not listed on Home; the change shows on the record itself.

Input Type Required Limits
contactId id yes
name text no 1–200 characters
emails list of text no each up to 254 characters
phone text or null no up to 80 characters
jobTitle text or null no up to 200 characters
labels list of text no each 1–40 characters
fields named values of anything no

Returns:

{
id: string;
organizationId: string;
name: string;
emails: string[];
phone: string | null;
jobTitle: string | null;
labels: string[];
context: string | null;
open: {
id: string;
kind: "you" | "them" | "follow_up";
text: string;
at: string;
thread?: string;
link?: string;
by?: {
kind: "agent" | "user" | "system";
id: string;
};
}[];
last: ContactLast;
fields: ContactFreeFields;
status: "active" | "suggested" | "ignored";
companies: CompanyRef[];
projects: ProjectRef[];
createdAt: string;
updatedAt: string;
}

contacts.context · Set Context and record why it changed; can be undone.

Owner’s yes: Checked. It changes what’s there.
Your work: listed on Home, with Undo.

Input Type Required Limits
contactId id yes
text text yes up to 1,500 characters
why text yes 1–2,000 characters

Returns:

{
contact: ContactRecord;
entryId: string;
}

contacts.note · Add a dated note.

Owner’s yes: No. It makes something new.
Your work: not listed on Home; the change shows on the record itself.

Input Type Required Limits
contactId id yes
text text yes 1–2,000 characters

Returns:

{
id: string;
at: string;
by: {
kind: "agent" | "user" | "system";
id: string;
};
kind: "removed" | "note" | "ask" | "context" | "labels" | "created" | "sent" | "drafted" | "triage" | "looked" | "merged" | "restored";
text?: string;
before?: string | string[] | null;
after?: string | string[] | null;
why?: string;
subject?: string;
to?: string[];
thread?: string;
link?: string;
hold?: number;
sentAt?: string;
approval?: {
kind: "rule" | "allowed" | "waiting" | "yes" | "undone" | "declined";
at?: string;
rule?: string;
};
}

contacts.follow_up · Add a follow-up for this agent; at is an ISO date and time.

Owner’s yes: No. It makes something new.
Your work: not listed on Home; the change shows on the record itself.

Input Type Required Limits
contactId id yes
text text yes 1–2,000 characters
at date and time (ISO 8601) yes

Returns:

{
id: string;
organizationId: string;
name: string;
emails: string[];
phone: string | null;
jobTitle: string | null;
labels: string[];
context: string | null;
open: {
id: string;
kind: "you" | "them" | "follow_up";
text: string;
at: string;
thread?: string;
link?: string;
by?: {
kind: "agent" | "user" | "system";
id: string;
};
}[];
last: ContactLast;
fields: ContactFreeFields;
status: "active" | "suggested" | "ignored";
companies: CompanyRef[];
projects: ProjectRef[];
createdAt: string;
updatedAt: string;
}

contacts.link_project · Link a contact to a project.

Owner’s yes: No. It makes something new.
Your work: not listed on Home; the change shows on the record itself.

Input Type Required Limits
contactId id yes
projectId id yes

Returns:

{
projectId: string;
contactId: string;
}

Named types in the results above.

type ContactLast = {
first?: string;
at?: string;
channel?: string;
by?: {
kind: "agent" | "user" | "system";
id: string;
};
members?: Record<string, string>;
agents?: Record<string, string>;
}
type ContactFreeFields = Record<string, unknown> & {
ask?: boolean;
source?: string;
addedBy?: {
kind: "agent" | "user" | "system";
id: string;
};
notDuplicate?: string[];
}
type ProjectRef = {
id: string;
name: string;
}
type CompanyRef = {
id: string;
name: string;
ticker: string | null;
handle: string;
role?: string | null;
since?: string | null;
ended?: string | null;
labels?: string[];
}
type ContactTimelineItem = {
kind: "mail" | "meeting";
id: string;
title: string;
at: string;
direction?: "out" | "in";
from?: string;
snippet?: string;
url?: string;
attendees?: string[];
}
type Failure = {
source: string;
kind: "not_connected" | "reauth" | "forbidden" | "not_found" | "rate_limited" | "unavailable" | "too_large" | "unsupported" | "bad_input" | "vendor" | "changed" | "unknown_outcome";
message: string;
integration: string;
connection: string;
}
type ContactRecord = {
id: string;
organizationId: string;
name: string;
emails: string[];
phone: string | null;
jobTitle: string | null;
labels: string[];
context: string | null;
open: {
id: string;
kind: "you" | "them" | "follow_up";
text: string;
at: string;
thread?: string;
link?: string;
by?: {
kind: "agent" | "user" | "system";
id: string;
};
}[];
last: {
first?: string;
at?: string;
channel?: string;
by?: {
kind: "agent" | "user" | "system";
id: string;
};
members?: Record<string, string>;
agents?: Record<string, string>;
};
fields: Record<string, unknown> & {
ask?: boolean;
source?: string;
addedBy?: {
kind: "agent" | "user" | "system";
id: string;
};
notDuplicate?: string[];
};
status: "active" | "suggested" | "ignored";
companies: CompanyRef[];
projects: ProjectRef[];
createdAt: string;
updatedAt: string;
}