---
title: Contacts
description: "The people the business deals with, shared by all the owner's agents. 11 tools."
group: contacts
tools: [contacts.list, contacts.find, contacts.read, contacts.timeline, contacts.create, contacts.add_many, contacts.update, contacts.context, contacts.note, contacts.follow_up, contacts.link_project]
source: [packages/agents/src/tools/features.ts, packages/agents/src/contact-record.ts]
generated: true
---

> Roadstar docs for agents. Every page, as markdown: https://docs.roadstar.app/llms.txt

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](/tools/companies/).

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

## How to use it

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.

## Tools

| Tool | What it does | Owner's yes |
| --- | --- | --- |
| [contacts.list](#list) | Compact contacts; company is a name or id | No |
| [contacts.find](#find) | The contact with that address, their Context, company Context and open items, or null | No |
| [contacts.read](#read) | Fields, Context, open items, newest 20 log entries, companies and projects | No |
| [contacts.timeline](#timeline) | Live mail threads and meetings this agent may read | No |
| [contacts.create](#create) | Add a contact | No |
| [contacts.add_many](#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](#update) | Change fields and labels | Checked |
| [contacts.context](#context) | Set Context and record why it changed; can be undone | Checked |
| [contacts.note](#note) | Add a dated note | No |
| [contacts.follow_up](#follow_up) | Add a follow-up for this agent; at is an ISO date and time | No |
| [contacts.link_project](#link_project) | Link a contact to a project | No |

## list

`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:

```ts
{
  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;
}
```

## find

`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:

```ts
{
  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
```

## read

`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:

```ts
{
  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;
}
```

## timeline

`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:

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

## create

`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:

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

## add_many

`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:

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

## update

`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:

```ts
{
  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;
}
```

## context

`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:

```ts
{
  contact: ContactRecord;
  entryId: string;
}
```

## note

`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:

```ts
{
  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;
  };
}
```

## follow_up

`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:

```ts
{
  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;
}
```

## link_project

`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:

```ts
{
  projectId: string;
  contactId: string;
}
```

## Types

Named types in the results above.

```ts
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;
}
```
