For AI agents: a documentation index is available at the root level at /llms.txt and /llms-full.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.
DashboardGet API Key
GuidesAPI ReferenceCommon Patterns
  • Getting Started
    • Welcome
    • Introduction
    • Quickstart
    • Authentication
  • Core Concepts
    • Sessions & Cookies
    • Profiles
    • Messaging
    • Posts & Engagement
    • Connections
    • Search
    • SalesNav
    • SalesNav Filters
  • Playbooks
    • Multi-Step Workflows
    • Query Patterns
  • Workflow Guides
    • Lead Discovery
    • Warm Outreach
    • Content Intelligence
    • Company Research
    • Job Market Intelligence
    • Webhooks
  • Integration
    • MCP Server
    • WebSocket Relay
DashboardGet API Key
On this page
  • Sessions & Cookies
  • Syncing Cookies
  • Required Cookies
  • Optional Parameters
  • Session Validation
  • Checking Session Health
  • Session Lifecycle
  • Auto-pause discipline
  • Manual reactivation
  • Cookie Hot-Swap
  • Session Expiry
  • Detection paths
  • Recovery
  • Export Current Cookies
  • Transport Architecture
Core Concepts

Sessions & Cookies

Was this page helpful?
Previous

Profiles

Next
Built with

Sessions & Cookies

A Voyager session is an authenticated LinkedIn connection backed by your real browser’s cookies. You provide the cookies once; Voyager forwards them on every call, either through the Chrome extension relay (your real Chrome, real IP) or directly from the server over a residential proxy.

Self-healing sessions with the Chrome extension. The extension auto-syncs cookies whenever they change in Chrome, and the server asks the extension to re-sync when a health check fails. Manual intervention is only needed if LinkedIn actually logs you out (password re-entry, suspicious-activity challenge).

Syncing Cookies

Use POST /api/session to sync cookies. If you’re using the Chrome extension, you never have to do this by hand.

$curl -X POST "$BASE/api/session" \
> -H "Authorization: Bearer $KEY" \
> -H "X-User-Id: $USER" \
> -H "Content-Type: application/json" \
> -d '{
> "cookies": [
> {"name": "li_at", "value": "AQEDAx...", "domain": ".linkedin.com"},
> {"name": "JSESSIONID", "value": "\"ajax:12345\"", "domain": ".linkedin.com"}
> ],
> "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36"
> }'

Required Cookies

CookiePurposeRequired
li_atLinkedIn authentication tokenYes
JSESSIONIDLinkedIn session ID (used as CSRF token)Yes
li_aSales Navigator authentication tokenOnly for SalesNav
li_ep_auth_contextEnterprise profile contextOnly for SalesNav

Optional Parameters

ParameterTypeDescription
userAgentstringBrowser User-Agent. Strongly recommended — sessions without it may fail LinkedIn’s fingerprint checks
proxyCountrystringTwo-letter country code ("gb", "us") to route direct-HTTP calls through a country-specific residential proxy
linkedInVanityNamestringVanity name for this user, used for self-profile lookups and multi-account routing

Always include userAgent when syncing cookies. LinkedIn compares the User-Agent from the cookie’s origin browser against the requesting browser. Mismatches can trigger session invalidation or redirect loops.

Session Validation

After syncing, Voyager probes LinkedIn (12s non-blocking timeout) and returns the result:

1{
2 "success": true,
3 "statusCode": 200,
4 "data": {
5 "userId": "charis",
6 "cookieCount": 4,
7 "sessionValid": true,
8 "sessionError": null
9 },
10 "errors": null
11}

If the probe returns a failure, sessionValid is false and sessionError describes why. The sync itself still succeeds — validation just tells you whether you can expect calls to work.

Checking Session Health

Use GET /api/session/capabilities for a full status readout:

