Every API here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for apis
7 MCP tools reach this
find_apisBrowse and filter every API in the catalog.
get_api_artifactsOne API's artifacts, grouped by type.
get_openapiThe primary OpenAPI for this API.
find_similar_apisAPIs that look like this one.
apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
resolveTurn a domain, URL or GitHub org into the provider it belongs to.
find_cohortsEvery scored population of providers in the catalog.
All 92 tools →
Call it yourself
curl for this page
This API
curl "https://apis.io/api/v1/apis/anjal-app:anjal-app-linkedin-sessions-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we
store it to create your key and to recognise you if you sign in with another
provider. See our Privacy Policy and
Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Anjal LinkedIn — Sessions API
version: 1.0.0
description: 'Anjal is a multi-tenant email API for AI agents.
Create inboxes on the default `@send.anjal.app` domain or your own custom
domains, send and receive mail, manage drafts, subscribe to real-time
events over WebSocket, and isolate resources across workspaces so your
reseller customers can serve their own end-customers under a single
billing relationship.
Install the TypeScript SDK: `npm install anjal`
(npm)
MCP server for Claude Desktop / Cursor: `npx -y anjal-mcp`
(npm)
## Authentication
Every request is authenticated with the `X-API-Key` header (or
`Authorization: Bearer `). Four key prefixes, each with increasing
scope:
| Prefix | Scope | Use it for |
|----------|-----------------------------------------|-----------------------------------------|
| `sa_...` | Super admin | Tenant lifecycle (platform operator) |
| `tk_...` | Tenant master | Everything in your tenant across workspaces |
| `wk_...` | Workspace master | One workspace only |
| `ak_...` | Inbox (agent) scoped | One inbox — its messages, drafts, lists |
## Resource hierarchy
```
Tenant (billing root)
└── Workspace (isolation container)
├── Inboxes
│ ├── Messages / Threads / Attachments
│ ├── Drafts
│ └── Lists
└── Domains
```
## Sending mail
Mail is sent via `POST /v1/inboxes/{id}/send`. Replies go through
`/reply/{messageId}`, reply-all through `/reply-all/{messageId}`, and
forwards through `/forward/{messageId}`. The `From` header always uses
the inbox''s **primary alias** — add a custom-domain address with
`POST /v1/inboxes/{id}/addresses` to change it.
### Safe retries (Idempotency-Key)
Every send endpoint (`/send`, `/reply`, `/reply-all`, `/forward` and
`POST /v1/inboxes/{id}/drafts/{did}/send`) accepts an `Idempotency-Key`
header. Send a unique value (a UUID works) and retry freely: the first
request sends and stores its response; a retry with the same key and the
identical request returns that stored response with the header
`Idempotent-Replayed: true` and sends nothing.
| Situation | Result |
|---|---|
| Same key, different request (body, inbox or endpoint) | `409 IDEMPOTENCY_KEY_REUSED` |
| Same key while the first request is still running | `409 IDEMPOTENCY_IN_PROGRESS` |
| Header present but blank, or longer than 255 characters | `400 IDEMPOTENCY_KEY_INVALID` |
| The first send failed (non-2xx) | The key is freed; retry with the same key |
Keys are scoped to your account and expire after 24 hours.
### Labels, open tracking, scheduled send
- **Labels**: pass `labels` (array, or a comma-separated string) on any
send, reply, reply-all, forward or draft. Change them later with
`PATCH /v1/inboxes/{id}/messages/{messageId}` or
`PATCH /v1/inboxes/{id}/threads/{threadId}`, and filter lists with
`?labels=a,b`. Labels are stored lowercase and deduplicated; at most 32
per message, each at most 64 characters, no commas.
- **Open tracking**: `trackOpens: true` on a send with an HTML body adds a
1x1 pixel (the stored copy never contains it). The first open fires
`message.opened`. Opens are a hint, not proof: Apple Mail Privacy
Protection fetches images on delivery, and clients that block images
never report an open.
- **Scheduled send**: set `sendAt` on a draft and Anjal sends it at that
time. See the Drafts endpoints.
- **Delivery status**: sent messages carry `deliveryStatus`
(`delivered`, `bounced`, `complained` or `rejected`; null until known)
and `deliveredAt`.
## Receiving mail
Inbound mail arrives via SMTP on the default domain or any verified
custom domain. Subscribe to delivery events either by registering a
webhook (`POST /v1/webhooks`) or by opening a WebSocket
(`wss://anjal.app/v1/ws`) — useful for in-line 2FA flows where
the agent doesn''t have a public webhook receiver.'
servers:
- url: https://anjal.app
description: Production
security:
- apiKeyHeader: []
tags:
- name: LinkedIn — Sessions
description: 'Login on behalf of the customer (email + password, with PIN or mobile-app
verification challenge support), or import existing cookies. Cookies are
AES-256-GCM encrypted at rest. Each session is tenant-scoped.'
paths:
/linkedin/sessions:
post:
tags:
- LinkedIn — Sessions
summary: Login (may challenge)
description: 'Begin a LinkedIn login on behalf of the customer using their email +
password. Returns `{ ok: true, sessionId }` on direct success, or
`{ challenge: true, challengeId, challengeType }` if LinkedIn requires
a PIN (email/SMS) or mobile-app approval. Follow up with
`/sessions/verify` (PIN) or `/sessions/poll` (mobile approval).'
operationId: linkedinLogin
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- email
- password
properties:
email:
type: string
format: email
password:
type: string
format: password
label:
type: string
description: Optional friendly label for this session.
responses:
'200':
description: Either a successful login or a challenge requirement.
content:
application/json:
schema:
oneOf:
- type: object
properties:
ok:
type: boolean
enum:
- true
sessionId:
type: string
label:
type: string
nullable: true
createdAt:
type: string
format: date-time
- type: object
properties:
ok:
type: boolean
enum:
- false
challenge:
type: boolean
enum:
- true
challengeId:
type: string
challengeType:
type: string
verifyEndpoint:
type: string
pollEndpoint:
type: string
expiresAt:
type: string
format: date-time
'401':
description: Invalid email/password.
'402':
description: LinkedIn module not subscribed.
'403':
description: Session limit reached.
get:
tags:
- LinkedIn — Sessions
summary: List sessions
operationId: linkedinListSessions
responses:
'200':
description: Array of session metadata (cookies are never returned).
content:
application/json:
schema:
type: object
properties:
ok:
type: boolean
sessions:
type: array
items:
type: object
properties:
id:
type: string
label:
type: string
nullable: true
workspaceId:
type: string
nullable: true
status:
type: string
createdAt:
type: string
format: date-time
lastUsedAt:
type: string
format: date-time
nullable: true
/linkedin/sessions/verify:
post:
tags:
- LinkedIn — Sessions
summary: Submit PIN to complete a challenge
description: Submit the email/SMS PIN LinkedIn sent to the customer.
operationId: linkedinVerifyPin
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- challengeId
- pin
properties:
challengeId:
type: string
pin:
type: string
label:
type: string
responses:
'200':
description: Session created.
'401':
description: Wrong PIN.
'404':
description: Challenge not found or expired.
/linkedin/sessions/poll:
post:
tags:
- LinkedIn — Sessions
summary: Collect mobile-app approval
description: 'For mobile-approval challenges, after the customer taps "I''ve approved"
in the LinkedIn mobile app. Returns 202 + `pending: true` if not yet
approved (try again in a few seconds).'
operationId: linkedinPollMobileApproval
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- challengeId
properties:
challengeId:
type: string
label:
type: string
responses:
'200':
description: Session created.
'202':
description: Approval not yet processed.
'404':
description: Challenge not found or expired.
/linkedin/sessions/import:
post:
tags:
- LinkedIn — Sessions
summary: Import existing cookies
description: 'Escape hatch for customers who already extracted `li_at` + `JSESSIONID`
from a logged-in browser. Skip when you can use `/sessions` instead.'
operationId: linkedinImportSession
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- liAt
- jsessionId
properties:
liAt:
type: string
jsessionId:
type: string
label:
type: string
responses:
'200':
description: Session created.
/linkedin/sessions/{id}:
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
get:
tags:
- LinkedIn — Sessions
summary: Get session metadata
operationId: linkedinGetSession
responses:
'200':
description: Session metadata (cookies never returned).
'404':
description: Not found.
delete:
tags:
- LinkedIn — Sessions
summary: Revoke session
operationId: linkedinRevokeSession
responses:
'200':
description: Revoked.
'404':
description: Not found or already revoked.
/linkedin/sessions/utilization:
get:
tags:
- LinkedIn — Sessions
summary: Per-session quota utilization
description: 'Returns each active LinkedIn session for the tenant with its
action counts in the trailing 24 hours and the remaining daily
budget per action type. Drives the "Today''s utilization" panel
in the dashboard and lets you decide when connecting another
LinkedIn account would add headroom.
Sessions used: search_signal (recurring polls), search_history
(one-shot lookups), profile_lookup (Pass-2 verification),
connection_request, send_message, post_like.'
operationId: linkedinSessionUtilization
responses:
'200':
content:
application/json:
schema:
type: object
properties:
ok:
type: boolean
budget:
type: object
additionalProperties:
type: integer
description: Per-action daily budget per session (the operator's safety cap).
sessions:
type: array
items:
$ref: '#/components/schemas/LinkedInSessionUtilization'
components:
schemas:
LinkedInSessionUtilization:
type: object
properties:
sessionId:
type: string
label:
type: string
nullable: true
status:
type: string
rateLimitedUntil:
type: string
format: date-time
nullable: true
rateLimitReason:
type: string
nullable: true
counts:
type: object
additionalProperties:
type: integer
description: 'Action → count of calls in the trailing 24h. Keys: search_signal, search_history, profile_lookup, connection_request, send_message, post_like.'
remaining:
type: object
additionalProperties:
type: integer
description: Action → remaining daily budget for this session.
securitySchemes:
apiKeyHeader:
type: apiKey
in: header
name: X-API-Key
description: 'Any of `sa_`, `tk_`, `wk_`, `ak_`, or `mam_` keys. Scope is enforced
per-endpoint — see individual operation descriptions.
'