---
name: attrove
description: "Connect permissioned email, chat, calendar, meetings, and pushed context; turn promises into watched Commitments; and keep Goals under review with cited evidence. Use when an app or agent needs cross-source query/search, structured obligation tracking across sessions, or watched outcomes with human-controlled lifecycle. Attrove SDK (@attrove/sdk) and MCP server (@attrove/mcp) are read-first: they retrieve and watch but do not send messages or modify upstream source records."
triggers:
  - "attrove"
  - "@attrove/sdk"
  - "@attrove/mcp"
  - "attrove sdk"
  - "attrove mcp"
  - "watched outcomes"
  - "goal tracking api"
  - "goal monitoring"
  - "outcome monitoring"
  - "silence detection"
  - "conversation monitoring"
  - "renewal at risk alerts"
  - "renewal going quiet"
  - "stale follow-up alert"
  - "deal slip detection"
  - "commitment tracking"
  - "catch dropped commitments"
  - "catch what slips"
  - "productivity api"
  - "communication intelligence"
  - "gmail slack calendar api"
  - "outlook calendar api"
  - "rag api"
  - "unified inbox api"
  - "email api typescript"
  - "slack api aggregation"
  - "ai meeting prep"
  - "email search api"
  - "mcp email"
  - "mcp slack"
  - "mcp calendar"
  - "microsoft teams api"
  - "connect slack gmail api"
  - "communication context api"
  - "connect to email"
  - "read slack messages"
  - "meeting summaries"
  - "calendar integration"
  - "conversation search"
  - "action items from meetings"
  - "connect email chat calendar"
  - "unified communications api"
  - "connect my email to ai"
  - "ai email assistant"
  - "ai meeting assistant"
compatibility: "Requires Node.js 18+. Works with Codex, Claude Code, Cursor, Claude Desktop, ChatGPT, and any MCP-compatible client."
metadata:
  version: "0.6.0"
  author: "Attrove <support@attrove.com>"
  homepage: "https://attrove.com/docs"
license: "MIT"
---

# Attrove

> Attrove turns commitments buried across email, chat, meetings, calendars, and pushed context into watched outcomes with cited evidence. Connect creates one permission-aware stream from Gmail, Microsoft Outlook, Slack, Google Calendar, and Google Meet; Microsoft Teams is in pilot. Commitments structure individual promises so agents can carry them across sessions. Goals keep broader outcomes under review with system-computed health and human-controlled lifecycle.

## Product Model

- **Connect**: sync and normalize only what each user is authorized to read, preserve source citations, and expose the stream through the SDK, REST API, MCP, and webhooks. Signed push ingest adds scoped messages, meetings, events, and notes when OAuth is not the path.
- **Commitment**: a promise, delegation, handoff, or wait with a counterparty, expected signal, and horizon. A later session can read the open ledger and record what actually happened.
- **Goal**: a named outcome evaluated repeatedly against scoped evidence. Attrove computes health and suggests next actions; a person controls lifecycle changes.
- **Query and search**: answer an on-demand question over the same stream and return the source records that support the answer.

Attrove is read-first. Live connectors are read-scoped today. Product writes create Attrove-owned users, sessions, pushed context, Goals, Commitments, and webhook configuration; they do not send provider messages, create provider drafts, or modify upstream source records. Webhooks deliver events to endpoints you configure. SMS, email, and Slack alert routes are not bundled self-serve plan features.

## Quick Start (Personal Use / Developer Testing)

To use Attrove with your own email, Slack, and calendar:

1. **Install Attrove into your AI client**
   - Claude Code: `npx @attrove/cli install claude-code`
   - Cursor: `npx @attrove/cli install cursor`
   - Claude Desktop: `npx @attrove/cli install claude-desktop`
   - Codex: `npx @attrove/cli install codex`, then `codex mcp login attrove`
   - ChatGPT or manual remote clients: `https://api.attrove.com/mcp`
