How to send a fax with OpenClaw

How to send a fax with OpenClaw

OpenClaw is the open-source, local-first personal AI agent that uses messaging platforms as its main interface -- WhatsApp, Telegram, Slack, Discord, Signal, iMessage, SMS and twenty-odd others. It was created by Peter Steinberger (launched as Warelay, renamed Clawdbot, then Moltbot, then OpenClaw in January 2026) and is now stewarded by the OpenClaw Foundation. MIT licensed, at https://github.com/openclaw/openclaw.

It has a full MCP client, so connecting SendFax is one command -- and its messaging-first design makes "text your agent a PDF and a fax number" the most natural version of this whole guide set.


The flow

text
  you (WhatsApp/Telegram/iMessage): "fax this to +14155550123"  [+ PDF attached]
        |
  OpenClaw gateway (running on your own machine or VPS)
        |
  calls send_fax (MCP)  --or--  runs curl with its bash tool
        |
        +--> HTTP 402: the fax is NOT sent yet
        |      MCP path:  challenge headers only  (see "The payment gap")
        |      bash path: challenge + x-payment-deposit-* address
        |
  payment happens OUTSIDE OpenClaw:
        +-- it messages you the address + QR; you pay from your phone wallet
        +-- or it messages you a Stripe Checkout link
        |
  OpenClaw retries the identical send --> 202 { id, statusUrl }
        |
  messages you: "delivered" (it polls get_fax_status for you)

Which payment path am I in?

OpenClaw holds no wallet by default. It does ship bash, process, read, write and edit tools with configurable sandbox modes, and it runs on your own hardware -- so all three paths are open:

SituationPath
OpenClaw with a funded key you placed on the boxIt reads the deposit address and sends the transfer itself. Fully autonomous.
OpenClaw, no keyIt messages you the address, amount, and a QR code; you pay from your phone; it retries until 202.
MCP tools only, sandbox locked downIt cannot obtain the deposit address -- see below. Use the Stripe link.

The middle row is the one that fits the product: you are already in a chat with it, so a QR code in the thread is the shortest path from "fax this" to "paid".


Install OpenClaw

sh
curl -fsSL https://openclaw.ai/install.sh | bash
powershell
iwr -useb https://openclaw.ai/install.ps1 | iex

Or npm install -g openclaw@latest. Docs at https://docs.openclaw.ai.

Source: https://github.com/openclaw/openclaw. Versioning is CalVer; check npm view openclaw version for the current release rather than trusting a number written down in a guide.


Connect the MCP server

sh
openclaw mcp add sendfax \
  --url https://mcp.sendfax.sh/mcp \
  --transport streamable-http

The transport string is streamable-http -- not http, not sse. OpenClaw is explicit about it where other clients infer it.

In config, this lands in ~/.openclaw/openclaw.json (JSON5, so comments and trailing commas are allowed) under the nested mcp.servers key:

json
{
  "mcp": {
    "servers": {
      "sendfax": {
        "url": "https://mcp.sendfax.sh/mcp",
        "transport": "streamable-http",
        "requestTimeoutMs": 20000,
        "connectionTimeoutMs": 5000
      }
    }
  }
}

SendFax is authless, so no auth or headers block is needed. If your setup insists on OAuth, the server advertises OAuth discovery with Dynamic Client Registration and auto-approves instantly -- add "auth": "oauth".

Manage and debug:

sh
openclaw mcp list
openclaw mcp show sendfax --json
openclaw mcp doctor sendfax --probe
openclaw mcp tools
openclaw mcp reload

openclaw mcp doctor --probe is the fastest way to tell a connection problem from a configuration problem; use it before anything else in troubleshooting.

Source: https://docs.openclaw.ai/cli/mcp, with the same mcp.servers shape confirmed in https://docs.openclaw.ai/gateway/configuration-reference.

Config-key warning. Third-party blog posts and older community guides show a top-level mcpServers key and/or a separate ~/.openclaw/mcp.json file. The official docs use mcp.servers inside ~/.openclaw/openclaw.json, and there is an open issue in the repo about MCP config layout, so this has churned. Use the official shape above, and if it does not load, run openclaw mcp show sendfax --json to see what your build actually parsed.


Ask for a fax

From whatever channel you have connected:

text
fax the attached PDF to +14155550123
text
send this lease to my landlord's fax, +1 415 555 0123 -- what will it cost?

It should quote the price before spending: $0.05 per page on the wallet rail, so a three-page fax is $0.15.

Standing instructions

OpenClaw has a skills system -- directories of metadata plus instructions, in bundled, managed, and workspace scopes, with workspace taking priority, and a community registry at clawhub.ai. Configuration lives under a skills block in openclaw.json (allowBundled, load.extraDirs, and per-skill entries).

The SendFax skill is portable Markdown and works in any agent that reads skill files:

sh
curl -fsSL https://sendfax.sh/SKILL.md -o ./sendfax-skill.md

The exact workspace-skill directory layout for hand-authoring is not something this guide verified against your build -- check https://docs.openclaw.ai/gateway/configuration-reference for the skills block and where load.extraDirs points before dropping files in. If you would rather not, just tell it the policy in chat and ask it to remember:

text
Remember: for faxes use the sendfax MCP tools. SendFax only delivers to the US,
Canada, Mexico, and Saint Pierre & Miquelon. $0.05/page in USDC on Base, or
max($0.50, $0.05 x pages) by card. Always confirm the number and page count
first, and never spend over $1 without asking me. A 402 means the fax was NOT
sent -- the deposit address is in the x-payment-deposit-address header, which
the MCP tool does not forward, so read it by calling the HTTP API with curl.

