Contacts (CRM)
Relationship layer — influencers, partners, candidates.
13 actions. Each is callable by the agent as a tool, over HTTP via /api/invoke, and (usually) from a UI page.
contacts.list
GET
List CRM contacts. Filter by type, stage, or owner.
- AI tool:
contacts_list - API:
GET /api/invoke/contacts.list
Input
| Field | Type | Required | Description |
|---|---|---|---|
type |
"influencer" | "partner" | "affiliate" | "press" | "candidate" | "vendor" | "fan" | "other" | — | |
stage |
string | — | |
ownerActorId |
string | — | |
limit |
integer | — | |
offset |
integer | — | |
orderBy |
string | — | |
orderDir |
"asc" | "desc" | — | |
q |
string | — |
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"influencer",
"partner",
"affiliate",
"press",
"candidate",
"vendor",
"fan",
"other"
]
},
"stage": {
"type": "string"
},
"ownerActorId": {
"type": "string",
"minLength": 1
},
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 500
},
"offset": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"orderBy": {
"type": "string"
},
"orderDir": {
"type": "string",
"enum": [
"asc",
"desc"
]
},
"q": {
"type": "string"
}
}
}contacts.search
GET
Find contacts by name, handle, company, or tags.
- AI tool:
contacts_search - API:
GET /api/invoke/contacts.search
Input
| Field | Type | Required | Description |
|---|---|---|---|
q |
string | yes |
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"q": {
"type": "string"
}
},
"required": [
"q"
]
}contacts.get
GET
Get one contact by id.
- AI tool:
contacts_get - API:
GET /api/invoke/contacts.get
Input
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | yes |
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"minLength": 1
}
},
"required": [
"id"
]
}contacts.create
POST · mutating
Create a CRM contact — someone you engage WITH (influencer, partner, journalist, candidate, vendor, fan), as opposed to a subject you post AS or promote.
When the agent calls it: Use when the user mentions an influencer, partner, journalist, candidate, vendor, or fan they want to track and reach out to. Pick the closest
type. Do NOT create a subject for these — subjects are promotion targets. If the same person is also someone the user promotes or posts as, create the contact then call contacts.link_subject to connect it.
- AI tool:
contacts_create - API:
POST /api/invoke/contacts.create
Input
| Field | Type | Required | Description |
|---|---|---|---|
type |
"influencer" | "partner" | "affiliate" | "press" | "candidate" | "vendor" | "fan" | "other" | yes | |
name |
string | yes | |
handle |
string | — | |
avatarUrl |
string | — | |
company |
string | — | |
title |
string | — | |
headline |
string | — | |
bio |
string | — | |
location |
string | — | |
timezone |
string | — | |
tags |
string[] | — | |
stage |
string | — | |
ownerActorId |
union | — | |
source |
string | — | |
linkedSubjectId |
union | — | |
audience |
object | — | |
links |
object[] | — | |
customFields |
object | — |
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"influencer",
"partner",
"affiliate",
"press",
"candidate",
"vendor",
"fan",
"other"
]
},
"name": {
"type": "string"
},
"handle": {
"type": "string"
},
"avatarUrl": {
"type": "string"
},
"company": {
"type": "string"
},
"title": {
"type": "string"
},
"headline": {
"type": "string"
},
"bio": {
"type": "string"
},
"location": {
"type": "string"
},
"timezone": {
"type": "string"
},
"tags": {
"default": [],
"type": "array",
"items": {
"type": "string"
}
},
"stage": {
"default": "new",
"type": "string"
},
"ownerActorId": {
"anyOf": [
{
"type": "string",
"minLength": 1
},
{
"type": "null"
}
]
},
"source": {
"default": "manual",
"type": "string"
},
"linkedSubjectId": {
"anyOf": [
{
"type": "string",
"minLength": 1
},
{
"type": "null"
}
]
},
"audience": {
"default": {},
"type": "object",
"properties": {
"totalReach": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"tier": {
"type": "string",
"enum": [
"nano",
"micro",
"mid",
"macro",
"mega"
]
},
"score": {
"type": "number",
"minimum": 0,
"maximum": 1
},
"byPlatform": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "object",
"properties": {
"followers": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"engagementRate": {
"type": "number"
},
"avgViews": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
}
}
}
}
}
},
"links": {
"default": [],
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"github",
"website",
"twitter",
"linkedin",
"youtube",
"instagram",
"tiktok",
"threads",
"bluesky",
"blog",
"paper",
"talk",
"podcast",
"document",
"pdf",
"other"
]
},
"url": {
"type": "string",
"format": "uri"
},
"label": {
"type": "string"
},
"verified": {
"type": "boolean"
},
"followers": {
"type": "number"
}
},
"required": [
"type",
"url"
]
}
},
"customFields": {
"default": {},
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
}
},
"required": [
"type",
"name"
]
}contacts.update
PATCH · mutating
Update fields on a contact (set stage to move it through the pipeline).
When the agent calls it: Advance the relationship by setting
stage. Valid stages depend on the contacttype.
- AI tool:
contacts_update - API:
PATCH /api/invoke/contacts.update
Input
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | yes | |
patch |
object | yes |
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"minLength": 1
},
"patch": {
"type": "object",
"properties": {
"id": {
"type": "string",
"minLength": 1
},
"type": {
"type": "string",
"enum": [
"influencer",
"partner",
"affiliate",
"press",
"candidate",
"vendor",
"fan",
"other"
]
},
"name": {
"type": "string"
},
"handle": {
"type": "string"
},
"avatarUrl": {
"type": "string"
},
"company": {
"type": "string"
},
"title": {
"type": "string"
},
"headline": {
"type": "string"
},
"bio": {
"type": "string"
},
"location": {
"type": "string"
},
"timezone": {
"type": "string"
},
"tags": {
"default": [],
"type": "array",
"items": {
"type": "string"
}
},
"stage": {
"default": "new",
"type": "string"
},
"ownerActorId": {
"anyOf": [
{
"type": "string",
"minLength": 1
},
{
"type": "null"
}
]
},
"source": {
"default": "manual",
"type": "string"
},
"linkedSubjectId": {
"anyOf": [
{
"type": "string",
"minLength": 1
},
{
"type": "null"
}
]
},
"convertedActorId": {
"anyOf": [
{
"type": "string",
"minLength": 1
},
{
"type": "null"
}
]
},
"audience": {
"default": {},
"type": "object",
"properties": {
"totalReach": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"tier": {
"type": "string",
"enum": [
"nano",
"micro",
"mid",
"macro",
"mega"
]
},
"score": {
"type": "number",
"minimum": 0,
"maximum": 1
},
"byPlatform": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "object",
"properties": {
"followers": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"engagementRate": {
"type": "number"
},
"avgViews": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
}
}
}
}
}
},
"totalReach": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"links": {
"default": [],
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"github",
"website",
"twitter",
"linkedin",
"youtube",
"instagram",
"tiktok",
"threads",
"bluesky",
"blog",
"paper",
"talk",
"podcast",
"document",
"pdf",
"other"
]
},
"url": {
"type": "string",
"format": "uri"
},
"label": {
"type": "string"
},
"verified": {
"type": "boolean"
},
"followers": {
"type": "number"
}
},
"required": [
"type",
"url"
]
}
},
"customFields": {
"default": {},
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
"lastInteractionAt": {
"anyOf": [
{},
{
"type": "null"
}
]
},
"createdBy": {
"anyOf": [
{
"type": "string",
"minLength": 1
},
{
"type": "null"
}
]
},
"createdAt": {},
"updatedAt": {}
}
}
},
"required": [
"id",
"patch"
]
}contacts.delete
DELETE · mutating
Soft-delete a contact. Reversible via contacts.restore.
- AI tool:
contacts_delete - API:
DELETE /api/invoke/contacts.delete
Input
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | yes |
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"minLength": 1
}
},
"required": [
"id"
]
}contacts.restore
POST · mutating
Restore a soft-deleted contact.
- AI tool:
contacts_restore - API:
POST /api/invoke/contacts.restore
Input
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | yes |
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"minLength": 1
}
},
"required": [
"id"
]
}contacts.timeline
GET
Get a contact's activity timeline — inbound engagements, DM threads, calls, outreach tickets, and notes merged in time order. Keyset-paginate with before.
- AI tool:
contacts_timeline - API:
GET /api/invoke/contacts.timeline
Input
| Field | Type | Required | Description |
|---|---|---|---|
contactId |
string | yes | |
limit |
integer | — | |
before |
any | — |
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"contactId": {
"type": "string",
"minLength": 1
},
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 200
},
"before": {}
},
"required": [
"contactId"
]
}contacts.send_outreach
POST · mutating
Send an outreach DM to a contact via the cross-platform DM router. Resolves the recipient from the contact's channels, mirrors the thread, links it to the contact, logs it, and advances an early-stage relationship to "contacted".
When the agent calls it: Use to reach out to a contact. For SMS, also set fromPhoneNumberId. Works in simulated mode when no provider is connected.
- AI tool:
contacts_send_outreach - API:
POST /api/invoke/contacts.send_outreach
Input
| Field | Type | Required | Description |
|---|---|---|---|
contactId |
string | yes | |
platform |
"twitter" | "linkedin" | "instagram" | "slack" | "discord" | "sms" | yes | |
body |
string | yes | |
fromSubjectId |
string | — | |
actorId |
string | — | |
fromPhoneNumberId |
string | — | |
composioUserId |
string | — | |
advanceStage |
boolean | — |
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"contactId": {
"type": "string",
"minLength": 1
},
"platform": {
"type": "string",
"enum": [
"twitter",
"linkedin",
"instagram",
"slack",
"discord",
"sms"
]
},
"body": {
"type": "string",
"minLength": 1
},
"fromSubjectId": {
"type": "string",
"minLength": 1
},
"actorId": {
"type": "string",
"minLength": 1
},
"fromPhoneNumberId": {
"type": "string",
"minLength": 1
},
"composioUserId": {
"type": "string"
},
"advanceStage": {
"type": "boolean"
}
},
"required": [
"contactId",
"platform",
"body"
]
}contacts.list_send_accounts
GET
List the subject/persona accounts that can SEND — subjects with an active Stagehand browser session or a usable Composio connection. Each account is tied to a platform (the account IS the platform choice). Pass platform to filter to one. Powers the "Send from" picker for outreach.
- AI tool:
contacts_list_send_accounts - API:
GET /api/invoke/contacts.list_send_accounts
Input
| Field | Type | Required | Description |
|---|---|---|---|
platform |
string | — |
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"platform": {
"type": "string"
}
}
}contacts.convert_to_actor
POST · mutating
Convert a candidate-type contact into an Actor (a hire / new persona-driver). Creates the actor, links contact.convertedActorId, and advances the contact to "onboarded". Idempotent.
When the agent calls it: Only for type="candidate". Use after the user decides to hire/onboard the person.
- AI tool:
contacts_convert_to_actor - API:
POST /api/invoke/contacts.convert_to_actor
Input
| Field | Type | Required | Description |
|---|---|---|---|
contactId |
string | yes | |
kind |
"human" | "agent" | — | |
sendWelcomeDm |
boolean | — | |
welcomeBody |
string | — | |
welcomePlatform |
"twitter" | "linkedin" | "instagram" | "slack" | "discord" | "sms" | — |
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"contactId": {
"type": "string",
"minLength": 1
},
"kind": {
"type": "string",
"enum": [
"human",
"agent"
]
},
"sendWelcomeDm": {
"type": "boolean"
},
"welcomeBody": {
"type": "string"
},
"welcomePlatform": {
"type": "string",
"enum": [
"twitter",
"linkedin",
"instagram",
"slack",
"discord",
"sms"
]
}
},
"required": [
"contactId"
]
}contacts.link_subject
POST · mutating
Link (or unlink, with subjectId=null) a contact to a Subject — for someone you both court (contact) and promote/post-as (subject).
- AI tool:
contacts_link_subject - API:
POST /api/invoke/contacts.link_subject
Input
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | yes | |
subjectId |
union | yes |
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"minLength": 1
},
"subjectId": {
"anyOf": [
{
"type": "string",
"minLength": 1
},
{
"type": "null"
}
]
}
},
"required": [
"id",
"subjectId"
]
}contacts.create_from_subject
POST · mutating
Create a CRM contact prefilled from an existing subject and link them. Use to move a relationship-style person (an influencer, partner, recruit) OUT of the subjects list into the CRM without losing the subject or its missions.
When the agent calls it: Use when the user realizes a person they created as a subject is really a relationship to manage (not something they post as). Keeps the subject; links it.
- AI tool:
contacts_create_from_subject - API:
POST /api/invoke/contacts.create_from_subject
Input
| Field | Type | Required | Description |
|---|---|---|---|
subjectId |
string | yes | |
type |
"influencer" | "partner" | "affiliate" | "press" | "candidate" | "vendor" | "fan" | "other" | — |
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"subjectId": {
"type": "string",
"minLength": 1
},
"type": {
"type": "string",
"enum": [
"influencer",
"partner",
"affiliate",
"press",
"candidate",
"vendor",
"fan",
"other"
]
}
},
"required": [
"subjectId"
]
}