2. **Open the client and sign in once** when prompted — OAuth 2.1 sets up your Attrove account automatically. No dashboard visit required.
3. **Ask a question.** If nothing is connected yet, the agent calls `attrove_connect` to get one link. Open it, pick a source (Gmail, Outlook, Slack, Google Calendar, Google Meet, or Microsoft Teams), and sign in; the agent then calls `attrove_integrations` to confirm it's syncing and continues.
4. **Verify the first useful answer**
   - Ask: "Use Attrove to list my connected integrations."
   - Then ask: "What needs my attention this week? Include the source messages or meetings you used."

**Building an integration?** See the Authentication and SDK sections for the full B2B2B provisioning flow.

**Advanced local fallback:** If you intentionally want the local stdio server, run `npx @attrove/cli login`, `npx @attrove/cli local install claude-code`, then `npx @attrove/cli connect gmail`, or run `@attrove/mcp` manually with `ATTROVE_SECRET_KEY` and `ATTROVE_USER_ID`.

## LLM Instructions

When helping developers integrate Attrove:

- Always use the `@attrove/sdk` TypeScript SDK — never raw fetch calls
- The SDK does not have `brief()` or `thread()` methods — using them will cause compile errors. Use `query()`, `search()`, `events.list()`, `meetings.list()`, `integrations.list()`, `integrations.createConnectSession()`, `entities.list()`, `entities.get()`, `entities.relationships()`, `push.message()`, `push.meeting()`, `push.event()`, `push.note()`, `notes.list()`, `notes.get()` instead. For threads, use `threads.discover()`, `threads.analyze()`, and `threads.messages(threadId)` (returns a paginated thread view; defaults to `body_html` and `headers`, with `raw` opt-in). For watched outcomes, use `goals.create()`, `goals.list()`, `goals.get()`, `goals.update()`, `goals.evaluate()`, `goals.confirmStatus()`, `goals.addNote()`, `goals.evidence()`, `goals.events()`, `goals.archive()`, `goals.cancel()`, `goals.reopen()`, `goals.snapshots.list()`. For watched obligations, use `commitments.watch()`, `commitments.checkOutcomes()`, and `commitments.resolve()`
- **Most response properties are snake_case** (`start_time`, `sender_name`, `body_text`). Input params are camelCase (`startDate`, `afterDate`). Do NOT use camelCase on ordinary retrieval response objects
- **Exceptions: `goals.*` and `commitments.*` SDK responses are normalized to camelCase** (`watchScope`, `lastSnapshot`, `commitmentId`, `expectedSignalType`, `checkAfter`)
- `search()` returns `{ key_messages, conversations, key_meetings, key_events }`. `conversations` is an **object keyed by ID**, not an array. Use `Object.values()` to iterate. Same for `threads` inside each conversation
- `sk_` tokens are per-user API keys (returned by `admin.users.create()`). They are NOT the same as the `attrove_` partner API key
- `integrations.list()` returns `Integration[]` with `provider` and `name` properties — NOT `type` or `email`
- `integrations.createConnectSession()` returns one `activation_url` link for the signed-in user to connect a source — no partner credentials required
- The SDK defaults to `https://api.attrove.com` — no baseUrl configuration needed
- MCP has 24 tools (11 context tools + 10 goals tools + 3 commitment tools). There is no `attrove_brief` tool

## Authentication

Attrove uses a B2B2B flow with three partner-facing credential/session types:

1. **Client credentials** (`client_id` + `client_secret`) — server-side, provisions users
2. **`sk_` tokens** — permanent per-user API keys for querying data
3. **Connect sessions** — durable activation links and CLI handoffs for OAuth

Short-lived OAuth exchange tokens are internal implementation details; partner docs and examples should create connect sessions and use their `activation_url`.

Flow: create user → receive `sk_` key → create a durable connect session when live OAuth is needed → user authorizes Gmail/Slack via OAuth or you push scoped context via `push.*` → query their data with `sk_` key.

## SDK

```bash
npm install @attrove/sdk
```

