FairMail Agent
Connect a user's Gmail once, then read their mail, send on their behalf, and receive new-email webhooks — all through Fair's API. Any platform (Launched, a portal, your own app) integrates the same way. The email provider is never exposed; you only ever call Fair endpoints and see Fair-shaped IDs.
Getting started
You need a project-scoped Fair API key from
tryfair.ai/api-keys.
Every request is authenticated with that key plus a
user_ref — your end-user's stable identifier.
https://api.faircompany.ai https://staging.api.faircompany.ai Authentication
Send your Fair API key as a Bearer token on every request:
Authorization: Bearer <FAIR_API_KEY> -
user_ref— your end-user's stable identifier. Fair namespaces each connection per(workspace, user_ref), so one tenant can never read another's mail. -
workspaceId— only required if a key owns multiple mail workspaces. Pass?workspaceId=…or theX-Workspace-Idheader. Single-workspace keys can omit it.
The end-to-end flow
1. POST /v1/mail/agent/connect → { connect_url, connection_id }
2. Redirect the user to connect_url → they approve Google consent
3. GET /v1/mail/agent/connection/:id → poll until { status: "ACTIVE" }
4. GET /v1/mail/agent/messages → read their mail
POST /v1/mail/agent/send → send on their behalf
5. POST /v1/mail/agent/triggers → subscribe; Fair POSTs new_email to you
A connection is PENDING until the user finishes Google
consent, then ACTIVE. Reading or sending before it's
ACTIVE returns 409 auth_required — surface
"connect your inbox to continue".
Endpoints
| Method | Path | What it does |
|---|---|---|
| POST | /v1/mail/agent/connect | Start (or idempotently reuse) a connection |
| GET | /v1/mail/agent/connection/:id | Poll connection status |
| GET | /v1/mail/agent/messages | Read a connected user's mail |
| POST | /v1/mail/agent/send | Send on the user's behalf (billable) |
| POST | /v1/mail/agent/triggers | Subscribe to new-email webhooks |
| — | your callback_url | Fair POSTs new_email events to you |
1. Start a connection
curl -X POST https://api.faircompany.ai/v1/mail/agent/connect \
-H "Authorization: Bearer $FAIR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "user_ref": "user_123" }' # New, or a not-yet-active connection (needs OAuth):
201 { "connect_url": "https://connect.composio.dev/link/…",
"connection_id": "magc_…", "status": "PENDING" }
# Already-connected user_ref — idempotent no-op (no re-auth, no connect_url):
200 { "connection_id": "magc_…", "status": "ACTIVE" }
Redirect the user's browser to connect_url when one is
returned. Re-calling connect for the same user_ref is
idempotent and safe: an already-ACTIVE
mailbox returns 200 with the existing
connection_id and does not re-link. Branch on
status / the presence of connect_url, not the
HTTP code alone.
2. Check connection status
curl https://api.faircompany.ai/v1/mail/agent/connection/magc_123 \
-H "Authorization: Bearer $FAIR_API_KEY" 200 { "status": "PENDING" } // user hasn't finished consent yet
200 { "status": "ACTIVE" } // ready to read/send
200 { "status": "EXPIRED" } // link lapsed — call connect again Poll after redirecting the user, or drive it from your own post-OAuth return.
3. Read messages
curl "https://api.faircompany.ai/v1/mail/agent/messages?user_ref=user_123&limit=20" \
-H "Authorization: Bearer $FAIR_API_KEY" 200 {
"messages": [
{ "id": "19f4…", "thread_id": "19f4…", "from": "Someone <a@b.com>",
"to": "user@their-inbox.com", "subject": "…",
"preview": "first line of the body…",
"timestamp": "2026-07-09T23:19:22Z",
"labels": ["INBOX", "UNREAD"] }
]
} Reads pull metadata + preview and are effectively free — fetch full bodies only when you need them.
4. Send an email billable — cost-plus per send
curl -X POST https://api.faircompany.ai/v1/mail/agent/send \
-H "Authorization: Bearer $FAIR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "user_ref": "user_123", "to": "x@y.com", "subject": "Hi", "body": "…" }' 200 { "message_id": "19f4…" }
Billing is charged before the send: an unfunded wallet
is rejected with 402 and no email goes out;
a funded wallet is charged and the mail is sent.
5. Subscribe to new-email events
curl -X POST https://api.faircompany.ai/v1/mail/agent/triggers \
-H "Authorization: Bearer $FAIR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "user_ref": "user_123",
"callback_url": "https://yourapp.com/webhooks/fair-mail" }' 201 { "trigger_id": "…" } 6. Receive new-email webhooks
Event-driven — Fair delivers one POST per new message to your
callback_url, deduped by message id (never a full re-pull):
POST {your callback_url}
{ "event": "new_email", "connection_id": "magc_…", "user_ref": "user_123",
"message": { "id": "…", "from": "…", "subject": "…",
"preview": "…", "timestamp": "…" } }
Respond 2xx to acknowledge. Analyze once per message id and
gate expensive processing (e.g. AI) behind your own rules, so you only
pay for the mail that matters.
GET /messages in addition to
relying on the webhook.
SDK & MCP
The same surface is available in the typed
@fair/sdk under mail.agent.*:
import { FairClient } from "@fair/sdk";
const fair = new FairClient({ apiKey: process.env.FAIR_API_KEY });
// 1. Connect a user's Gmail once
const { connect_url, connection_id } = await fair.mail.agent.connect({
user_ref: "user_123",
});
// 2. Read their mail
const { messages } = await fair.mail.agent.messages({
user_ref: "user_123",
limit: 20,
});
// 3. Send on their behalf (billable — cost-plus per send)
const { message_id } = await fair.mail.agent.send({
user_ref: "user_123",
to: "x@y.com",
subject: "Hi",
body: "…",
});
// 4. Subscribe to new-email webhooks
await fair.mail.agent.subscribeTrigger({
user_ref: "user_123",
callback_url: "https://yourapp.com/webhooks/fair-mail",
}); The agent surface is also exposed as tools on the unified Fair MCP server, so an AI agent can connect a mailbox, read, and send using the same endpoints described here.
Error reference
| Status | Code | Meaning |
|---|---|---|
| 400 | fair/validation-error | Bad/missing field, or multiple workspaces — pass workspaceId |
| 402 | wallet/insufficient-balance | Wallet empty; top up. (Charged before send — no mail sent.) |
| 404 | mail/agent-connection-not-found | Unknown connection_id (or not in your workspace) |
| 409 | mail/agent-not-connected | No connection for this user_ref — call connect first |
| 409 | mail/agent-auth-required | Connection exists but still PENDING — finish consent |
| 4xx | connector/* | Upstream provider issue, surfaced brand-clean (no internal leak) |
Full API reference
Every endpoint, schema, and response is in the live Swagger reference: api.faircompany.ai/v1/docs/ui. See also the Fair API overview and the Connections (Slack) guide.
Support
Questions? Email hello@faircompany.ai.