# Send a Fax (Fax for Agents) (`faxforagents/apify-actor`) Actor

Send a PDF or text document as a fax from any Apify run, schedule or integration. Prepare, preview, quote, then send with an approval step. Delivery status lands in the run dataset. Powered by Fax for Agents, the fax API for AI agents.

- **URL**: https://apify.com/faxforagents/apify-actor.md
- **Developed by:** [Fax for Agents](https://apify.com/faxforagents) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-usage

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

<p align="center">
  <a href="https://faxforagents.com"><img src="https://faxforagents.com/brand/logo-512.png" width="88" alt="Fax for Agents"></a>
</p>

<h1 align="center">Send a fax (Fax for Agents)</h1>

<p align="center">An Apify Actor that sends one fax through <a href="https://faxforagents.com">Fax for Agents</a> and waits for the carrier's outcome. A PDF or image URL, text or Markdown to a number.</p>

<p align="center">
  <a href="https://faxforagents.com">Website</a> ·
  <a href="https://faxforagents.com/docs/quickstart/">Quickstart</a> ·
  <a href="https://faxforagents.com/docs/api/">API reference</a> ·
  <a href="https://faxforagents.com/docs/sdks/">SDKs</a> ·
  <a href="https://faxforagents.com/mcp/">MCP</a> ·
  <a href="https://app.faxforagents.com">Dashboard</a> ·
  <a href="https://faxforagents.com/pricing/">Pricing</a> ·
  <a href="https://faxforagents.com/changelog/">Changelog</a> ·
  <a href="https://gitlab.com/faxforagents/apify-actor">GitLab</a>
</p>

<div align="center">

[![ci](https://img.shields.io/github/actions/workflow/status/faxforagents/apify-actor/ci.yml?branch=main\&label=ci)](https://github.com/faxforagents/apify-actor/actions/workflows/ci.yml) [![MIT](https://img.shields.io/badge/licence-MIT-1f6feb)](https://github.com/faxforagents/apify-actor/blob/main/LICENSE) [![Apify Actor](https://img.shields.io/badge/Apify-Actor-00c3a0)](https://apify.com/store)

</div>

Fax for Agents is a fax API, a hosted MCP server and SDKs built so AI agents can send and receive
faxes. It is made by JSB Holdings LLC and built by [Synapse](https://synapsereality.io/).

The Actor runs on Node 20 with the Apify SDK and nothing else. It prepares the document and quotes it
to the number. It queues the fax under an idempotency key tied to the Apify run. Then it polls until
the carrier reports `delivered`, `partial`, `failed` or `unknown`, and writes one record to the
dataset and to the `OUTPUT` key.

### Input

| Field | Required | Default | Does |
|---|---|---|---|
| `apiKey` | no | | The Fax for Agents API key, stored as a secret input. Empty means check mode: the run probes the API, sends nothing and writes one `mode: check` record |
| `to` | yes | | Destination in E.164, for example `+12025550123` |
| `fileUrl` | one of three | | URL of a PDF, PNG, JPG or TIFF, up to 25 MB |
| `text` | one of three | | Plain text, rendered to a letter PDF |
| `markdown` | one of three | | Markdown, rendered to PDF |
| `coverSubject`, `coverMessage`, `coverToName`, `coverFromName` | no | | Fields of an optional cover sheet. Any of them adds a billable cover page |
| `approvalId` | no | | An approved request bound to this send, when the account needs a human approval for live sends |
| `clientReference` | no | the run id | Your own reference on the fax, up to 200 characters |
| `waitForDelivery` | no | `true` | `false` returns once the fax is queued |
| `timeoutSeconds` | no | `600` | How long to wait for a terminal state before the run fails |
| `failOn` | no | `failed,unknown` | Terminal states that fail the run. Empty never fails it |
| `baseUrl` | no | `https://api.faxforagents.com` | Only for a test double |

```json
{
  "apiKey": "ffa_test_...",
  "to": "+15550000001",
  "text": "Hello from an Apify Actor.",
  "coverSubject": "Deploy notice"
}
```

A test key (`ffa_test_...`) reaches the sandbox only. No carrier, no cost. The destination number
picks the outcome: `+15550000001` is delivered, `+15550000003` is partial and `+15550000005` is
unknown. A live key (`ffa_live_...`) reaches the carrier and spends the prepaid wallet. Keys are
created at [app.faxforagents.com](https://app.faxforagents.com).

### Output

One dataset record, also stored as `OUTPUT` in the default key-value store.

```json
{
  "fax_id": "fax_...",
  "quote_id": "qte_...",
  "document_id": "doc_...",
  "to": "+15550000001",
  "state": "delivered",
  "failure_code": null,
  "pages": 1,
  "pages_sent": 1,
  "amount_micro": 39000,
  "provider_ref": "...",
  "evidence": { "remote_station_id": "...", "pages": 1 },
  "idempotency_key": "apify:<run id>:+15550000001"
}
```

`amount_micro` is the quoted price in USD micro-units: 39000 is $0.039. `evidence` is what the
carrier reported on a delivered or partial fax. It is not legal proof the recipient accepted it. A
`partial` or `unknown` outcome never retransmits on its own.

An API refusal fails the run with the remedy and the stable `error_code` in the status message, for
example `error_code=insufficient_balance`. When the quote says a human approval is required and no
`approvalId` was given, the run fails before anything is sent.

### Prices

The price card is effective from 2026-10-28. $0.039 per page sent, $0.019 per page received. $2 per
number per month, $19 per month for the Agent workspace. Sandbox pages cost 0. Apify charges its own
compute for the run.

### Working on this repository

```bash
npm ci
npm test                 # the send flow against a fake API, no network
npm run test:local       # a run through the Apify SDK against the fake API, dataset and OUTPUT checked
npx apify-cli run        # the same through the Apify CLI, input from storage/key_value_stores/default/INPUT.json
docker build -t faxforagents-send-a-fax .
sh scripts/truthcheck.sh
```

Publishing to the Apify Store goes through Apify Console: push the Actor (`apify push`), build it,
then Publishing, "Publish on Store".

### Licence

MIT. See [LICENSE](LICENSE). Security reports go to hello@faxforagents.com.

# Actor input Schema

## `apiKey` (type: `string`):

ffa_test\_... reaches the sandbox only. ffa_live\_... reaches the carrier and spends the prepaid wallet. Keys are created at app.faxforagents.com. Leave it empty and the run only checks that the API is reachable, sends nothing and writes one check record.

## `to` (type: `string`):

The destination fax number in E.164, for example +12025550123.

## `text` (type: `string`):

Plain text rendered to a letter PDF. Pass this, markdown or fileUrl, not two.

## `markdown` (type: `string`):

Markdown (headings, lists, emphasis, code) rendered to PDF.

## `fileUrl` (type: `string`):

A URL of a PDF, PNG, JPG or TIFF the Actor downloads and sends. Up to 25 MB.

## `coverSubject` (type: `string`):

Any cover field adds a billable cover page in front of the document.

## `coverMessage` (type: `string`):

A short message on the cover sheet.

## `coverToName` (type: `string`):

The recipient name on the cover sheet.

## `coverFromName` (type: `string`):

The sender name on the cover sheet.

## `approvalId` (type: `string`):

An approved request bound to this send (apr\_...), when the account needs a human approval for live sends.

## `clientReference` (type: `string`):

Your own reference stored on the fax, up to 200 characters. Defaults to the Apify run id.

## `waitForDelivery` (type: `boolean`):

Poll until the fax is delivered, partial, failed or unknown. Off returns once the fax is queued.

## `timeoutSeconds` (type: `integer`):

How long to wait for a terminal state before the run fails.

## `failOn` (type: `string`):

Comma-separated terminal states that fail the run. Empty never fails the run.

## `baseUrl` (type: `string`):

Only change it to point the Actor at a test double.

## Actor input object example

```json
{
  "to": "+15550000001",
  "text": "Hello from an Apify Actor.",
  "waitForDelivery": true,
  "timeoutSeconds": 600,
  "failOn": "failed,unknown",
  "baseUrl": "https://api.faxforagents.com"
}
```

# Actor output Schema

## `faxes` (type: `string`):

The fax record this run sent: id, destination, state, pages, price and the carrier's evidence.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "to": "+15550000001",
    "text": "Hello from an Apify Actor."
};

// Run the Actor and wait for it to finish
const run = await client.actor("faxforagents/apify-actor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "to": "+15550000001",
    "text": "Hello from an Apify Actor.",
}

# Run the Actor and wait for it to finish
run = client.actor("faxforagents/apify-actor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "to": "+15550000001",
  "text": "Hello from an Apify Actor."
}' |
apify call faxforagents/apify-actor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,faxforagents/apify-actor"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/i8m0frDOczCrGW9Ya/builds/wvEcfkR1NSK9L9qWg/openapi.json