### Provision a user (server-side)

```typescript
import { Attrove } from '@attrove/sdk';

const admin = Attrove.admin({
  clientId: process.env.ATTROVE_CLIENT_ID,
  clientSecret: process.env.ATTROVE_CLIENT_SECRET,
});

const { id: userId, apiKey } = await admin.users.create({ email: 'user@example.com' });
const session = await admin.users.createConnectSession(userId, { includeInstall: true });
// Send user to: session.activation_url
// Terminal/agent handoff: session.cli?.command
```

### Query user data

```typescript
const attrove = new Attrove({
  apiKey: process.env.ATTROVE_SECRET_KEY!,
  userId: process.env.ATTROVE_USER_ID!,
});

const response = await attrove.query(
  'What needs my attention this week? Include the source messages or meetings you used.',
  { includeSources: true }
);
console.log(response.answer);
console.log(response.used_message_ids); // source message IDs (msg_xxx)
console.log(response.used_meeting_ids); // source meeting IDs (mtg_xxx)
console.log(response.used_event_ids);   // source event IDs (evt_xxx)
```

### Search messages

```typescript
const results = await attrove.search('project deadline', {
  afterDate: '2026-01-01',
  senderDomains: ['acme.com'],
  includeBodyText: true,
});
// results.conversations is Record<string, SearchConversation> — NOT an array
for (const convo of Object.values(results.conversations)) {
  for (const msgs of Object.values(convo.threads)) {   // threads is also a Record
    for (const msg of msgs) {
      console.log(msg.sender_name, msg.body_text);      // snake_case properties
    }
  }
}
```

### Other methods

```typescript
const integrations = await attrove.integrations.list();    // connected services
const session = await attrove.integrations.createConnectSession(); // one link to connect a source (no partner creds)
const { data: events } = await attrove.events.list({       // calendar events
  startDate: new Date().toISOString().split('T')[0],
  endDate: tomorrow.toISOString().split('T')[0],
  expand: ['attendees'],
});
const { data: meetings } = await attrove.meetings.list({   // past meetings with AI summaries
  expand: ['short_summary', 'action_items'],
  limit: 5,
});
const { data: contacts } = await attrove.entities.list();  // people the user communicates with
const { data: graph } = await attrove.entities.relationships(); // co-occurrence network
const { data: notes } = await attrove.notes.list();        // user notes
const note = await attrove.notes.get('note_xxx');          // single note by ID
const goal = await attrove.goals.create({                  // outcome monitoring
  title: 'ACME renewal',
  watchScope: { seedQuery: 'ACME renewal', keywords: ['ACME'] },
});
const { data: goals } = await attrove.goals.list({ lifecycle: 'active' });
```

### Push data (no OAuth required)

```typescript
// Push a message
const msg = await attrove.push.message({
  source: 'email',
  bodyText: 'The Q4 report is ready.',
  senderEmail: 'alice@acme.com',
  externalId: 'email-12345',
});

// Push a meeting
const mtg = await attrove.push.meeting({
  title: 'Sprint Planning',
  startTime: '2026-01-15T14:00:00Z',
  endTime: '2026-01-15T14:45:00Z',
  summary: 'Discussed roadmap priorities...',
});

// Push a note
const note = await attrove.push.note({
  body: 'Decision: chose Redis for caching.',
  title: 'Architecture Decision',
  refType: 'goal',
  refId: 'gol_xxx',
});
```

Meeting, event, and note titles are trimmed and limited to 500 Unicode code points.

### Watch outcomes with Goals

A Goal is a watched outcome. Attrove keeps re-evaluating it against the
communication stream (messages, meetings, events, notes), computes `health`,
and suggests lifecycle changes with cited evidence — no polling loop in your
code. A person controls `lifecycle`.