The payment gap

The MCP send_fax tool does not surface the deposit address. On a 402 it forwards only www-authenticate, payment-required, and content-type (mcp/tools.ts:207-210). The x-payment-deposit-* headers carrying the per-request address that actually settles the payment (lib/payments/gate.ts:219-234) are dropped.

Over MCP alone, OpenClaw gets a challenge it cannot act on. It has a bash tool, and the headers are all present over plain HTTP:

sh
curl -sS -D headers.txt -o body.json -w '%{http_code}\n' \
  -F document=@lease.pdf -F to=+14155550123 \
  https://sendfax.sh/api/v1/faxes
# 402

grep -i '^x-payment-deposit' headers.txt
# x-payment-deposit-address: 0x157a57423869bf2666d3998b1319c2263ce852bb
# x-payment-deposit-amount: 0.050000
# x-payment-deposit-amount-micros: 50000
# x-payment-deposit-network: base
# x-payment-deposit-token: 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913

Because you are in a chat, the nicest version messages you a scannable code:

sh
# EIP-681 token transfer. Wallet support for this form varies, so always send
# the address and amount as text too.
qrencode -t ANSIUTF8 \
  "ethereum:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913@8453/transfer?address=$ADDR&uint256=$MICROS"

Then retry the identical request until it returns 202:

sh
until curl -sS -o body.json -w '%{http_code}' \
        -F document=@lease.pdf -F to=+14155550123 \
        https://sendfax.sh/api/v1/faxes | grep -q 202
do sleep 5; done
jq -r .statusUrl body.json

The full 20-line script, plus a Node/Bun version that pays from a local key with viem, is in ../curl.md and ../typescript.md. Handing OpenClaw those files is the fastest route to autonomy.

Or pay by card

sh
curl -sS -F document=@lease.pdf -F to=+14155550123 \
  https://sendfax.sh/api/v1/uploads
# -> { "id": "...", "pageCount": 1, "amountCents": 50 }

curl -sS -H 'content-type: application/json' -d '{"id":"<id>"}' \
  https://sendfax.sh/api/v1/checkout
# -> { "url": "https://checkout.stripe.com/..." }

It messages you the link, you tap it, the fax goes. Card pricing is max($0.50, $0.05 x pages) because Stripe will not charge under 50 cents (lib/domain/pricing.ts:16, 23).


How payment actually works

No account, no API key -- payment is the credential. Full mechanics in ../usdc.md:

  1. The 402 carries a fresh deposit address, the exact amount in USDC base units, and the network (base).
  2. Send that exact amount of USDC on Base as a plain ERC-20 transfer. No signature, no approval, no facilitator.
  3. Retry the identical request until 202.

Two rules that bite agents:

  • Exact amount. Use x-payment-deposit-amount-micros ("50000"), never the decimal display string through a float (lib/payments/deposit.ts:16-18).
  • Same request, ~15 minutes. Identical retries reuse the same PaymentIntent and are not a second charge (lib/payments/deposit.ts:50, 94-108).

A word about keys on a messaging agent

OpenClaw is reachable from a chat app. That is its charm and its risk profile: anyone who can send messages to that account can ask it to spend. If you put a funded key on the box, then

  • fund it with a small, refillable amount rather than your main wallet;
  • enforce the ceiling in code, not in the prompt -- check the network, the token, and the amount before signing (the check is in ../typescript.md);
  • keep the sandbox mode as tight as the task allows;
  • prefer the QR-to-your-phone flow for anything you would not want automated.

Prompt-level spending limits are a preference, not a control.


Troubleshooting

openclaw mcp doctor sendfax --probe fails. Confirm the URL includes the /mcp path and the transport is streamable-http. SendFax also serves SSE at https://mcp.sendfax.sh/sse if Streamable HTTP is blocked on your network.

The server is in the config but no tools appear. Run openclaw mcp reload, or restart the gateway. Check openclaw mcp tools and any toolFilter you set -- an include list that does not match drops everything.

openclaw mcp show sendfax --json prints nothing. Your build probably did not parse the config key you used. See the config-key warning above.

An OAuth browser flow starts. Expected on clients that act on OAuth discovery. SendFax never requires a token; it advertises OAuth with Dynamic Client Registration and auto-approves so OAuth-mandating clients can connect. The token carries no identity and tools stay payment-gated.

It says "payment required" and stops. The fax has not been sent. See the payment gap.

"Unsupported destination" (422). SendFax delivers to the United States, Canada, Mexico, and Saint Pierre & Miquelon (lib/domain/fax.ts:70-78). Rejected before payment, so nothing was charged.

"Invalid PDF" (422). Encrypted or password-protected PDFs are rejected -- common for documents forwarded from banking apps, which is exactly the kind of thing you fax.

413. Over the 20 MB limit. Photos-as-PDF from a phone hit this fast.

The fax says failed. Read failureReason. Destination-related failures on a Stripe-settled payment -- which includes every deposit-rail payment -- are refunded automatically to the sending wallet (lib/payments/refund.ts:1-30).


Try it without spending anything

sh
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/faxes

Point OpenClaw at http://localhost:3001 and the whole flow works with no wallet. The 402 without the bypass header is genuine.


Cross-references