How to send a fax with Claude
How to send a fax with Claude
Claude Code, Claude Desktop, and claude.ai can all reach SendFax through the remote MCP server at https://mcp.sendfax.sh/mcp. Claude Code can additionally install the fax skill, which teaches it the HTTP flow directly -- worth having, because of the payment gap described below.
- Guide index: ../README.md | protocol: ../usdc.md
- Docs: https://sendfax.sh/docs | contract: https://sendfax.sh/openapi.json
The flow
you: "fax this PDF to +1 415 555 0123"
|
Claude calls send_fax (MCP) --or-- runs curl (skill + shell)
|
+--> HTTP 402: the fax is NOT sent yet
| MCP path: challenge headers only (see "The payment gap")
| curl path: challenge + x-payment-deposit-* address
|
payment happens OUTSIDE Claude:
+-- you or Claude send exact USDC on Base to the deposit address
+-- or you pay by card via Stripe Checkout
|
Claude retries the identical send --> 202 { id, statusUrl }
|
Claude calls get_fax_status(id) --> queued -> sending -> deliveredWhich payment path am I in?
Claude has no wallet. Nothing in Claude Code, Claude Desktop, or claude.ai holds USDC or signs transactions. So the question is what Claude can reach:
| Situation | Path |
|---|---|
| Claude Code with shell access and a funded key you control | Claude runs the transfer itself (a viem one-liner, or cast send). Fully autonomous. |
| Claude Code, no key | Claude prints the deposit address and amount; you pay from any wallet; Claude retries. |
| claude.ai / Desktop (MCP only) | Claude cannot obtain the deposit address -- see below. Use the Stripe link, or move to Claude Code. |
Connect the MCP server
Claude Code
# Streamable HTTP (recommended)
claude mcp add --transport http sendfax https://mcp.sendfax.sh/mcp
# Available to every project, not just this one
claude mcp add --transport http --scope user sendfax https://mcp.sendfax.sh/mcp
# Shared with your team via .mcp.json at the repo root
claude mcp add --transport http --scope project sendfax https://mcp.sendfax.sh/mcpVerify and manage:
claude mcp list
claude mcp get sendfax
claude mcp remove sendfax
# In-session, to see connection state and tools:
/mcpScopes: local (default -- you, this project only), project (shared via .mcp.json), user (you, every project). local and user are stored in ~/.claude.json; project writes .mcp.json in the repo, and teammates are prompted to approve it before it loads.
If you hand-write .mcp.json, the type field is required for a remote server -- an entry with a url and no type is read as a stdio server and fails:
{
"mcpServers": {
"sendfax": { "type": "http", "url": "https://mcp.sendfax.sh/mcp" }
}
}SSE is available as a fallback if Streamable HTTP is blocked on your network:
claude mcp add --transport sse sendfax https://mcp.sendfax.sh/sseVerified against Claude Code 2.1.220 and https://code.claude.com/docs/en/mcp.
claude.ai and Claude Desktop
Both use the same hosted connector infrastructure:
- Settings > Connectors (the help centre labels this Customize > Connectors; the UI naming has shifted, so check both).
- Add custom connector.
- URL:
https://mcp.sendfax.sh/mcp - Add. No client id, no secret -- the server is authless.
On Team and Enterprise plans an owner adds it under Admin settings > Connectors first, then members connect it from their own Settings. Free accounts are limited to one custom connector. Enable it per conversation from the + button.
claude_desktop_config.json is not the right mechanism here -- it is for local stdio servers, and servers configured there are not available in claude.ai.
Sources: https://claude.com/docs/connectors/custom/remote-mcp and https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp. Authless servers are explicitly supported: the auth reference lists type none -- "No authentication (authless server)" -- as Supported.
One operational note: Claude connects from Anthropic's cloud, not from your device, on every surface including Desktop and mobile.
Install the skill (Claude Code)
The MCP server gives Claude tools. The skill gives it the procedure -- when to fax, what the 402 means, how to settle it over plain HTTP. With shell access, the skill alone is enough; you do not need the MCP server at all.
The zero-dependency install is a file drop, which is the officially documented layout (~/.claude/skills/<name>/SKILL.md):
mkdir -p ~/.claude/skills/sendfax
curl -fsSL https://sendfax.sh/SKILL.md -o ~/.claude/skills/sendfax/SKILL.mdClaude Code picks up new skill directories live; if you had no ~/.claude/skills directory before, restart the session once.
There is also a third-party installer that automates the same file drop:
# `npx skills` is vercel-labs/skills -- MIT, not an Anthropic product.
npx skills add DanielSinclair/sendfax.sh --skill sendfax -g
# The TypeScript SDK skill, for Claude writing code against the `sendfax` package:
npx skills add DanielSinclair/sendfax --skill sendfax-sdk -gIt writes into the same ~/.claude/skills/ path documented above. Use whichever you prefer; the curl version has no dependencies and no third party.
Skill layout: https://code.claude.com/docs/en/skills. npx skills: https://github.com/vercel-labs/skills.
Ask for a fax
Fax ~/Documents/signed-lease.pdf to +14155550123.I need to send this to my doctor's office at +1 415 555 0123 -- it's a
3-page PDF at ./referral.pdf. What will it cost and can you send it?Claude should tell you the price before spending anything: $0.05 per page on the wallet rail, so a three-page fax is $0.15. If it does not, ask.
A good standing instruction, in CLAUDE.md or as a project memory:
## Faxing
Use the sendfax tools for fax requests. Always confirm the destination number
and the page count with me before sending. Never spend more than $1 per fax
without asking.The payment gap (read this before you rely on MCP)
The MCP send_fax tool does not surface the deposit address. When the API answers 402, the tool forwards only three headers -- www-authenticate, payment-required, and content-type (mcp/tools.ts:207-210). The x-payment-deposit-* headers, which carry the per-request address that actually settles the payment (lib/payments/gate.ts:219-234), are dropped.
So over MCP alone, Claude receives a well-formed payment challenge it cannot act on. get_payment_instructions explains the flow correctly in prose, but the address it tells you to pay is minted per request and only appears in those dropped headers.
Three ways through it:
1. Let Claude use the HTTP API directly (Claude Code). The headers are all there over plain HTTP. With the skill installed and shell access, this is what Claude will do anyway:
curl -sS -D headers.txt -o body.json -w '%{http_code}\n' \
-F document=@referral.pdf -F to=+14155550123 \
https://sendfax.sh/api/v1/faxes
grep -i '^x-payment-deposit' headers.txtClaude reads the address and the exact amount, you (or it) send the USDC, and it re-runs the identical curl until the status code is 202. Full walkthrough: ../curl.md.
2. Pay by card instead. No wallet anywhere:
curl -sS -F document=@referral.pdf -F to=+14155550123 \
https://sendfax.sh/api/v1/uploads
# -> { "id": "...", "pageCount": 3, "amountCents": 50 }
curl -sS -H 'content-type: application/json' -d '{"id":"<id>"}' \
https://sendfax.sh/api/v1/checkout
# -> { "url": "https://checkout.stripe.com/..." }Claude prints the URL, you pay in a browser, and the fax sends. Note the price: max($0.50, $0.05 x pages) on the card rail, because Stripe will not charge under 50 cents (lib/domain/pricing.ts:16, 23). A one-page fax is $0.05 by wallet and $0.50 by card.
3. Send a human to https://sendfax.sh/human. Drag, drop, pay, done.
How payment actually works
There is no account and no API key -- payment is the credential. The full mechanics are in ../usdc.md; the short form:
- The 402 carries a fresh deposit address, the exact amount in USDC base units, and the network (
base). - You send that exact amount of USDC on Base to that address as a plain ERC-20 transfer. No signature, no approval, no facilitator.
- You retry the identical request. The server polls Stripe, sees the transfer, and returns
202.
Two rules that bite agents specifically:
- Exact amount.
x-payment-deposit-amount-microsis an integer string ("50000"= $0.05). Use it directly; never parse"0.050000"through a float. A mismatched amount cannot be auto-matched (lib/payments/deposit.ts:16-18). - Same request, ~15 minutes. Identical retries reuse the same PaymentIntent for 15 minutes and are not a second charge (
lib/payments/deposit.ts:50, 94-108). Change the PDF or the number and you mint a new address. Go quiet past the window and your transfer is stranded.
Troubleshooting
/mcp shows sendfax as failed or disconnected. Check the URL is https://mcp.sendfax.sh/mcp with the /mcp path. Try claude mcp remove sendfax and re-add with --transport http explicitly. If your network blocks streaming responses, try the SSE endpoint.
Project-scoped server shows "Pending approval". .mcp.json servers require interactive approval -- run claude in that directory and approve it.
A browser window opens asking to authorize. Expected on clients that insist on OAuth. The server is authless, but it advertises OAuth discovery and Dynamic Client Registration and auto-approves instantly, so a client that refuses to connect without OAuth still works. The token carries no identity and every tool call is still payment-gated.
Claude says "payment required" and stops. That is the design -- the fax has not been sent. See the payment gap above. Over MCP alone Claude cannot get the deposit address; give it shell access, or pay by card.
"Unsupported destination" (422). SendFax delivers to the United States, Canada, Mexico, and Saint Pierre & Miquelon (lib/domain/fax.ts:70-78). This is checked before any payment, so nothing was charged.
"Invalid PDF" (422). Encrypted or password-protected PDFs are rejected. Bank and government exports often are. Remove the protection and retry.
413. The PDF is over 20 MB.
The fax says failed. Check failureReason -- busy, no answer, not a fax line. If the failure is destination-related and you paid through Stripe or the deposit rail, the refund is automatic and returns USDC to your sending wallet (lib/payments/refund.ts:1-30).
Tool output looks truncated. Claude Code caps MCP tool results (configurable via MAX_MCP_OUTPUT_TOKENS); claude.ai caps around 150,000 characters. Fax responses are small, so this should not bite -- but a 402 challenge body plus headers is the largest thing this server returns.
Try it without spending anything
Run SendFax locally in mock mode and point Claude at it. The 402 is genuine; the bypass header is the only thing that is not.
bun run dev # http://localhost:3001, FAX_MODE=mock
curl -sS -H 'x-mock-payment: 1' \
-F document=@test.pdf -F to=+14155550123 \
http://localhost:3001/api/v1/faxesCross-references
- ../README.md -- the chooser and the decision matrix
- ../curl.md -- the curl reference implementation Claude will use
- ../typescript.md -- the Node/Bun version that pays from a local key
- ../usdc.md -- how the USDC settlement actually works
- codex.md | hermes.md | openclaw.md
- https://sendfax.sh/SKILL.md | https://sendfax.sh/docs