```typescript
// Create a watched outcome
const goal = await attrove.goals.create({
  title: 'Acme renewal closed by Jun 30',
  watchScope: {
    seedQuery: 'Acme renewal',
    keywords: ['Acme', 'renewal'],
    sourceTypes: ['messages', 'meetings', 'notes'],
  },
  successCriteria: 'Signed order form received.',
  deadline: '2026-06-30T23:59:59Z',
});

// List goals that need attention
const { data: atRisk } = await attrove.goals.list({
  lifecycle: 'active',
  health: 'at_risk',
});

// Read the latest snapshot — why the state is what it is
const g = await attrove.goals.get(goal.id);
console.log(g.lastSnapshot?.summary);
console.log(g.lastSnapshot?.riskSignals);   // e.g. [{ kind: 'silence', description: '...' }]

// Fetch the evidence behind the state
const { citedInLatestSnapshot, manualNotes } = await attrove.goals.evidence(goal.id);

// Other methods: update(), evaluate() (queue a fresh run, 6/goal/hour),
// confirmStatus() (human confirm/override with reason), addNote(),
// archive(reason, { expectedLifecycleVersion?, idempotencyKey?, initiator? }),
// cancel(reason, { expectedLifecycleVersion?, idempotencyKey?, initiator? }),
// reopen({ expectedLifecycle,
// expectedLifecycleVersion, reasonCode, reason, idempotencyKey }),
// snapshots.list(goalId)
```

Notes for agents:

- `watchScope` requires at least one anchor: `entityIds`, `seedQuery`, or `keywords`
- `deadline` must be a full ISO 8601 datetime — date-only strings are rejected
- `lifecycle` is one of `active | completed | archived | cancelled`; it never changes autonomously. Deliberate close/reopen commands preserve authenticated actor plus human/agent/automation initiator provenance; pass the observed lifecycle version and a stable idempotency key for safe commands.
- `health` is system-evaluated: `on_track | at_risk | blocked | waiting_on_human | insufficient_evidence`
- Risk signal kinds: `silence | deadline | sentiment | churn | blocker | ambiguous_signal | other`

### Watch obligations with Commitments

A Commitment is one promise, delegation, handoff, or wait that should survive
the current session. Give it a real counterparty or beneficiary, an expected
future signal, and a check-after time or due date. Underspecified rows are kept
as low-confidence suggestions and are never treated as active watches.

```typescript
const watched = await attrove.commitments.watch({
  commitmentText: 'Send the revised proposal to the reviewer',
  commitmentKind: 'promised_action',
  counterparty: 'the reviewer',
  expectedSignalType: 'reply',
  expectedSignalDescription: 'Reviewer acknowledges the revised proposal',
  dueAt: '2026-09-04T23:59:59Z',
  clientDedupKey: 'proposal-review-2026-09-04',
});

const { commitments } = await attrove.commitments.checkOutcomes({
  reader: 'release-agent',
});

await attrove.commitments.resolve(watched.commitmentId, {
  resolution: 'satisfied',
  signalRef: 'message:reply-from-reviewer',
  actor: 'release-agent',
});
```

`checkOutcomes()` returns open `watching` and `at_risk` rows, plus `suggested`
rows only when requested. It is an open-obligation ledger, not resolved
history. External notifications are not created automatically; use webhooks or
scope a founder-led workflow separately when delivery is required.

### Response types (mostly snake_case; Goals and Commitments use camelCase)

