---
title: "Unlink device from account / sign out in the extension (device-signed)"
method: POST
path: "/api/v2/account/unlink"
tags: ["v2 Account"]
---

# Unlink device from account / sign out in the extension (device-signed)

`POST /api/v2/account/unlink`

## Request

- Base URL: `https://pdf2md.dev`
- URL: `https://pdf2md.dev/api/v2/account/unlink`
- Auth: HTTP bearer

Device-signed (ES256). Removes the link and returns the device's active jobs to anonymous mode (owner_type=device, tier=anonymous) without data loss. Idempotent (for an already anonymous device — linked=false).

## Response `200`

Unlinked

- V2AccountStatus: EP-023-T1: device-to-account link status (device-signed GET/POST /api/v2/account*).
  - `linked` boolean, required: true — the device is linked to an account
  - `account_id` string, uuid
  - `email` string
  - `display_name` string, nullable
  - `avatar_url` string, nullable
  - `tier` 'anonymous' | 'free' | 'lifetime' | 'builder' | 'pro' | 'business' | 'enterprise': EP-069: `lifetime` is the one-time unlock, ranked BETWEEN free and builder — it raises the interactive limits and carries 600 pages/month, but grants no API keys, hosted MCP, webhooks or batch, so a subscription is genuinely more and its holder may still buy one.
  - `affected_jobs` integer: How many jobs were re-attached/returned on link/unlink
  - `limits` V2Limits: Live actor entitlements (resolved from the subscription, §7.3)
    - `tier` 'anonymous' | 'free' | 'lifetime' | 'builder' | 'pro' | 'business' | 'enterprise': EP-069: `lifetime` is the one-time unlock, ranked BETWEEN free and builder — it raises the interactive limits and carries 600 pages/month, but grants no API keys, hosted MCP, webhooks or batch, so a subscription is genuinely more and its holder may still buy one.
    - `max_active_slots` integer
    - `max_concurrent_conversions` integer
    - `max_file_size_bytes` integer
    - `processing_soft_budget_sec` integer: The tier's visible "time limit": the engine runs up to it and returns a partial result + the tier disclaimer (truncated=true), rather than a rejection. Invariant: < processing_timeout_sec. Shown on pricing and in the disclaimer.
    - `processing_timeout_sec` integer: The worker's internal hard-backstop (= soft + ~5 min); not a marketing limit.
    - `ready_ttl_sec` integer
    - `priority_base` integer: §7.7, step-20 scale (anonymous=100)
    - `pool_eligibility` 'free_only' | 'free_and_paid'
    - `monthly_pages_soft_limit` integer, nullable: EP-069: the monthly page allowance. SOFT on subscription tiers (past it the actor drops to the free pool for the rest of the cycle, never refused). HARD on anonymous (150), free (400) and lifetime (600) — those are refused with 409 quota_exceeded. Resolved for THIS actor: an API-key caller does not get the lifetime unlock (EP-070-T6).
    - `monthly_pages_used` integer: EP-070-T7: pages converted this UTC calendar month. Present here as well as on /api/v2/limits because the extension cannot reach that path (it needs an API key or a session cookie, and a cross-site extension fetch has neither).
    - `webhooks_enabled` boolean
    - `slots_in_use` integer: slots occupied right now
    - `rate_limits` object: per-class: api_requests_per_min, job_creates_per_min, upload_bytes_per_hour, download_requests_per_min, mcp_tool_calls_per_min
  - `slots_in_use` integer, nullable: EP-025-T2 (additive): active jobs currently owned by the linked account.
  - `subscription` object, nullable: EP-025-T5 (additive): subscription status/period for the extension Account & Plan section. Omitted when the device is anonymous and has neither a subscription nor a live lifetime unlock.
    - `status` 'free' | 'trialing' | 'active' | 'past_due' | 'canceled' | 'paused'
    - `cancel_at_period_end` boolean
    - `current_period_end` string, date-time, nullable
    - `trial_end` string, date-time, nullable
    - `lifetime` boolean: EP-069-T13: the account holds a LIVE one-time unlock. Server-computed so four client surfaces do not each infer it and disagree.
    - `manageable` boolean: EP-069-T13/EP-070-T5: there is something the provider portal can change or cancel. FALSE for an account whose only purchase is the unlock — it has no schedule and cannot lapse, so offering "cancel" there is a support ticket at best. Built by ONE server-side builder shared by /me and /account: the two used to disagree, and the endpoint the extension reads never set it, so every recurring subscriber lost their Manage billing button.

## Other responses

- `401`: Missing/invalid API key or session

---

[API](https://skmtc.dev/pdf2md/apis/pdf-to-markdown-api-facade.md) · [All operations](https://skmtc.dev/pdf2md/apis/pdf-to-markdown-api-facade/llms.txt) · [OpenAPI document](https://skmtc.dev/pdf2md/apis/pdf-to-markdown-api-facade/revisions/4636782fb8b6?raw)