$curl -H "Authorization: Bearer $KEY" -H "X-User-Id: $USER" \
> "$BASE/api/session/capabilities"
1{
2 "success": true,
3 "data": {
4 "linkedInSessionValid": true,
5 "salesNavSessionValid": true,
6 "canSend": true,
7 "canConnect": true,
8 "canGetReliableConnectionDegree": true,
9 "relayConnected": true,
10 "usage": {
11 "messagesSent": 12,
12 "connectionsRequested": 3,
13 "profilesViewed": 47,
14 "accountAgeDays": 1825,
15 "secondsSinceLastAction": 120
16 },
17 "warnings": []
18 }
19}

GET /api/health/tenant is a lighter health check that doesn’t probe LinkedIn. Use it for frequent polling. Use /api/session/capabilities when you need to confirm the session is actually working.

Session Lifecycle

Auto-pause discipline

Voyager’s background session watchdog checks /me periodically. A failed check only auto-pauses the user if the response is a clear auth signal — HTTP 401 or 403. Transient 5xx, network errors, and "Relay connection closed" are logged but never pause a user. This keeps your users active across Railway redeploys, LinkedIn upstream blips, and Chrome extension reconnects.

The RiskMonitor tracks real outbound traffic and auto-pauses on sustained trouble signals:

SignalThresholdPause duration
HTTP 429 bursts3 within 10 min1h
Checkpoint redirect1 (any)24h
Authwall redirect1 (any)15m
CSRF failures5 within 5 min10m

Paused users get HTTP 423 on every request until the cooldown expires or you manually unpause.

Manual reactivation

If a user is paused (e.g. the RiskMonitor tripped, or checkSession observed a real 401), you have two ways to bring them back:

$# Tenant-scoped: "the session should be fine now, just un-pause"
$curl -X POST "$BASE/api/users/$USER/reactivate" \
> -H "Authorization: Bearer $KEY"
$
$# RiskMonitor-scoped: clears the risk side of the pause
$curl -X POST "$BASE/api/risk/$USER/unpause" \
> -H "Authorization: Bearer $KEY"

Or simpler: open the Chrome extension and click Export — a successful POST /api/session auto-flips user.status = 'active'.

Cookie Hot-Swap

Re-sync cookies at any time without interrupting in-flight work. Voyager:

  1. Replaces stored cookies atomically
  2. Invalidates per-request caches that depend on the old session (CSRF, mailbox URN)
  3. Probes the new session
  4. Resumes using the same server process — no restart

If an action is running when the swap lands, it completes against the old cookies, and the next action picks up the new ones.

Session Expiry

LinkedIn cookies typically last 1–3 months but can expire earlier if:

  • LinkedIn detects suspicious activity
  • You log out from the browser
  • LinkedIn forces a password reset
  • The IP changes significantly without the extension relaying

Detection paths

  1. On an outbound call — LinkedIn returns 401/403 → the endpoint surfaces SESSION_EXPIRED and the RiskMonitor tracks the signal.
  2. Background polling — PollService checks messages/invitations periodically; repeated failures emit a session_expired webhook.
  3. On sync — POST /api/session validates the new cookies immediately and reports sessionValid.

Recovery

  1. Log in to LinkedIn in your browser (if you were logged out).
  2. Click Export in the Voyager Chrome extension — that’s it.
  3. If you’re managing cookies manually, POST them to /api/session as shown at the top of this page.

Export Current Cookies

For debugging, you can read back the cookies Voyager currently holds:

$curl -H "Authorization: Bearer $KEY" -H "X-User-Id: $USER" \
> "$BASE/api/cookies/export"

Transport Architecture

The entire REST surface is pure HTTP — no headless browser anywhere on the server (Puppeteer was removed 2026-04-22). Every outbound LinkedIn call goes through one of two tiers:

  1. Chrome extension relay (preferred) — the request is forwarded over a WebSocket to the user’s real Chrome, which issues the call from the real browser with real cookies and a real residential IP. Result is streamed back.
  2. Direct HTTP + residential proxy (fallback) — the server issues the call itself using the stored cookies and an IPRoyal residential proxy, identified by the user’s proxyCountry if set.

Tier 1 is automatic when the extension is connected. Tier 2 is automatic when it isn’t. Callers never need to choose — and per-endpoint retry logic already handles brief transitions between the two (e.g. a Railway redeploy that drops the WebSocket).