```typescript
// query() → QueryResponse
{ answer: string; history: ConversationMessage[]; used_message_ids: string[]; used_meeting_ids: string[]; used_event_ids: string[]; used_note_ids: string[]; sources?: { title: string; snippet: string }[] }

// search() → SearchResponse
{ key_messages: SearchKeyMessage[];                      // key message refs
  conversations: Record<string, {                        // keyed by conversation ID
    conversation_name: string | null;
    threads: Record<string, SearchThreadMessage[]>;      // keyed by thread ID
  }>;
  key_meetings: SearchMeeting[];                         // empty array when no matches
  key_events: SearchEvent[];                             // empty array when no matches
  warnings?: string[];                                   // present when enrichment had non-fatal errors
}
// SearchThreadMessage fields:
//   message_id, sender_name, body_text?, received_at, integration_type, recipient_names[]

// integrations.list() → Integration[]
// Integration fields:
//   id, provider (e.g. 'gmail', 'slack', 'outlook', 'google_calendar'), name, is_active, auth_status
//   NOTE: use `provider` not `type`, use `name` not `email`

// CursorPage<T> pagination is discriminated: has_more=true includes next_cursor.
type CursorPage<T> = {
  data: T[];
  pagination:
    | { limit: number; has_more: true; next_cursor: string }
    | { limit: number; has_more: false };
};

// events.list() → CursorPage<CalendarEvent>
// CalendarEvent fields:
//   id, title, start_time, end_time, all_day (boolean), description?, location?,
//   attendees?: { email: string; name?: string; status?: string }[]

// meetings.list() → CursorPage<Meeting>
// Meeting fields:
//   id, title, start_time, end_time, summary?, short_summary?, provider?,
//   action_items?: { description: string; assignee?: string }[],
//   attendees?: { email?: string; name?: string }[]

// entities.list() → CursorPage<EntityContact>
// EntityContact fields:
//   id (ent_xxx), name, entity_type ("person" | "company" | "other" | "bot" | "user"), external_ids: string[],
//   is_bot: boolean, avatar_uri: string | null

// entities.relationships() → RelationshipsPage
{ data: EntityRelationship[]; pagination: { limit: number; offset: number; has_more: boolean; total_count?: number } }
// EntityRelationship fields:
//   entity_a: { id, name, entity_type, external_ids, is_bot, avatar_uri }, entity_b: { id, name, entity_type, external_ids, is_bot, avatar_uri },
//   co_occurrence_count: number, last_interaction_at: string | null

// push.message() / push.meeting() / push.event() / push.note() → PushResponse
// SDK unwraps the envelope — returns { id, user_id, status, indexed_at } directly
// `queued` means accepted for asynchronous indexing, not query-ready yet.
{ id: string; user_id: string; status: 'queued' | 'processing' | 'indexed' | 'failed'; indexed_at: string | null }

// notes.list() → CursorPage<Note>
// Note fields:
//   id, body, title?, ref_type? ("message" | "meeting" | "event" | "entity" | "goal"), ref_id?, status?, indexed_at?, created_at, updated_at

// goals.list() → GoalsPage (cursor pagination)
{ data: Goal[]; pagination: { limit: number; has_more: boolean; next_cursor?: string } }
// Goal fields (camelCase — goals responses are normalized, unlike other resources):
//   id (gol_xxx), title, description, watchScope, successCriteria, completionCondition,
//   deadline, lifecycle ('active' | 'completed' | 'archived' | 'cancelled'),
//   health ('on_track' | 'at_risk' | 'blocked' | 'waiting_on_human' | 'insufficient_evidence'),
//   lastEvaluatedAt, lastSnapshot?, lastRun?, createdAt, updatedAt
// GoalStatusSnapshot fields (goals.get() lastSnapshot / goals.snapshots.list()):
//   id, goalId, lifecycle, health, summary, citedEvidenceRefs[], riskSignals[],
//   nextActions[], confidence, suggestedLifecycle, nextMove, createdAt
// goals.evidence() → { citedInLatestSnapshot: CitedEvidenceRef[]; manualNotes: Note[] }
// goals.events() → { data: GoalEvent[]; pagination: CursorPage metadata; watermark?: { eventId, occurredAt } }
//   Pass watermark back as since/sinceEventId; pass pagination.next_cursor as cursor to drain pages.

// commitments.watch() → WatchCommitmentResult (camelCase)
// { commitmentId, status, parentGoalId, summaryLine, idempotencyReplayed, missing? }
// commitments.checkOutcomes() → CheckOutcomesResult (camelCase)
// { commitments: OpenCommitment[]; count, totalCount, hasMore, nextCursor }
// OpenCommitment fields include: id, parentGoalId, status, commitmentKind,
//   commitmentText, counterparty, agent, expectedSignalType,
//   expectedSignalDescription, checkAfter, dueAt, escalationTarget, sourceType,
//   sourceRef, createdAt, summaryLine
// commitments.resolve() → { commitmentId, fromStatus, toStatus, summaryLine }
```

