Product updates from the Attrove team: new APIs, connectors, retrieval improvements, and reliability work.
Machine-readable auth for agents
An agent that wants to connect to an API hits the same wall a developer does, except it cannot read your onboarding guide. It needs to discover how to authenticate, register itself, and get a token with no human in the loop. Most APIs make that impossible. Attrove now publishes it as data.
A self-contained /auth.md at the site root documents every way to connect: hosted MCP OAuth 2.1, partner client credentials, dynamic client registration, and token revocation. It is written for a machine to parse, not a person to skim.
Alongside it, the standard OAuth discovery metadata now carries an agent_auth block that points an agent straight at the guide and at the live registration endpoint, mirrored from the real upstream so it never goes stale.
This is a small file with a large bet behind it. The next wave of integrations will not be wired up by a developer reading docs. It will be agents that arrive, read the well-known endpoints, register, and authenticate on their own. We would rather Attrove be the API they can already navigate.
Transcript push enriches existing meetings
When a meeting already exists, pushing its transcript now fills in that record instead of creating a duplicate. Notetakers and calendar syncs often land a meeting shell first, the attendees, the title, the Google Meet link, with no transcript yet. Push the transcript later and Attrove attaches it to the same meeting.
await attrove.push.meeting({
title: 'Weekly Standup',
startTime: '2026-06-18T09:00:00Z',
endTime: '2026-06-18T09:30:00Z',
transcript: 'Alice: Good morning everyone...',
});
// Matches the already-synced Google Meet and fills it in. No duplicate.
Before, a transcript pushed against an already-synced meeting either reported a duplicate and dropped the transcript, or forked a second, dangling record. The match was detected, but the one honest path, attaching the transcript to the meeting it belongs to, did not exist. Now it does.
The merge is fill-only. It adds the transcript and anything missing, and it never touches the meeting's provider identity or its calendar link, so the record stays connected to the event it came from. A meeting that already has a transcript is left untouched rather than overwritten.
No single failure stalls a sync
A connected account has one job: keep the data flowing. What quietly breaks that job is almost never a big outage. It is one bad moment, a dropped connection or a single malformed message, that wedges the entire sync. We closed two of those this week.
Provider transport blips no longer look like dead credentials. When a token refresh or a subscription renewal to Google or Microsoft drops mid-flight, that is a network hiccup, not a revoked grant. Gmail, Google Calendar, Google Meet, and Outlook now treat those transport failures as retryable, back off, and recover on their own. Only a real credential or consent failure asks your user to reconnect.
One unparseable message can no longer jam the queue. Providers occasionally hand back an email with no sender address. That single message used to fail resolution and replay forever, holding the integration behind its cursor so nothing newer synced. Addressless senders now get a deterministic identity, and the rest of the mailbox keeps moving.
A connected account should degrade gracefully, never silently freeze. Your users stay synced through the noise, and you stop hearing about the mailbox that mysteriously stopped updating.
Sharper retrieval across question types
A partner's questions are not all the same shape. "What did we decide last Tuesday" is a question about time. "What is overdue on the renewal" is a question about commitments and deadlines. "Who raised the security concern" is a question about meaning. One retrieval strategy serves none of them well, which is why generic search over a communication history underperforms on the questions people actually ask.
This release routes each query to the retrieval strategy that fits its intent and expands the semantic index behind it. Time-anchored questions get recency-aware retrieval. Task and deadline questions get the work-item lane. Open-ended questions get the widened semantic search.
The result, measured on our communication retrieval benchmark: recall@10 (the right source landing in the top ten) climbed from 0.49 to 0.66, a 34% improvement, with the best answer surfacing higher in the list. Same query and search calls, materially better answers underneath.
Communication retrieval is not one problem. Treating it as several is what makes an answer feel like it came from someone who was actually in the room.
AI notetaker notes now searchable
More of your users run an AI notetaker than record a transcript. Gemini, Otter, and the like drop a clean "Notes:" summary into the inbox after a meeting. Attrove was throwing those away.
Two things went wrong, with one effect. Notetaker summary emails look like automated mail, a no-reply sender and an unsubscribe header, so they were classified as bot messages and filtered out of search and query. And the meeting record itself stayed blank, because the only path that ever populated it was a live transcript, which a notetaker-only meeting never produces.
Both are fixed. Notetaker summaries are recognized as content, not noise. A message that pairs a known notetaker sender with a "Notes:" subject is kept, indexed, and returned in search and query like any other meeting artifact. And when a meeting has no transcript, Attrove resolves the notetaker's notes from the user's mail and the notes document, matched by meeting title and time, then writes them onto the meeting.
The result: meetings that used to come back empty now carry their summary and action items. If a user's whole workflow is "let the notetaker handle it," Attrove finally sees what the notetaker saw.
Honest failures in the connect flow
When you embed Attrove's connect flow, the worst thing that can happen is not an error. It's silence: a connection that quietly stops working while everyone assumes it's fine. This week the connect path got honest about failure and able to recover from it.
Honest provider status. When an integration provider is unavailable on Attrove's side, not the user's, connect calls now return a 503 INTEGRATION_PROVIDER_UNAVAILABLE and tell the end-user there's nothing to fix on their end. No more generic 500 that reads like the user did something wrong.
Self-healing verification. A slow OAuth callback used to consume the connect token before provisioning finished, so the follow-up check failed even when the connection had actually succeeded. Connect sessions now re-mint a fresh token before verifying, so a slow handoff recovers instead of falsely reporting a dead connection.
Clean popup completion. Popup-based connect flows close themselves on success, even when another integration is still recommended, so the end-user is never stranded on a finished screen.
A connection that lies about its own health is worse than one that's honestly down. Embedded flows have to say whose problem a failure is, and recover on their own when the answer is nobody's.
Idempotent, reversible writes for meetings and notes
Agents write meetings and notes into Attrove constantly: a transcript pushed from Otter or Granola, a note drafted mid-session, then a retry when the connection blips. The write surface used to assume that never happened, so a retried push created a second meeting and a bad write had no undo. This week, meeting and note writes became idempotent and reversible. If an agent can hit a write, it will hit it twice, and now that is safe.
// Archive by the same external_id you pushed with. Idempotent; re-push to restore.
await attrove.meetings.delete({ externalId: 'otter-standup-0529' });
// { id: 'mtg_abc', status: 'archived' }
// A retried push returns the original instead of a second copy.
await attrove.push.meeting({ title: 'Standup', externalId: 'otter-standup-0529' });
// { status: 'duplicate', duplicate_of: 'mtg_abc' } (force: true overrides)
The near-duplicate guard is meeting-specific: push.meeting checks for a near-duplicate before creating a second copy and returns status: "duplicate" with the existing duplicate_of id, so a retry is a no-op unless you pass force: true. Archive covers meetings and notes, by opaque id or by the external_id you pushed with. Archived items leave list, search, and query immediately, and re-pushing the same item restores it.
We deliberately did not hard-delete. An agent that archives the wrong meeting should restore it by re-pushing, not by filing a support ticket. Writes you can retry and undo are what separate an API a human babysits from one an agent can drive unattended.
Meeting transcripts through MCP
Agents that already read meeting transcripts from Otter, Read.ai, Fireflies, or paste from Granola had nowhere to put them. The new attrove_push_meeting MCP tool closes that loop. An agent can lift a transcript out of any meeting source, hand it to Attrove, and search it the next moment alongside Gmail, Slack, and Google Calendar.
await attrove.push.meeting({
title: '2026-05-22 advisor sync',
start_time: '2026-05-22T19:00:00Z',
end_time: '2026-05-22T19:45:00Z',
transcript: '...',
action_items: [{ text: 'Send pilot terms Friday.' }],
});
// Returns the meeting envelope with the canonical mtg_ ID.
We left metadata off the MCP input on purpose. Agents stuff garbage into free-form fields, and meetings already have first-class structure: attendees, action items, transcript. Constraining the shape is the point.
Eight MCP tools now live in the hosted server. The shape is consistent across all of them: read live communication, push agent-authored facts back to the same place.
Connected account on every capability event
Partners reconciling connect state hit the same wall: capability.granted told you an email capability had been granted, but not *which* email. Knowing the address required a second call to /v1/users/:user_id/capabilities. Now both layers carry a nested account object.
We chose a nested object over a flat account_email. Slack has no email coming out of OAuth, so a flat field would be permanently null for that provider. The nested shape returns { display_name } for Slack and { email } for Gmail, Outlook, Google Calendar, Google Meet, and Microsoft Teams. Room for provider_account_id is reserved without breaking the contract.
Events are deltas, /capabilities is truth, and both now carry the same identifier.
Partner connect flow becomes first-class
Partners building on Attrove had two friction points at the connect boundary. End-users finishing OAuth landed on an Attrove page instead of returning to the partner's product. And partner servers had no good way to know what a user had actually granted without polling integration state. Both got first-class connect-flow primitives this week.
redirect_url on API keys. Configure where end-users land after OAuth completes, with status, integration_id, and error_code returned as standardized query params. The callback resolves the final redirect against the API key's origin so the URL cannot be tampered with on the wire.
capability.granted and capability.revoked webhooks. Provider-agnostic capability names (email, chat, calendar, meet) with the source provider on every event. One fact per event per capability, so a single OAuth that grants multiple capabilities fans out to N events with no schema change.
GET /v1/users/:user_id/capabilities. The reconciliation source of truth. Events are deltas; the endpoint is truth. Partners that miss a delivery can resync state in one call.
Hosted MCP activation path
We tightened the hosted MCP onboarding path so developers can get from install to a useful answer with less guessing.
CLI docs now lead with hosted MCP installs for Claude Code, Cursor, and Claude Desktop.
MCP docs now include a concrete activation check: list connected integrations, then ask for a source-backed answer.
Examples and LLM-facing docs now point to the hosted path first and keep the local stdio server as an advanced fallback.
Partner handoffs now emphasize durable connect sessions for terminal and agent-driven onboarding.
The activation goal is simple: a connected MCP server is not enough. A useful first answer should cite the messages, meetings, or events behind it.
Cursor-only pagination across the list API
Partners paging through list endpoints with offset kept hitting the same problems: duplicated rows when new data arrived mid-walk, an expensive COUNT on every request from total_count, and silent wrong-page bugs when a cursor from one endpoint was passed to another. Cursor-only pagination replaces all of it across the entire list API.
let cursor: string | undefined;
do {
const page = await attrove.messages.list({ cursor });
for (const msg of page.data) console.log(msg.subject);
cursor = page.pagination.has_more ? page.pagination.next_cursor : undefined;
} while (cursor);
Opaque, sort-key-bound tokens. A cursor minted for /events and passed to /messages returns a clean 400 CURSOR_SORT_KEY_MISMATCH instead of silently returning the wrong page.
One primitive, one metric. Every list endpoint now flows through listPaginated() and emits a unified api.list.performance metric. Routes carry zero pagination bookkeeping.
No more total_count. It was load-bearing for nobody and expensive for everybody. has_more is the contract; next_cursor is the only thing partners need to keep walking.
Faster auth on the hot path
Every partner request resolves an sk_ token, looks up the user, and checks a quota. Doing that as cold database reads on every call put a floor under p95 that didn't need to be there. The new in-process auth-context cache moves warm-path lookups to memory while keeping the cold path correct.
Scoped to long-lived sk_ tokens. Short-lived pit_ integration tokens always bypass the cache. They are single-use by design, and caching them would defeat the security boundary.
Explicit invalidation, not hopeful TTLs. Token rotation, partner-user deletion, and profile updates all flush the relevant cache entries synchronously. Stale auth context is not a class of bug we are willing to ship.
Visibility built in. New api.auth.public_api_token and api.quota.check metrics record cache outcome and timing on every authenticated request, including the 429 path. If the cache stops earning its keep, we will see it before partners do.
Existing API keys inherit the speedup with no client changes. The cache fails closed: any miss falls through to the original lookup path, so correctness was never on the table.
Push ingest under load
Push ingest is how partner data enters Attrove, so it's the surface that has to absorb production load without surprising anyone. Three changes this week made it more forgiving.
Broader retry coverage. The push client now retries 503 and 504 (not just 429) with exponential backoff: 1, 2, 4, 8, then 16 seconds between attempts, up to six total tries. Retry-After headers are parsed strictly. Malformed values log a warning and fall back to backoff, non-finite values are rejected, and the cap stays at 300 seconds.
Explicit timeout budgets. Axios calls and the webhook job queue now share an enforced timeout, so a slow ingest fails in one place with one error rather than racing two timers and producing inconsistent partial state.
Structured warnings on partial ingests. When a multi-record document fails partway through, the response now names the document_id and the failed chunk_ids. Partners can reconcile precisely which records made it, instead of comparing full inventories on every retry.
No code change needed on the partner side. Existing SDK calls inherit the retry behavior, and existing webhook handlers inherit the new warning shape.
External IDs in search responses
Partners building on the push API kept hitting the same friction. They ingest records with their own external_id (a Salesforce opportunity ID, a Linear issue key, a CRM contact ID), but search responses came back with only Attrove's opaque IDs. Reconciling a result back to your own system meant maintaining a mapping table, or paying for an extra round trip per result.
Search now returns external_id on every record across messages, conversations, meetings, and events.
const results = await attrove.search({
user_id: 'usr_xxx',
query: 'pricing discussion with Sarah',
});
for (const message of results.key_messages) {
// message.external_id === your CRM record ID
// message.message_id === Attrove's internal ID
}
When we can't positively confirm an ID (the row was hard-deleted, an integration moved tenants, or a soft-delete is in flight), the response includes a structured identifiers_partial warning naming the missing IDs. That separates "the partner didn't supply one" from "we couldn't resolve it," which matters when the search is feeding an audit log or a side-by-side reconciliation view.
The decision worth flagging: we kept the response schema additive. external_id is required on every record, nullable when absent. Partners adding the field to their parsers don't need a feature flag, and existing parsers don't break. Reconciliation gets cheaper without a migration.
MCP error envelopes and OAuth metadata
MCP clients pin themselves to a spec. When they aren't getting the response shape they expect, they fail in ways that look like product bugs but are really protocol drift. We hardened the MCP transport surface to remove that ambiguity.
Three changes shipped:
JSON-RPC envelopes everywhere. Every error path now returns the canonical JSON-RPC envelope, with jsonrpc, id, and a structured error object carrying code, message, and data. Clients parsing strictly no longer need a fallback branch for legacy REST shapes.
Explicit token-expiry signaling. Expired access tokens return a dedicated ExpiredAccessTokenError with a WWW-Authenticate: Bearer error="invalid_token" header per RFC 6750, so introspecting clients refresh the token instead of treating it as a hard auth failure.
OAuth Protected Resource Metadata. The /.well-known/oauth-protected-resource endpoint now exposes the metadata MCP clients use for auto-configuration (RFC 9728), with a sensible fallback when not configured.
The point: MCP partners shouldn't be debugging our error shapes. The protocol is the contract.
Planned retrieval for /query
Partners debugging a /query trace kept landing on a familiar problem. The same question sometimes returned different retrieval, because the request-time pipeline stitched together vector, BM25, and entity expansion ad hoc and let an LLM rewrite the question freely. Planned retrieval replaces that with a deterministic planner.
const result = await attrove.query({
user_id: 'usr_xxx',
question: 'What did Sarah flag about the Acme deal last week?',
});
// result.warnings: [{ code: 'temporal_window_widened', ... }]
// result.no_context === true when retrieval finds nothing in scope
The planner extracts hard anchors (people, organizations, dates, exact phrases), classifies intent, composes a structured plan, and dispatches it in a single call. Same question in, same plan out.
Anchor-aware planning: people, organizations, dates, and quoted phrases survive query rewriting and constrain retrieval directly.
Time-range overlap semantics: temporal queries now use proper interval overlap instead of single-point matching, with entity expansion constrained to the inferred window. Recall@10 on the temporal evaluation set lifted to 0.74, and the explicit-date bucket reached 0.98.
Structured warnings: when retrieval degrades (window widened, query too short, no in-scope evidence), the response surfaces a typed code in warnings[] instead of a vague answer.
We rejected letting an LLM rewrite every query end-to-end. Faster to ship, harder to debug. Predictability matters more than cleverness when partners are paging through retrieval traces.
Webhook delivery health at a glance
Webhooks that quietly fail tend to stay quiet. The endpoint returns 200 most of the time, retries the rest, and the dead-letter queue fills up before anyone notices.
The webhooks dashboard now shows per-endpoint delivery telemetry at the list level, so a drifting endpoint is obvious without opening it.
Every row carries a recent success rate, a trend bar of the last delivery attempts colored by status, and a dead-letter pill when retries are exhausted. Open an endpoint and the same numbers expand into a fuller panel: latest attempt, pending and retrying counts, and a stale-signal banner that flags when a snapshot fetch failed instead of silently showing yesterday's numbers.
Same delivery records the API already exposes via GET /webhooks/:id/deliveries. New surface that makes drift legible at a glance.
Threads, raw email, and mailbox-style access
You can now page through a full Gmail or Outlook conversation with one call to the new threads endpoint, and every message arrives with its HTML body and parsed RFC 5322 headers attached. That's enough to render mailbox-style UIs, quote a message in a reply, or hand a complete chain to your model, without standing up your own mail integration.
const message = await attrove.messages.get('msg_abc');
const page = await attrove.threads.messages(message.thread_id, { offset: 0 });
for (const m of page.data) {
console.log(m.subject, m.body_html);
}
Full-fidelity bodies: every message now carries body_html and parsed RFC 5322 headers. Opt in to raw_mime when you need the exact source for signing, forwarding, or strict parsing.
Thread-ordered pagination: GET /threads/:thread_id/messages returns the complete chain in thread order across Gmail and Outlook, with default expansion of body_html and headers for mailbox views.
pit_ tokens accepted: the messages and threads endpoints both work with short-lived integration tokens, so end-user clients can render mailboxes directly instead of proxying through your server.
Ships in SDK 0.2.7 as attrove.threads.messages(threadId, options).
MCP discovery is origin-correct
The OAuth Protected Resource Metadata document at attrove.com/.well-known/oauth-protected-resource now identifies that origin directly, instead of forwarding the API origin's metadata verbatim. Strict validators and agent-readiness audits no longer reject it as an origin mismatch, so MCP clients probing Attrove's identity get a self-consistent answer.
api.attrove.com remains the source of truth for authorization_servers, scopes_supported, and bearer_methods_supported. Adding a new auth server upstream still propagates automatically. The shim only rewrites the resource field to match the origin where the document lives, per RFC 9728 §3.1.
After this fix, both origins describe themselves correctly: api.attrove.com advertises the API as the protected resource, and attrove.com advertises itself.
Notes and calendar events ground every answer
The /query endpoint now grounds answers in pushed notes and calendar events, alongside email and chat messages. Previously these sources appeared in citations but their bodies were stripped before reaching the model, so questions that depended on a note or event returned "I don't know."
Two new response fields make grounding explicit:
const result = await attrove.query({
user_id: 'usr_xxx',
question: 'What did the analyst flag about Acme last week?',
});
// result.no_context === true when retrieval returns zero records
// result.warnings lists non-fatal issues, e.g. records dropped to fit budget
no_context: true is a clean discriminator when retrieval returns zero records, replacing brittle string matching on the answer text
warnings[] surfaces non-fatal issues such as records truncated to fit the token budget
Token accounting is now record-aware, so deep queries pack the budget more tightly without truncating mid-source
If you push a note today, the next query will cite it and the answer will reflect what it says.
Payload size in request logs
Every API request and webhook delivery now records its request and response byte counts. The numbers appear inline in the dashboard logs and in CSV exports, so you can spot oversized payloads, undersized responses, and per-endpoint bandwidth trends without standing up your own metrics layer.
API logs show request_body_bytes and response_body_bytes for every call
Webhook deliveries show request_payload_bytes (what Attrove sent) and response_body_bytes (what your endpoint returned)
CSV exports preserve 0 and null distinctly, so an empty body is never confused with a missing measurement
Counts come from the Content-Length header when present and fall back to a strict body measurement. Parsing is hardened against malformed headers so a bad value never breaks the request path.
Attrove CLI for developers and agents
@attrove/cli is now available on npm. Install hosted MCP, connect integrations, and verify your setup without leaving the terminal.
// Current hosted-first path
npx @attrove/cli install claude-code
npx @attrove/cli connect --session <session-id>
npx @attrove/cli doctor
install writes hosted MCP config pointed at https://api.attrove.com/mcp, and Claude Code, Cursor, and Claude Desktop complete OAuth inside the client on first use. login still exists for SDK, CI, and advanced local stdio fallback flows.
install configures hosted MCP for Claude Desktop, Cursor, and Claude Code
local install keeps the advanced stdio fallback available for local credential-backed workflows
connect launches OAuth flows for Gmail, Outlook, Slack, and other integrations directly from the terminal
doctor verifies credentials, API connectivity, and MCP client configuration
env write exports ATTROVE_SECRET_KEY and ATTROVE_USER_ID to a project-local .env for SDK work
whoami shows the active profile, credential source, and which MCP clients are already configured
Every command supports a --json flag for machine-readable output, making the CLI a first-class integration point for agentic workflows and CI pipelines. The package has zero external dependencies.
Timezone-aware MCP tool output
All MCP tool outputs now include timezone-aware datetime formatting. Events, meetings, search results, and notes display localized timestamps with explicit timezone labels instead of raw UTC strings.
The MCP server resolves your local timezone automatically from system settings and applies it consistently across every tool response. A meeting that starts at 2026-04-08T14:00:00Z now renders as Apr 8, 2026 7:00 AM PDT rather than leaving the conversion to you.
This matters most when agents are reasoning about scheduling, availability, or message timing. Unambiguous local timestamps reduce hallucinated time conversions and make tool output directly usable in summaries, action items, and calendar-aware workflows.
Bot message filtering for queries and search
Query and search endpoints now accept an include_bot_messages parameter that controls whether automated bot messages are included in results. By default, bot messages are excluded to keep results focused on human communication.
This is especially useful for Slack-heavy workspaces where bots generate a high volume of automated notifications, deployment alerts, and workflow updates. Excluding them surfaces the conversations that matter. When you do need bot content, for example to find a CI notification or a calendar reminder, set the flag to true.
Available in SDK 0.2.3 and the latest MCP server release.
Interactive query playground in the dashboard
The partner dashboard now includes an interactive query playground that lets you test AI queries against live user data without writing any code.
Once your first integration is connected and syncing, the playground appears on your dashboard home. Pick one of three suggested queries or type your own. Results stream back with source attribution showing exactly which integrations contributed to the answer, color-coded by platform with message counts.
Suggested queries help you see results immediately with one click
Source chips show Gmail, Slack, Calendar, and other integration icons inline with the answer
Code snippet below each result shows the exact SDK call to reproduce it in your application
The playground also detects returning partners who connected integrations but never ran a query, surfacing a re-engagement prompt with fresh suggestions. Every result is a live preview of what your end users will experience through the API.
Custom source labels for pushed messages
Push API messages now support a displaySource field that sets a custom label for how the message appears in search results and query answers. Instead of showing a generic "Email" or "Custom" badge, you control what your users see.
The field is optional. When provided, it must be a non-empty string of 100 characters or fewer. The SDK trims whitespace and validates before sending. Omit it entirely to keep the default source label.
This is particularly useful when pushing data from multiple internal systems. A single user might receive messages labeled "CRM Alert", "Support Ticket", and "Live Signal", making it immediately clear where each piece of intelligence originated. SDK 0.2.2 ships with full displaySource support.
Push API and notes primitive
Four new endpoints let you push messages, meetings, events, and notes directly into a user's Attrove context. No OAuth flow required. Authenticate with your sk_ token, POST the data, and it becomes searchable via query and search within seconds.
await attrove.push.message({
source: 'email',
bodyText: 'The Q4 report is ready for review.',
senderEmail: 'alice@acme.com',
externalId: 'email-12345',
});
Every push is idempotent via externalId. Re-pushing the same ID updates the existing record and re-indexes it. Virtual integrations are auto-provisioned per source type on first push, so there is zero setup beyond the API call.
Notes are a new first-class primitive. Push analyst observations, session summaries, or partner-generated context with optional cross-references to messages, meetings, events, or entities. Read them back with attrove.notes.list() and attrove.notes.get(), filtering by reference type and ID.
Two new MCP tools ship alongside: attrove_notes for reading and attrove_push_note for writing. AI agents can now save and retrieve structured observations during workflows. SDK 0.2.1 released with full Push and Notes support.
Custom instructions and context for queries
The query endpoint now accepts two new parameters that give you fine-grained control over AI-generated answers.
instructions lets you define output formatting rules, filtering logic, and conditional behavior. The AI treats these as top-priority directives when composing its response.
context injects authoritative reference data into answer generation. The AI treats this as ground truth alongside retrieved communications. Context influences query rewriting but does not affect vector search, keeping your reference data separate from the retrieval step.
const response = await attrove.query('How is the deal progressing?', {
instructions: 'Format as bullet points. Flag risks in bold.',
context: 'Johnson deal stage: negotiation. Close target: April 15.',
});
Both parameters accept up to 20,000 characters with automatic token budget management. If context exceeds the available budget, it is truncated gracefully rather than failing the request.
Entity-aware search ranking
Search results now incorporate entity-scoped ranking signals. When your query mentions a person by name, messages from and about that person receive a post-rerank boost, surfacing the most relevant communications first.
The improvement works across the auto and deep retrieval modes. The reranker scores all candidate chunks, then applies a boost to chunks linked to entities matching the query. "What did Sarah say about pricing?" now prioritizes Sarah's actual messages over tangential mentions by others.
Entity resolution uses an in-memory cache with a 5-minute TTL per user, eliminating redundant lookups when the same user issues multiple queries in quick succession. The entity name map supports fuzzy matching across all connected platforms. Whether someone appears as "Sarah Chen" in Gmail, "sarah.chen" in Slack, or "Sarah C." in a meeting transcript, the resolver maps them to a single stable entity ID and boosts all related results equally.
One-click test users from the dashboard
The partner dashboard now supports test user provisioning in a single click. Hit "Create Test User," receive a one-time API token, and start making queries immediately. No OAuth flow, no server-side provisioning call, no waiting for a real user to connect an inbox.
Test users are flagged with is_test and their tokens carry a sk_test_ prefix. These tokens are automatically rejected in production, so sandbox credentials can never accidentally touch live data. The safety check fails closed: if the environment is ambiguous, test tokens are blocked.
Time-to-first-query for new partners drops from minutes to seconds. Spin up a test user, copy the token into the SDK playground, and start exploring the API before writing any integration code.
Proactive calendar webhooks
A new events.starting_soon webhook event type notifies your app before a user's calendar events begin. Configure the lead time per endpoint: 5, 10, 15, 30, or 60 minutes before the scheduled start.
Each payload includes attendee intelligence signals so your app can surface relevant context at exactly the right moment:
has_new_attendee flags first-contact meetings where the user has never met one or more attendees
open_action_item_count surfaces unresolved tasks assigned to meeting participants
last_meeting_with_attendees returns when this group last met, helping identify cold reconnections
The scanner runs continuously, deduplicates across rescheduled events, and enriches every notification before delivery. Combined with the user scoping and CloudEvents support shipped earlier this month, events.starting_soon makes it possible to build fully automated meeting prep workflows that fire at exactly the right time with exactly the right context.
Agent-ready: MCP Registry and machine-readable docs
Attrove is now auto-discoverable by AI agents. A new /.well-known/mcp/server.json endpoint serves Attrove's MCP server manifest, enabling automatic discovery via the MCP Registry and compatible clients like Claude Desktop, Cursor, and VS Code.
The manifest advertises both connection transports so agents can choose the integration path that fits their runtime.
Enhanced machine-readable documentation now ships across three surfaces:
AGENTS.md with fit/anti-fit guidance so agents can evaluate whether Attrove is the right tool before writing any code
SKILL.md with a complete integration workflow and framework-specific examples for OpenAI Agents SDK, LangChain, and MCP clients
llms.txt updated with structured "When to Use" criteria and step-by-step integration instructions
The goal is zero-friction discovery. An AI agent building a product that needs communication intelligence should be able to find Attrove, evaluate the fit, and start integrating without a human reading docs first.
Webhook user scoping and CloudEvents
Webhook endpoints now support user scoping. Pass an array of user IDs when creating or updating an endpoint, and only events for those users will be delivered. Leave it null to receive everything.
This reduces noise for partners who manage large user bases but only need webhook events for a subset. Scoping is optional and backward-compatible with existing endpoints. Pass null to continue receiving all events, or update the scope at any time without re-registering the endpoint.
Webhook payloads now conform to the CloudEvents 1.0 specification with structured event types (messages.new, sync.completed, integration.status_changed, meetings.new, events.new) and built-in versioning. Standard type, source, and id fields mean your existing event-processing libraries work out of the box.
Together, user scoping and standardized payloads make Attrove webhooks production-ready for event-driven architectures at scale.
Entity relationships: map your users' communication graph
The Entities API now exposes relationship data between contacts. A new endpoint returns co-occurrence pairs: which people communicate with each other, how often, and when, across all connected platforms.
This unlocks a class of features that the flat contact list couldn't support: org charts inferred from communication patterns, key-person identification, and relationship-aware search ("who does Sarah work with most?").
Each relationship pair includes the full entity profile for both sides (name, type, platform identifiers, and avatar) so you can render a people graph without additional API calls. Pagination defaults to 200 pairs (max 500) and supports a min_interactions filter to surface only meaningful relationships.
Entities API: query your users' people graph
New API endpoints are now available for querying the entity/people graph. This cross-platform identity layer makes Attrove's communication intelligence possible.
Every person your user communicates with (across email, Slack, Teams, and meetings) is resolved into a single entity with an opaque ID. You can now easily search, filter, and paginate the full people graph.
Entity IDs (formatted as ent_xxx) are stable and opaque. They won't change even as new platform connections are added. The entity graph underpins features like asking "What has Sarah been working on?" and getting answers spanning all connected platforms in a single query.
We've also added integration tests to cover search, filtering, pagination, user isolation, and error cases to ensure the people graph is rock-solid in production.
Webhooks & push endpoints
Partners can now receive real-time event notifications via webhooks. Register a URL, subscribe to event types, and receive signed payloads as data flows through Attrove.
Every webhook delivery includes webhook-id, webhook-timestamp, and webhook-signature headers with HMAC-SHA256 verification, so your server can validate that every event genuinely came from Attrove.
Built for production reliability:
Automatic retries with exponential backoff on failed deliveries
Dead letter queue for events that fail after all retry attempts
SSRF protection with IP address validation on registered URLs
Dashboard UI for managing endpoints, viewing delivery history, and debugging failures
This unlocks push-based architectures. Instead of polling the API, partners can react to new data the moment it arrives.
Sender metadata in messages API
Messages now include structured sender information (who sent each message), normalized across Gmail, Outlook, Slack, and Teams.
Sender identity is consistent across platforms. Whether Sarah sent an email from Gmail or a message in Slack, the sent_by field resolves to the same identity. This works hand-in-hand with entity resolution, as the people graph tracks who said what, where, and when.
Unknown or automated senders are handled gracefully with standardized fallbacks, so partner code never has to deal with null sender fields.
Integration health monitoring & dashboard logs
The partner dashboard now provides actionable health guidance for each integration, not just "healthy" or "unhealthy," but specific recommendations when something needs attention. Health status evaluations include precision formatting near thresholds so partners know exactly where they stand.
Logs view significantly enhanced:
Filter by billable vs. non-billable queries
Method-level color coding for quick scanning
Real-time error rate calculations with clear severity framing
Extended status filter options for granular debugging
Sync stats now break down per integration with detailed counts for messages, meetings, and events, so partners can see exactly what data is flowing from each connected platform.
Embedding model upgrade
We upgraded the embedding model that powers semantic search across all connected platforms. The new model produces 2x larger vector representations and supports a 16x larger context window, meaning it captures more nuance per message and can process longer content without truncation.
All existing indexed data was re-embedded with the new model to ensure consistent quality across historical and new messages.
The result: search queries return more relevant results, especially for longer messages, technical discussions, and threads where context matters. This compounds with the retrieval improvements shipped earlier this month (Cohere reranking, RRF fusion, temporal understanding) to push overall retrieval quality significantly higher.
Time-aware query understanding
Natural time references in queries now work automatically. Ask "what happened last week?" or "updates since Monday" and the retrieval pipeline understands the temporal intent, applies date-range filtering, and boosts recent results.
The system extracts temporal cues from queries, infers date boundaries, and applies a temporal rerank boost that surfaces the most relevant time-scoped results. Combined with the entity expansion layer, you can ask "What did Sarah say about the project this month?" and get precisely scoped results across all platforms.
Three retrieval modes give partners control over the speed/quality tradeoff:
fast: sub-second, keyword + vector search only
auto: balanced, adds reranking (default)
deep: maximum quality, adds entity expansion and temporal analysis
Microsoft Teams pilot integration
Microsoft Teams is now in pilot alongside Attrove's five live connectors: Gmail, Outlook, Slack, Google Calendar, and Google Meet. Channel messages, direct messages, and team conversations are synced, normalized, and queryable for pilot customers alongside their other communication data.
Entity resolution includes Teams for pilot customers. If the same person messages your user in Teams and emails them in Outlook, Attrove recognizes them as one entity. This means queries like "What has Sarah been working on?" can return Teams channels, email threads, Slack conversations, and meeting transcripts in a single response when Teams is enabled.
The integration supports channel subscriptions for ongoing sync, handles missing chats and channels gracefully, and includes comprehensive error recovery for the Microsoft Graph API.
Hybrid search: text + vector fusion via RRF
Queries now combine BM25 full-text search with vector embeddings using Reciprocal Rank Fusion (RRF). When benchmarked against production data, the hybrid approach with a top_n=40 cutoff outperforms either search method alone.
This means queries work by both exact keywords and semantic meaning simultaneously. Searching for "Q4 budget" finds messages that say "fourth quarter financial plan" (semantic match) alongside messages that literally mention "Q4 budget" (keyword match), then intelligently merges and ranks the results.
The improvement is especially noticeable for technical queries where exact terms matter (such as API names, project codenames, or ticket numbers) that pure vector search sometimes missed.
25% retrieval quality improvement
We've released a major overhaul of the retrieval pipeline, driven by a new 115-query evaluation framework that measures Recall and MRR across 7 query types (from simple lookups to complex multi-entity temporal queries).
Key changes:
Cohere reranker integrated as a second-pass scoring layer
Entity edge tracking with timestamps for better relationship-aware retrieval
Temporal expansion improves time-scoped queries by understanding how conversations evolve over days
8 query type generators for comprehensive, repeatable quality measurement
The result is a 25% improvement in retrieval quality measured against production-representative data. This is the kind of work that compounds because every query across every partner gets better answers, and the eval framework ensures we never ship a regression.
MCP server: HTTP transport & new tools
The Attrove MCP server now supports HTTP transport alongside stdio, so you can run it as a standalone service and connect from any MCP-compatible client over the network.
Two new tools ship with this release:
attrove_events lists calendar events from a user's connected accounts. Filter by date range to answer questions like "What's on my calendar this week?" or "When is my next meeting with Sarah?"
attrove_meetings returns meetings with AI-generated summaries and action items. Your agent can summarize yesterday's standup or extract follow-ups from a product review without any custom code.
These join the existing attrove_search, attrove_query, and attrove_integrations tools, giving AI agents a complete toolkit for working with communication data across Gmail, Outlook, Slack, Google Calendar, and Google Meet.
Thread discovery & analysis API
Two new endpoints let you find and analyze conversation threads across your users' connected platforms.
threads.discover() takes a natural language query and returns the most relevant threads, scored by semantic similarity. Filter by platform, date range, or integration.
threads.analyze() returns structured intelligence for a specific thread: summary, sentiment, action items, decisions, blockers, participants, and date range. This is the data you need to power meeting prep, thread digests, or incident postmortems without writing any NLP yourself.
New exclude_bots parameter filters out automated messages from Slack bots, calendar notifications, and other non-human senders. When your users' Slack workspace has dozens of bots posting, this keeps query results focused on real conversations.
Also shipped: token rotation in the partner dashboard. You can now rotate API keys without downtime, with the old key remaining valid for a grace period.
Google Meet & Outlook sync hardened
Two of our most-used integrations got significant reliability upgrades.
Outlook now handles transient Microsoft Graph errors with exponential backoff. Instead of failing on temporary network issues, the sync retries intelligently and recovers automatically. Delta link handling improved to gracefully manage unexpected response formats.
Google Meet gained better pagination for large meeting histories, improved time range handling, and more reliable meeting summary generation. Meeting deduplication logic was rewritten to handle edge cases where the same meeting appears with different conference IDs.
Meeting schemas are cleaned up across the board with consistent field types, nullable handling for optional fields, and proper action item attribution.
API date handling standardized to YYYY-MM-DD
All date parameters across the API now use clean YYYY-MM-DD format with automatic end-of-day inclusivity. No more timezone confusion or off-by-one errors when querying date ranges.
When you pass end_date: '2026-01-31', the API automatically includes the entire day so you get results through 11:59 PM, not midnight. This matches developer expectations and eliminates the most common date-range bug we saw in partner integrations.
SDK & MCP server improvements
Refined @attrove/sdk and @attrove/mcp packages with standardized environment variables, cleaned distribution files, and enhanced MCP server compatibility across Claude, Cursor, and ChatGPT.
Environment variables are now consistent. ATTROVE_SECRET_KEY replaces the previous ATTROVE_API_KEY across all SDKs and examples. A new version sync pipeline ensures SDK, MCP server, and documentation always ship in lockstep, so version mismatches between packages are a thing of the past.
import Attrove from '@attrove/sdk';
const attrove = new Attrove({
secretKey: process.env.ATTROVE_SECRET_KEY,
});
Build and deployment infrastructure are also upgraded, with Node.js version bumped, Dockerfiles optimized, and health check endpoints hardened across all services.
Introducing Attrove Connect
Attrove Connect is live.
We started building an AI productivity app and realized the hardest part wasn't the AI. It was getting clean, normalized access to what people say to each other across email, Slack, and meetings. That infrastructure didn't exist. So we built it instead.
Attrove Connect is a unified API for communication intelligence. Five platforms at launch: Gmail, Outlook, Slack, Google Calendar, and Google Meet. You provision users, they connect via OAuth, and you query their communication data with one SDK. Sync, normalization, and semantic indexing are handled so you can focus on what you build with the data, not how you get it.
This is day one. Everything that follows builds on this foundation.