Product2026-09-223 min read

Same Operation, Four Clients: Browser, curl, MCP, pdfx

Same PDF123 merge via four clients: the browser form, curl to /api/v1/general/merge-pdfs, MCP at /mcp, and pdfx local or --cloud against your API base.

PDF123 · Updated 2026-09-22

Four clients look like four products. They share one catalog. Take Merge as the concrete job: combine PDFs in upload order without rasterizing pages into images. The same pattern holds for every other catalog tool (compress, OCR, convert, and the rest): one op id, four ways to call it.

Browser

Open /merge, add files in the order you want, Process, download. No account for catalog tools. The page’s “Call this from code” block builds curl from the same fields the form sends, so the UI and the HTTP contract stay aligned.

That block is not marketing copy. It is generated from the tool definition the portal already uses for the form, which is why a parameter rename shows up in both places together. If the form accepts optional sortType=byFileName, the curl example can carry the same field.

curl / REST

curl -fsS -X POST "$API_BASE/api/v1/general/merge-pdfs" \
  -F "[email protected]" \
  -F "[email protected]" \
  -o merged.pdf

Anonymous catalog calls need no key. Keys from Developers give automation a stable identity (and self-hosted servers a gate). OpenAPI is at /v1/openapi.json.

For multi-step jobs, POST /api/v1/pipeline accepts ordered ops in a steps field (for example merge, then watermark, then compress). Send Idempotency-Key when retries must not double-run the work; a successful replay can return with Idempotency-Replayed. Failures use application/problem+json with stable codes such as rate_limited and bad_request (Developers errors).

Hosted responses also advertise X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset; HTTP 429 includes Retry-After. Treat those headers as the live budget, not a number memorized from a blog post (anonymous rate limiting).

MCP

Agents that speak Model Context Protocol connect to /mcp, discover merge as a tool, and call it with the same API-key story as REST. Docs: Developers MCP.

MCP is not a second catalog. It is a discovery and invocation protocol over the same operations OpenAPI lists. If an op is missing from MCP, that is a server bug, not a separate product roadmap. Pair MCP with /llms.txt when you want a prose index of tools before the client connects.

pdfx CLI

Local pdf-core:

pdfx merge a.pdf b.pdf -o merged.pdf

Or the same server the portal uses:

pdfx --cloud --api-base "$API_BASE" --api-key "$KEY" merge a.pdf b.pdf -o merged.pdf

Local mode never uploads; cloud mode hits your base URL with the same multipart shape as curl. The coding-agent skill under dist/skills/pdf-toolbox/SKILL.md documents the same merge and pipeline shapes so agents do not invent a second OpenAPI.

OCR through any of these clients still returns Markdown from /api/v1/misc/ocr-pdf, not a hidden text layer. That fact is part of the shared contract: changing the return type in one client without the others would break the “same op” promise. Compress stays on /api/v1/misc/compress-pdf for stream recompression; it is a different op from merge, reachable the same four ways.

Why sameness matters

If browser merge and API merge ever diverged, automations would silently regress while the demo page still looked fine. One op, four handles, is the point. The portal is a convenience client, not a second implementation of merge, compress, or OCR.

That is also why there is no desktop fork: a fifth UI tree would recreate the drift problem under a different binary name. Broader framing: Built for AI agents, not just browsers. For the desktop decision, see Why we skipped the desktop app. For standing the API up on your network, see Self-host.

Open tool
Process in the browser — no watermark, files removed after the job.
Open tool