### Error handling

```typescript
import { AuthenticationError, RateLimitError, isAttroveError } from '@attrove/sdk';

try {
  await attrove.query('...');
} catch (err) {
  if (err instanceof AuthenticationError) { /* invalid sk_ token (401) */ }
  if (err instanceof RateLimitError) { /* retry after err.retryAfter seconds (429) */ }
  if (isAttroveError(err)) { /* other API error */ }
}
```

## MCP Server

Attrove provides an MCP server for AI assistants (Codex, Claude Desktop, Cursor, ChatGPT, Claude Code).

**Hosted remote MCP (recommended)** — use `npx @attrove/cli install codex`, `npx @attrove/cli install claude-code`, `npx @attrove/cli install cursor`, `npx @attrove/cli install claude-desktop`, or point remote-capable clients to `https://api.attrove.com/mcp`. Auth is automatic via OAuth 2.1. For Codex, run `codex mcp login attrove` after install; raw hosted tools include `attrove_notes`, `attrove_create_goal`, and `attrove_list_goals`. If you only see `mcp__codex_apps__attrove` tools, that is the OpenAI Apps connector.

**Advanced local stdio fallback** — use this only if you explicitly want local credential-backed MCP:

```bash
npx @attrove/cli login
npx @attrove/cli local install claude-code
```

**Manual stdio config**:

```json
{
  "mcpServers": {
    "attrove": {
      "command": "npx",
      "args": ["-y", "@attrove/mcp@latest"],
      "env": {
        "ATTROVE_SECRET_KEY": "sk_...",
        "ATTROVE_USER_ID": "user-uuid"
      }
    }
  }
}
```

24 MCP tools available:
- `attrove_query` — ask questions, get AI-generated answers with sources
- `attrove_search` — semantic search across messages, meetings, and calendar events
- `attrove_integrations` — list connected services
- `attrove_connect` — create one link the user opens to connect a source (Gmail, Outlook, Slack, Google Calendar, Google Meet, or Microsoft Teams); call it when `attrove_integrations` shows no sources or a query comes back empty, then confirm with `attrove_integrations` once the user finishes
- `attrove_events` — calendar events with attendees
- `attrove_meetings` — meetings with AI summaries and action items
- `attrove_notes` — list notes with filtering
- `attrove_push_note` — save a note to user context
- `attrove_push_meeting` — save a meeting from another meeting MCP (Otter, Read.ai, Fireflies) or a user-shared transcript (Granola, voice memo, manual notes). Meetings are joined to email, chat, and calendar context via Attrove's entity resolution, so cross-platform queries surface them alongside Google Meet / Zoom / Teams meetings.
- `attrove_delete_meeting` — reversibly archive a pushed meeting by `id` or `external_id`
- `attrove_delete_note` — reversibly archive a note by `id` or `external_id`
- `attrove_create_goal` — create a watched outcome (title + watch scope; optional success criteria and deadline)
- `attrove_list_goals` — list goals filtered by lifecycle, health, whose move is next (`next_move_owner`: us | them | ambiguous | none), or acknowledgment state; rows include `next_move` from the latest evaluation and cursor pagination — one call builds a ball-in-court board
- `attrove_get_goal_status` — full goal record with latest snapshot, risk signals, and evidence refs
- `attrove_evaluate_goal` — queue a fresh evaluation run for a goal
- `attrove_add_goal_note` — attach a manual note as goal evidence
- `attrove_confirm_goal_status` — human confirm or override of lifecycle/health, with a reason
- `attrove_goal_events` — poll goal lifecycle transitions with cursor metadata
- `attrove_acknowledge_goal` — acknowledge that a goal's silence is expected; suppress silence escalation until a horizon or until real activity arrives, whichever comes first
- `attrove_clear_goal_acknowledgment` — clear an active acknowledgment and resume silence monitoring immediately
- `attrove_draft_goal_follow_up` — draft an evidence-grounded follow-up email for a quiet goal (read-only; nothing is sent)
- `attrove_watch_commitment` — register a promise, delegation, or handoff (counterparty + expected signal + check-after/due time) so it survives the session and surfaces as at risk if the signal stays quiet; underspecified registrations land as suggestions that never become active watches
- `attrove_check_my_outcomes` — read back open commitments (watching/at_risk, optionally suggested) at session start, soonest-due first with cursor pagination; never returns resolved history
- `attrove_resolve_commitment` — record what actually happened (satisfied / silent_drop / dismissed / at_risk), citing the satisfying signal

