# 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.

- Guide index: [../README.md](/guides/index.md) | protocol: [../usdc.md](/guides/usdc.md)
- Docs: <https://sendfax.sh/docs> | contract: <https://sendfax.sh/openapi.json>

---

## The flow

```
  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:

| Situation | Path |
|---|---|
| OpenClaw with a funded key you placed on the box | It reads the deposit address and sends the transfer itself. Fully autonomous. |
| OpenClaw, no key | It messages you the address, amount, and a QR code; you pay from your phone; it retries until `202`. |
| MCP tools only, sandbox locked down | It 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:

```
fax the attached PDF to +14155550123
```

```
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:*

```
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.
```

---

<a name="the-payment-gap"></a>

## 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](/guides/curl.md) and [../typescript.md](/guides/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](/guides/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](/guides/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](#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

- [../README.md](/guides/index.md) -- the chooser and the decision matrix
- [../curl.md](/guides/curl.md) -- the curl implementation OpenClaw can run
- [../typescript.md](/guides/typescript.md) -- the Node/Bun version that pays from a local key
- [../usdc.md](/guides/usdc.md) -- how USDC settlement works
- [claude.md](/guides/agents/claude.md) | [codex.md](/guides/agents/codex.md) | [hermes.md](/guides/agents/hermes.md)
- <https://sendfax.sh/quickstart.md> | <https://sendfax.sh/docs>
