API Reference
Control your email, calendar, and LinkedIn from any AI client or your own code.
Last updated July 2026
Quick Start
Two ways to connect. Pick whichever fits your workflow.
MCP Server
Add to Claude Desktop, Cursor, or any MCP client. Your AI gets 28 tools instantly.
{
"mcpServers": {
"eaai": {
"url": "https://eaai.io/api/mcp",
"headers": {
"Authorization": "Bearer sk_eaai_..."
}
}
}
}Prompt API
One REST endpoint. Send a question, get an answer with real data from your accounts.
curl -X POST https://eaai.io/api/v1/prompt \
-H "Authorization: Bearer sk_eaai_..." \
-H "Content-Type: application/json" \
-d '{"prompt": "What meetings do I have tomorrow?"}'Authentication
All API requests require a Bearer token in the Authorization header.
Authorization: Bearer sk_eaai_YOUR_API_KEYCreate API keys in your EA AI dashboard. Each key has a scope that controls what it can do.
Read-only access to all tools. Mutations are blocked.
Read access plus mutations that queue for your approval in the dashboard.
Direct execution of all tools. Use for trusted, fully autonomous automation.
Scopes & Permissions
What each scope level can do across all 28 tools.
| Tool | read | approve | full |
|---|---|---|---|
Email list_emails | |||
get_email | |||
search_emails | |||
send_email | |||
reply_to_email | |||
Calendar list_events | |||
get_event | |||
check_availability | |||
create_event | |||
update_event | |||
delete_event | |||
LinkedIn list_linkedin_conversations | |||
get_linkedin_conversation | |||
mark_linkedin_read* | |||
send_linkedin_message | |||
reply_linkedin_message | |||
get_linkedin_profile | |||
list_linkedin_connections | |||
search_linkedin | |||
list_linkedin_invitations | |||
send_linkedin_invitation | |||
accept_linkedin_invitation* | |||
withdraw_linkedin_invitation | |||
list_linkedin_posts | |||
get_linkedin_post | |||
create_linkedin_post | |||
comment_linkedin_post | |||
react_linkedin_post* |
* mark_linkedin_read, accept_linkedin_invitation, and react_linkedin_post are low-risk mutations allowed under the approve scope without requiring approval.
The notetaker joins your video calls and writes them up on every plan, and you can ask about any meeting in EA AI chat. Meeting notes are not reachable from the API or the MCP server yet.
Rate Limits
| Limit | Value | Response |
|---|---|---|
| Requests per minute | 60 | 429 Too Many Requests |
| Daily mutation cap | Configurable per key | Blocked with reason |
| Recipient domain allowlist | Configurable per key | Blocked (send_email only) |
The daily mutation cap and recipient domain allowlist are optional safety guards you can set when creating an API key.
LinkedIn Safety Limits
EA AI enforces per-account rate limits on every LinkedIn action to keep your account safe. These limits mirror the behavior of a careful human user and prevent triggering LinkedIn's anti-automation detection.
Daily Limits
| Action | Daily Limit | Min. Gap |
|---|---|---|
| New messages | 25 | ~45s |
| Replies | 80 | ~30s |
| Connection requests | 20 | ~60s |
| Posts | 3 | ~120s |
| Comments | 15 | ~45s |
| Reactions | 30 | ~10s |
| Searches | 30 | ~15s |
| Profile views | 80 | ~10s |
| Total actions (all types) | 150 | - |
Daily limits reset at midnight UTC. Min. gap values are randomized (0.5x to 1.5x) to mimic natural human timing.
Weekly Limits
| Action | Weekly Limit | Window |
|---|---|---|
| Connection requests | 100 | Rolling 7 days |
Account Warm-up
Newly connected LinkedIn accounts enter a 28-day warm-up with gradually increasing limits. This mirrors industry best practices (Expandi, Dux-Soup) to prevent triggering LinkedIn's detection.
20% of normal limits. Establish a baseline with mostly organic activity.
40% of limits. Light automated activity begins alongside organic use.
70% of limits. Ramping toward full capacity with proven account health.
100% capacity. Fully warmed up and operating at normal limits.
Natural Behavior Pacing
Every LinkedIn action is spaced with randomized delays. If your AI agent sends actions too quickly, the API returns a pacing error with a retry_after_seconds value. The AI client should wait and retry automatically.
{
"error": "Pacing: wait 12s before the next LinkedIn message send action.",
"retry_after_seconds": 12,
"hint": "Wait before retrying. This pacing protects the LinkedIn account from detection.",
"linkedin_safety": {
"category": "message_send",
"daily_usage": "5/30",
"warmup_phase": "full"
}
}Activity Dashboard
Use the get_linkedin_activity tool or the REST endpoint to check your current usage at any time.
GET https://eaai.io/api/v1/linkedin/activity
Authorization: Bearer sk_eaai_...After every LinkedIn mutation, the response also includes a linkedin_safety field showing current usage for that action category.
MCP Server
EA AI exposes a Model Context Protocol server using Streamable HTTP transport. Add it to any MCP-compatible client.
Endpoint
https://eaai.io/api/mcpClaude Desktop
{
"mcpServers": {
"eaai": {
"url": "https://eaai.io/api/mcp",
"headers": {
"Authorization": "Bearer sk_eaai_..."
}
}
}
}Cursor
{
"mcpServers": {
"eaai": {
"type": "streamable-http",
"url": "https://eaai.io/api/mcp",
"headers": {
"Authorization": "Bearer sk_eaai_..."
}
}
}
}Supported methods
The server supports tools/list and tools/call over POST (JSON-RPC). Sessions are stateless.
Prompt API
A single REST endpoint. Send a natural-language prompt and EA AI runs a tool loop (up to 10 turns) to answer using your connected accounts.
Endpoint
POST https://eaai.io/api/v1/promptRequest
{
"prompt": "What meetings do I have tomorrow?"
}Response
{
"response": "You have 3 meetings tomorrow:\n\n1. 9:00 AM - Team standup (Google Meet)\n2. 11:30 AM - Product review with Sarah\n3. 2:00 PM - Client call with Acme Corp",
"usage": {
"input_tokens": 1542,
"output_tokens": 387
}
}Parameters
| Parameter | Type | Description |
|---|---|---|
prompt | string | Natural-language request. EA AI determines which tools to call. |
Errors
All errors return JSON with an error field.
{ "error": "Unauthorized" }| Status | Meaning |
|---|---|
400 | Bad request. The prompt field is missing or not a string. |
401 | Invalid, revoked, or missing API key. |
429 | Rate limit exceeded. Wait and retry. |
When a tool call is blocked by scope enforcement (e.g., a mutation on a read-scoped key), the error is returned within the response text, not as an HTTP error.
Email Tools
Read, search, send, and reply to emails across Gmail and Outlook.
list_emailsreadList recent email threads. Optionally filter to unread only or by sender name/email.
Parameters
| Parameter | Type | Description |
|---|---|---|
limit | number | Max threads to return (default 20, max 50). |
unread_only | boolean | Only return unread threads. |
from_filter | string | Filter by participant name or email (case-insensitive partial match). |
Response
[
{
"id": "a1b2c3d4-...",
"subject": "Q3 budget review",
"from": "Sarah Chen <sarah@example.com>",
"date": "2026-06-30T14:22:00.000Z",
"source": "gmail"
}
]get_emailreadGet full thread details and all messages for a given thread ID.
Parameters
| Parameter | Type | Description |
|---|---|---|
thread_id | string | The thread ID to retrieve. |
Response
{
"thread": {
"id": "a1b2c3d4-...",
"subject": "Q3 budget review"
},
"messages": [
{
"from": "Sarah Chen <sarah@example.com>",
"body": "Hi, attached is the Q3 budget...",
"date": "2026-06-30T14:22:00.000Z",
"outbound": false
}
]
}search_emailsreadSearch email threads by subject, participant name, or participant email.
Parameters
| Parameter | Type | Description |
|---|---|---|
query | string | Search term to match against subject, name, or email. |
limit | number | Max results (default 10, max 20). |
Response
[
{
"id": "a1b2c3d4-...",
"subject": "Q3 budget review",
"from": "Sarah Chen <sarah@example.com>",
"date": "2026-06-30T14:22:00.000Z",
"source": "gmail"
}
]send_emailmutationSend a new email via your connected email account. Subject to domain allowlist if configured on the API key.
Parameters
| Parameter | Type | Description |
|---|---|---|
to | string | Recipient email address. |
subject | string | Email subject line. |
body | string | Email body (plain text or HTML). |
from_email | string | Sender address (if the connector supports aliases). |
Response
{
"success": true,
"provider": "gmail",
"messageId": "msg_abc123"
}reply_to_emailmutationReply to the most recent inbound message in an existing email thread. Replies bypass the domain allowlist.
Parameters
| Parameter | Type | Description |
|---|---|---|
thread_id | string | The thread ID to reply in. |
body | string | Reply body (plain text or HTML). |
Response
{
"success": true,
"messageId": "msg_def456"
}Calendar Tools
List, create, update, and delete events across Google Calendar, Microsoft Calendar, and CalDAV.
list_eventsreadList calendar events across all connected calendars within a date range, sorted by start time.
Parameters
| Parameter | Type | Description |
|---|---|---|
start_date | string | Start date in ISO format (e.g. 2026-07-01). |
end_date | string | End date in ISO format (e.g. 2026-07-02). |
Response
[
{
"id": "evt_abc123",
"title": "Team standup",
"start": "2026-07-01T09:00:00.000Z",
"end": "2026-07-01T09:30:00.000Z",
"location": "Google Meet",
"attendees": ["alice@example.com"],
"source": "google_calendar",
"account": "you@example.com"
}
]get_eventreadGet full details of a specific calendar event by its ID. Currently fully supported for Google Calendar.
Parameters
| Parameter | Type | Description |
|---|---|---|
event_id | string | The event ID to retrieve. |
calendar_id | string | Calendar ID (defaults to account email). |
source | string | Source filter: google_calendar, microsoft_calendar, or caldav. |
check_availabilityreadCheck busy/free status across all connected calendars for a given time range.
Parameters
| Parameter | Type | Description |
|---|---|---|
start_date | string | Start of range in ISO format. |
end_date | string | End of range in ISO format. |
Response
{
"busy": [
{
"start": "2026-07-01T09:00:00.000Z",
"end": "2026-07-01T09:30:00.000Z",
"calendarId": "you@example.com"
}
]
}create_eventmutationCreate a new calendar event. Uses the first connected Google Calendar by default.
Parameters
| Parameter | Type | Description |
|---|---|---|
title | string | Event title. |
start | string | Start time in ISO format. |
end | string | End time in ISO format. |
description | string | Event description. |
attendees | string[] | List of attendee email addresses. |
location | string | Event location. |
Response
{
"eventId": "evt_new123",
"title": "Product review",
"start": "2026-07-02T14:00:00.000Z",
"end": "2026-07-02T15:00:00.000Z"
}update_eventmutationUpdate an existing calendar event. Only provided fields are changed. Requires Google Calendar.
Parameters
| Parameter | Type | Description |
|---|---|---|
event_id | string | The event ID to update. |
calendar_id | string | Calendar ID (defaults to account email). |
title | string | New event title. |
start | string | New start time in ISO format. |
end | string | New end time in ISO format. |
description | string | New event description. |
attendees | string[] | Updated list of attendee email addresses. |
location | string | New event location. |
Response
{
"success": true,
"eventId": "evt_abc123"
}delete_eventmutationDelete a calendar event by its ID. Requires Google Calendar.
Parameters
| Parameter | Type | Description |
|---|---|---|
event_id | string | The event ID to delete. |
calendar_id | string | Calendar ID (defaults to account email). |
Response
{
"success": true,
"eventId": "evt_abc123"
}LinkedIn Tools
Full LinkedIn support: messaging, profiles, connections, invitations, and posts.
Messaging
list_linkedin_conversationsreadList recent LinkedIn conversations. When multiple LinkedIn accounts are connected, aggregates across all unless filtered.
Parameters
| Parameter | Type | Description |
|---|---|---|
limit | number | Max conversations per account (default 20, max 50). |
account_email | string | Scope to a specific LinkedIn account. |
Response
{
"account": "you@example.com",
"conversations": [
{
"id": "conv_abc123",
"participant": "Alex Kim",
"lastMessageAt": "2026-06-30T16:45:00.000Z"
}
]
}get_linkedin_conversationreadGet full message history for a LinkedIn conversation. Auto-resolves which connected account owns the conversation.
Parameters
| Parameter | Type | Description |
|---|---|---|
conversation_id | string | The conversation ID to retrieve. |
account_email | string | LinkedIn account to use (auto-resolved if omitted). |
Response
{
"account": "you@example.com",
"conversation": {
"id": "conv_abc123",
"participant": "Alex Kim"
},
"messages": [
{
"body": "Hey, are you free for a call this week?",
"sender": "Alex Kim",
"date": "2026-06-30T16:45:00.000Z",
"outbound": false
}
]
}mark_linkedin_readmutationMark a LinkedIn conversation as read. Low-risk mutation allowed even under the approve scope without approval.
Parameters
| Parameter | Type | Description |
|---|---|---|
conversation_id | string | The conversation ID to mark as read. |
account_email | string | LinkedIn account (auto-resolved if omitted). |
Response
{
"success": true,
"account": "you@example.com"
}send_linkedin_messagemutationSend a new LinkedIn message to a recipient, starting a new conversation. When multiple accounts are connected, account_email is required.
Parameters
| Parameter | Type | Description |
|---|---|---|
recipient_id | string | LinkedIn profile ID or URN of the recipient. |
message | string | Message body to send. |
account_email | string | LinkedIn account to send from (required if multiple accounts). |
Response
{
"success": true,
"chat_id": "conv_new456",
"account": "you@example.com"
}reply_linkedin_messagemutationReply to an existing LinkedIn conversation. Auto-resolves which connected account owns the conversation.
Parameters
| Parameter | Type | Description |
|---|---|---|
conversation_id | string | The conversation ID to reply in. |
message | string | Reply message body. |
account_email | string | LinkedIn account to reply from (auto-resolved if omitted). |
Response
{
"success": true,
"messageId": "msg_li_789",
"account": "you@example.com"
}Profiles & Search
get_linkedin_profilereadGet a LinkedIn profile by identifier (profile URN, LinkedIn URL, or vanity name). Returns name, headline, location, company, and connection degree.
Parameters
| Parameter | Type | Description |
|---|---|---|
identifier | string | LinkedIn profile URN, URL, or vanity name. |
account_email | string | LinkedIn account to use (optional). |
Response
{
"account": "you@example.com",
"profile": {
"id": "urn:li:person:abc123",
"name": "Sarah Chen",
"headline": "VP Engineering at Acme Corp",
"location": "San Francisco, CA",
"company": "Acme Corp",
"connectionDegree": "2nd"
}
}list_linkedin_connectionsreadList LinkedIn connections (1st-degree relations) for the connected account.
Parameters
| Parameter | Type | Description |
|---|---|---|
limit | number | Max connections to return (default 50). |
account_email | string | LinkedIn account to use (optional). |
Response
{
"account": "you@example.com",
"connections": [
{
"id": "urn:li:person:def456",
"name": "Alex Kim",
"headline": "Product Manager",
"company": "TechCo"
}
]
}search_linkedinreadSearch LinkedIn profiles by keyword query. Returns matching profiles with name, headline, company, and connection degree.
Parameters
| Parameter | Type | Description |
|---|---|---|
query | string | Search query (name, title, company, keywords). |
limit | number | Max results (default 20). |
account_email | string | LinkedIn account to use (optional). |
Response
{
"account": "you@example.com",
"results": [
{
"id": "urn:li:person:ghi789",
"name": "Jordan Lee",
"headline": "CTO at StartupXYZ",
"company": "StartupXYZ",
"connectionDegree": "3rd"
}
]
}Invitations
list_linkedin_invitationsreadList pending LinkedIn connection requests (both sent and received).
Parameters
| Parameter | Type | Description |
|---|---|---|
account_email | string | LinkedIn account (optional). |
Response
{
"account": "you@example.com",
"invitations": [
{
"id": "inv_abc123",
"direction": "received",
"providerName": "Pat Taylor",
"message": "I'd love to connect!"
}
]
}send_linkedin_invitationmutationSend a LinkedIn connection request to a person. Optionally include a personalized message.
Parameters
| Parameter | Type | Description |
|---|---|---|
provider_id | string | LinkedIn profile URN of the person to connect with. |
message | string | Optional personalized connection message (max 300 chars). |
account_email | string | LinkedIn account to send from (required if multiple accounts). |
Response
{
"success": true,
"invitation_id": "inv_new789",
"account": "you@example.com"
}accept_linkedin_invitationmutationAccept a received LinkedIn connection request. Low-risk mutation allowed under the approve scope without approval.
Parameters
| Parameter | Type | Description |
|---|---|---|
invitation_id | string | The invitation ID to accept. |
account_email | string | LinkedIn account (optional). |
Response
{
"success": true,
"account": "you@example.com"
}withdraw_linkedin_invitationmutationWithdraw a sent LinkedIn connection request.
Parameters
| Parameter | Type | Description |
|---|---|---|
invitation_id | string | The invitation ID to withdraw. |
account_email | string | LinkedIn account (optional). |
Response
{
"success": true,
"account": "you@example.com"
}Posts & Engagement
list_linkedin_postsreadList LinkedIn posts by a user or company. If no identifier provided, lists your own posts.
Parameters
| Parameter | Type | Description |
|---|---|---|
identifier | string | Profile URN to list posts for (omit for own posts). |
limit | number | Max posts to return (default 20). |
account_email | string | LinkedIn account (optional). |
Response
{
"account": "you@example.com",
"posts": [
{
"id": "post_abc123",
"authorName": "You",
"text": "Excited to announce our new product launch...",
"createdAt": "2026-06-28T10:00:00.000Z",
"likeCount": 47,
"commentCount": 12
}
]
}get_linkedin_postreadGet a LinkedIn post by ID, including its comments.
Parameters
| Parameter | Type | Description |
|---|---|---|
post_id | string | The post ID. |
account_email | string | LinkedIn account (optional). |
Response
{
"account": "you@example.com",
"post": {
"id": "post_abc123",
"authorName": "Sarah Chen",
"text": "Great insights on AI in healthcare...",
"likeCount": 23,
"commentCount": 5
},
"comments": [
{
"id": "cmt_def456",
"authorName": "Alex Kim",
"text": "Totally agree with this take!"
}
]
}create_linkedin_postmutationCreate a new LinkedIn post as the connected user.
Parameters
| Parameter | Type | Description |
|---|---|---|
text | string | Post text content. |
account_email | string | LinkedIn account to post from (required if multiple). |
Response
{
"success": true,
"post_id": "post_new789",
"account": "you@example.com"
}comment_linkedin_postmutationComment on a LinkedIn post.
Parameters
| Parameter | Type | Description |
|---|---|---|
post_id | string | The post ID to comment on. |
text | string | Comment text. |
account_email | string | LinkedIn account (optional). |
Response
{
"success": true,
"comment_id": "cmt_new012",
"account": "you@example.com"
}react_linkedin_postmutationReact to a LinkedIn post. Low-risk mutation allowed under the approve scope without approval.
Parameters
| Parameter | Type | Description |
|---|---|---|
post_id | string | The post ID to react to. |
reaction_type | string | Reaction type: LIKE, CELEBRATE, SUPPORT, LOVE, INSIGHTFUL, or FUNNY. |
account_email | string | LinkedIn account (optional). |
Response
{
"success": true,
"account": "you@example.com"
}Activity Dashboard
get_linkedin_activityreadView your LinkedIn activity dashboard: daily usage vs. safety limits, warm-up status, weekly invitation quota, and recent actions. Use to monitor remaining capacity.
Response
{
"connectedAt": "2026-06-15T10:30:00.000Z",
"warmup": {
"phase": "full",
"daysConnected": 18,
"daysRemaining": 0,
"multiplier": 1
},
"categories": [
{
"category": "message_send",
"label": "Messages sent",
"used": 5, "limit": 30, "pct": 17
},
{
"category": "invitation_send",
"label": "Connection requests",
"used": 12, "limit": 20, "pct": 60
}
],
"weeklyInvitations": {
"used": 45, "limit": 100, "pct": 45
},
"recentActions": [
{
"tool": "reply_linkedin_message",
"category": "message_reply",
"at": "2026-07-03T14:22:00Z",
"status": "ok"
}
]
}