## Supported Integrations

Live: Gmail, Microsoft Outlook, Slack, Google Calendar, and Google Meet. Microsoft Teams is in pilot. Push ingest: messages, meetings, events, and notes.

## When to Use Attrove

- Your product needs to understand users' email, Slack, calendar, or meeting data
- You need cross-platform intelligence (query across Gmail + Slack + Calendar simultaneously)
- You're building AI features that need communication context (meeting prep, daily digests, search agents)
- You need watched outcomes — Goals keep checking the communication stream and flag silence, deadline pressure, or risk with cited evidence, without a polling loop in your code
- You need agent accountability — Commitments register promises, delegations, and handoffs with an expected signal and a horizon, then surface quiet obligations in later sessions
- You need B2B2B: your product serves end-users who each connect their own tools via OAuth, and/or your product needs to append scoped notes, meetings, events, or messages

## When NOT to Use Attrove

- You only need data from a single provider — use that provider's API directly
- You need Attrove to send email or chat messages, create provider drafts, or modify upstream source records — live connectors are read-scoped today
- You need guaranteed provider-native sub-second ingestion from every upstream system — Attrove supports token streaming and proactive webhooks, but source freshness still follows each provider's sync cadence
- You need document/wiki indexing (Notion, Drive) — use Graphlit or Hyperspell

Note: Attrove works great for both B2B2B (embed for your users) AND single-developer use (query your own email/Slack via MCP). The personal MCP experience is the evaluation path to B2B2B adoption.

## Agent Integration Workflow

If you are an AI agent helping a developer integrate Attrove:

1. Install the SDK: `npm install @attrove/sdk`
2. Partner creates account at https://connect.attrove.com/auth/signup
3. Partner gets client credentials (`client_id` + `client_secret`) from dashboard Settings > API Keys
4. Initialize admin client and provision an end-user:
   ```typescript
   import { Attrove } from '@attrove/sdk';
   const admin = Attrove.admin({ clientId: '...', clientSecret: '...' });
   const { id: userId, apiKey } = await admin.users.create({ email: 'user@example.com' });
   ```
5. Create a durable connect session for the OAuth flow:
   ```typescript
   const session = await admin.users.createConnectSession(userId, { includeInstall: true });
   // Send user to: session.activation_url
   // Terminal/agent handoff: session.cli?.command
   ```
6. End-user visits the activation URL and authorizes Gmail/Slack/Calendar via OAuth
7. Query the user's communication data:
   ```typescript
   const attrove = new Attrove({ apiKey, userId });
   const answer = await attrove.query('What did Sarah say about the budget?');
   ```

For MCP integration (Codex, Claude Desktop, Cursor, Claude Code):
1. Prefer hosted MCP: `npx @attrove/cli install codex`, `npx @attrove/cli install claude-code`, `npx @attrove/cli install cursor`, `npx @attrove/cli install claude-desktop`, or add `https://api.attrove.com/mcp` to a remote-capable client
2. Use the advanced local fallback only when you explicitly need stdio: `npx @attrove/cli login` then `npx @attrove/cli local install claude-code`
3. Agent can use the eleven context tools, the ten Goals tools, and the three Commitment tools (`attrove_watch_commitment`, `attrove_check_my_outcomes`, `attrove_resolve_commitment`). All 24 are served by default.

