Sessions & Cookies

Sessions & Cookies

A Voyager session is an authenticated LinkedIn connection backed by browser cookies. You provide cookies from your real LinkedIn browser session; Voyager maintains them in a headless Chromium instance that makes API calls on your behalf.

Syncing Cookies

Use POST /api/session to sync your LinkedIn cookies:

$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_aSalesNav authentication tokenOnly for SalesNav
li_ep_auth_contextEnterprise profile contextOnly for SalesNav

Optional Parameters

ParameterTypeDescription
userAgentstringBrowser User-Agent string. Strongly recommended — sessions without it may fail LinkedIn’s fingerprint checks
proxyCountrystringTwo-letter country code (e.g., "gb", "us") for geo-routing through country-specific proxies
linkedInVanityNamestringLinkedIn vanity name for this user, used for self-profile lookups

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

Session Validation

After syncing, Voyager automatically probes LinkedIn (12-second timeout) and returns the validation result:

1{
2 "success": true,
3 "sessionValid": true,
4 "message": "Session synced and validated"
5}

If the probe fails:

1{
2 "success": true,
3 "sessionValid": false,
4 "sessionError": "LinkedIn returned 401 -- cookies may be expired"
5}

Checking Session Health

Use GET /api/session/capabilities for a comprehensive session check:

$curl -H "Authorization: Bearer $KEY" \
> -H "X-User-Id: $USER" \
> "$BASE/api/session/capabilities"
1{
2 "success": true,
3 "linkedInSessionValid": true,
4 "salesNavSessionValid": true,
5 "canSend": true,
6 "usage": {
7 "messagesSent": 12,
8 "connectionsRequested": 3,
9 "profilesViewed": 47,
10 "accountAgeDays": 1825,
11 "secondsSinceLastAction": 120
12 }
13}

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

You can re-sync cookies at any time without interrupting service. Voyager:

  1. Clears existing cookies from the browser context
  2. Sets the new cookies
  3. Validates the new session
  4. Continues using the same browser page (no restart needed)

If a phantom or action is currently running, the cookie swap waits until it completes to avoid mid-operation disruption.

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 address changes significantly

Detection

Voyager detects expired sessions in three ways:

  1. On API call — Returns 401 SESSION_EXPIRED when LinkedIn rejects the session
  2. Background polling — PollService checks messages/invitations every 5 minutes. If all checks fail with session errors, it emits a session_expired webhook and pauses polling for that user
  3. On syncPOST /api/session validates the new cookies immediately

Recovery

When a session expires:

  1. Log in to LinkedIn in your browser
  2. Use the Chrome extension to export fresh cookies (or manually extract li_at and JSESSIONID)
  3. Call POST /api/session with the new cookies
  4. Voyager validates and resumes normal operation
$# Quick re-sync
$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": "FRESH_TOKEN", "domain": ".linkedin.com"},
> {"name": "JSESSIONID", "value": "FRESH_JSESSION", "domain": ".linkedin.com"}
> ],
> "userAgent": "Mozilla/5.0 ..."
> }'

Export Current Cookies

To export the cookies Voyager currently holds (useful for debugging):

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

Browser Architecture

Voyager uses per-country browser pooling. Each unique country code gets its own Chromium process; users in the same country share the process via isolated BrowserContexts (~70MB each vs. 300MB per process). The browser sits on /robots.txt (no JavaScript, no detection) and makes LinkedIn API calls via same-origin fetch().

The main API page is never navigated away from /robots.txt. Operations that need DOM interaction (rare fallback paths) use disposable temp pages that are closed after use.