## Framework Integration Examples

### OpenAI Agents SDK

```typescript
import { Attrove } from '@attrove/sdk';
import OpenAI from 'openai';

const attrove = new Attrove({ apiKey: sk_key, userId });
const openai = new OpenAI();

const tools = [{
  type: 'function' as const,
  function: {
    name: 'query_communication',
    description: 'Query user email, Slack, and calendar data',
    parameters: { type: 'object', properties: { question: { type: 'string' } }, required: ['question'] },
  },
}];

const response = await openai.chat.completions.create({
  model: 'gpt-4o',
  messages: [{ role: 'user', content: 'Summarize my meetings this week' }],
  tools,
});

if (response.choices[0].message.tool_calls) {
  const question = JSON.parse(response.choices[0].message.tool_calls[0].function.arguments).question;
  const result = await attrove.query(question);
  // Feed result.answer back to the model
}
```

### LangChain

```typescript
import { Attrove } from '@attrove/sdk';
import { tool } from '@langchain/core/tools';
import { z } from 'zod';

const attrove = new Attrove({ apiKey: sk_key, userId });

const attroveQuery = tool(
  async ({ question }) => {
    const result = await attrove.query(question);
    return result.answer;
  },
  {
    name: 'attrove_query',
    description: 'Query user email, Slack, calendar, and meeting data with natural language',
    schema: z.object({ question: z.string() }),
  },
);
```

### Codex / Claude Desktop / Cursor (MCP — zero code)

```json
{
  "mcpServers": {
    "attrove": {
      "command": "npx",
      "args": ["-y", "@attrove/mcp@latest"],
      "env": { "ATTROVE_SECRET_KEY": "sk_...", "ATTROVE_USER_ID": "user-uuid" }
    }
  }
}
```

### OpenClaw (Skill-Based)

OpenClaw agents discover Attrove via SKILL.md. Configure the agent with environment variables:

```yaml
# OpenClaw agent config
skills:
  - name: attrove
    source: npm:@attrove/mcp@latest
    env:
      ATTROVE_SECRET_KEY: sk_...
      ATTROVE_USER_ID: user-uuid
```

The agent reads the SKILL.md trigger phrases and automatically invokes Attrove tools when the user asks about email, Slack, calendar, or meeting data.

## Pricing

Self-serve Connect pricing follows published allocations and hard caps. Founder-led workflow implementations are scoped separately.

- **Starter — $0/month**: 5 connected users and 500 AI queries/month included; hard caps of 10 users and 1,000 queries. No workspace contexts or seats. No usage overage billing.
- **Growth — $99/month**: 25 users, 5,000 queries/month, 1 workspace context, and 50 workspace seats included. Hard caps: 50 users, 10,000 queries, and 5 workspace contexts. Overages: $5/additional user/month, $0.01/additional query, and $1/additional workspace seat.
- **Scale — $499/month**: 100 users, 50,000 queries/month, 5 workspace contexts, and 500 workspace seats included. Hard caps: 200 users, 100,000 queries, and 20 workspace contexts. Overages: $3/additional user/month, $0.008/additional query, and $1/additional workspace seat.
- **Enterprise — custom**: no published fixed hard caps or overage rates; allocations, limits, and billing terms are configured by agreement.

Core sync, indexing, webhook delivery, signal updates, and Goals are not billed as separate usage units. Plans do not bundle SMS, email, or Slack alert routes. Machine-readable pricing: https://attrove.com/pricing.md

## Links

- SDK: https://www.npmjs.com/package/@attrove/sdk
- MCP: https://www.npmjs.com/package/@attrove/mcp
- Examples: https://github.com/attrove/examples
- Documentation: https://attrove.com/docs
- API reference: https://attrove.com/docs/api-reference
- Dashboard: https://connect.attrove.com
