An open source fax API gives developers a way to send and receive faxes straight from their own code, using HTTP calls instead of a physical machine or a paid SaaS gateway. ICTFax exposes a REST fax API on top of a FreeSWITCH and T.38 backend, so you trigger a fax with a simple request, track its status, and keep every page on hardware you control.

If you’ve ever tried to bolt faxing onto an application, you already know the pain. Most cloud fax providers charge per page, lock your documents inside their portal, and hand you an API that works fine until your volume grows or compliance team asks where the data actually lives. For healthcare, legal, and finance teams, that last question isn’t optional.

This guide walks through what an open source fax API really does, how the ICTFax community edition handles it, what a send request looks like, and where the honest limits are. No marketing fluff. Just the parts that matter when you’re the one wiring it up.

What an Open Source Fax API Actually Does

At its core, an open source fax API is an HTTP interface sitting in front of a fax engine. Your application posts a document, a destination number, and a few parameters. The server queues the job, dials out over SIP, negotiates the fax session using the T.38 protocol, and reports back whether the transmission succeeded.

The word “open source” carries real weight here. You can read the code, change it, host it on your own server, and audit exactly how documents move through the system. That’s a different relationship than you get with a closed gateway, where you trust a vendor’s word and accept whatever their dashboard tells you.

ICTFax is built on FreeSWITCH, not Asterisk. That matters because FreeSWITCH handles concurrent fax sessions and T.38 negotiation in a way that holds up under load. The community edition ships the same fax engine the paid editions use, which means the API you build against today won’t get yanked out from under you later. For a deeper look at the transport layer, the team’s writeup on open source faxing software based on the T.38 protocol covers why T.38 beats raw G.711 pass-through for reliability.

My honest take: the per-page billing model from cloud fax vendors looks cheap at low volume and gets ugly fast. If you’re sending more than a few hundred pages a month, owning the stack usually wins on cost alone, and that’s before you count the data-control benefits.

Why Developers Reach for a Self-Hosted Fax API

Three reasons come up again and again, and they’re not the same for every team.

The first is data residency. A hospital integration can’t have patient records bouncing through a third-party fax cloud in another jurisdiction. When you run the fax server yourself, the document never leaves your network until it hits the destination line. That’s a clean story to tell an auditor.

Second is cost at scale. Picture a billing department that fires off 4,000 statements a month. On a typical per-page plan, that adds up quickly and keeps climbing as the business grows. A self-hosted server turns that variable cost into a fixed one: your SIP trunk minutes plus a box to run it on.

Third is control over the integration itself. You’re not waiting on a vendor’s roadmap to add a status field or a new endpoint. The code is yours. Need to log every transmission to your own database, retry failed jobs on a custom schedule, or tag faxes by department? You can, because nothing’s hidden behind a closed API contract.

There’s a tradeoff, and I won’t pretend otherwise. You’re now responsible for uptime, SIP configuration, and keeping the server patched. If your team has zero ops capacity, a managed service might be the saner choice. But if you already run infrastructure, adding a fax server is a small lift for a big payoff.

How the ICTFax REST Fax API Works

The API lives at /api on your own ICTFax server and speaks JSON. You log in with an ICTFax username and password, and you get back a JWT that goes in an Authorization: Bearer header on every later call. There’s no separate API key to generate.

Sending a fax takes four calls:

  1. POST /api/documents creates a document record and returns its id.
  2. PUT /api/documents/{id}/media uploads the file as the raw request body, with Content-Type set to application/pdf, image/tiff, image/png or image/jpeg.
  3. POST /api/programs/sendfax/transmissions creates the fax job for a destination number and returns a transmission id.
  4. POST /api/transmissions/{id}/send starts it.

Here’s the whole flow in curl. Swap in your own host and password:

API=https://fax.example.com/api
TOKEN=$(curl -s -X POST $API/authenticate -H "Content-Type: application/json" \
  -d '{"username":"admin@ictcore.org","password":"YOUR_PASSWORD"}' | jq -r .token)

DOC_ID=$(curl -s -X POST $API/documents -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"invoice-1001","description":"Invoice 1001","quality":"standard"}')

curl -s -X PUT $API/documents/$DOC_ID/media -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/pdf" --data-binary @invoice-1001.pdf

TX_ID=$(curl -s -X POST $API/programs/sendfax/transmissions -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"program_data":{"document_id":'$DOC_ID'},"transmission_data":{"title":"Invoice 1001","phone":"15550199001"}}')

curl -s -X POST $API/transmissions/$TX_ID/send -H "Authorization: Bearer $TOKEN"

ICTFax then dials out through your SIP trunk, and FreeSWITCH handles the T.38 negotiation with the receiving end.

To learn how it went, you poll. ICTFax has no webhooks or callback URLs, so your code asks GET /api/transmissions/{id} and reads the status field, along with response for the detail when a fax fails. POST /api/transmissions/{id}/retry tries a failed fax again. A number on your Contact DNC list is never dialled: its transmission is marked failed(dnc).

Receiving works the other way round. An inbound number routes to your server, FreeSWITCH answers and captures the pages, and ICTFax stores the document. You list received faxes with GET /api/transmissions?direction=inbound (or GET /api/faxes), and view or download each one from Receive Fax in the web panel. You can also have a DID email each fax to you instead.

One thing worth flagging: faxing isn’t instant like an HTTP call. A multi-page fax can take a minute or two, because it’s a real-time session over the line. Treat sending as asynchronous, and check status from a background job every few seconds rather than blocking a user while the pages go through. The fax API tutorial has a ready-made polling loop and the receive calls in full.

Setting Up an Open Source Fax API the First Time

Getting from zero to a working fax API takes a few pieces. You need the ICTFax server installed, a SIP trunk from a provider that supports T.38, and a number to send from. The install itself is well documented.

Start with the platform install. The ICTFax installation guide covers the full setup, including the Docker image if you’d rather not install packages by hand. Budget an afternoon for a first-time setup if you’re new to FreeSWITCH; less if you’ve configured SIP before.

Once the server’s running, the order of operations looks like this:

  • Point your SIP trunk credentials at the FreeSWITCH gateway and confirm registration.
  • Verify T.38 is enabled on both your trunk and the server, since a mismatch here is the most common reason first faxes fail.
  • Log in to /api/authenticate with an ICTFax username and password, and send the JWT it returns as a Bearer token. Change the default admin password first.
  • Send a test fax to a number you control and watch the logs to confirm the T.38 handshake completes.

That T.38 verification step is the one people skip and then spend an hour debugging. Do it first. If your provider only offers G.711 fax pass-through, faxes will work intermittently and fail under any jitter, which is maddening to diagnose after the fact.

Where an Open Source Fax API Fits and Where It Doesn’t

Be clear-eyed about the fit. A self-hosted open source fax API shines when you have steady or growing volume, a compliance reason to keep data in-house, and at least some operations capability on the team. Healthcare, legal, financial services, and government integrations check all three boxes.

It’s a weaker choice if you send two faxes a month and have nobody to maintain a server. In that case the operational overhead outweighs the savings, and a pay-as-you-go service is fine. There’s no shame in picking the simpler tool when the volume doesn’t justify owning the stack.

On the AI front, automated fax classification and content extraction are interesting ideas, and they’re on the development roadmap rather than shipping today. If you’re evaluating ICTFax, plan around the fax API and FreeSWITCH transport that exist now, and treat the smarter document-handling features as coming soon rather than something to build on this quarter.

For most teams that already run their own infrastructure, the open source route is the one I’d recommend. You get a fax API you can read, modify, and trust, sitting on a FreeSWITCH backend that’s proven under load. You can dig into the full capability list on the ICTFax features page before you commit.

Frequently Asked Questions

Is the ICTFax fax API really free to use?

Yes. The ICTFax community edition is open source and free to download and run. You’ll still pay for a SIP trunk and the server you host it on, but there’s no per-page software fee and no license cost for the fax API itself.

Does the fax API support both sending and receiving?

It does. You can submit outbound faxes through a REST request and route inbound numbers to the server, which answers, captures the pages, and stores or forwards the document. Both directions run over the same FreeSWITCH and T.38 backend.

Why does ICTFax use FreeSWITCH instead of Asterisk?

FreeSWITCH handles concurrent fax sessions and T.38 negotiation reliably under load, which suits a server expected to process many faxes at once. ICTFax is built on FreeSWITCH end to end, not Asterisk, so the fax engine and API are tuned for that platform.

What do I need before I can send my first fax through the API?

Three things: the ICTFax server installed, a SIP trunk that supports the T.38 protocol, and a sending number. Once those are in place, you log in to the API with your ICTFax username and password, take the token it returns, and make your first send calls.

Can I get a webhook when a fax finishes sending?

No. ICTFax has no webhooks or callback URLs. Poll GET /api/transmissions/{id} every few seconds from a background job and read its status field until the fax has finished.

Is this suitable for HIPAA-sensitive faxing?

Self-hosting helps because documents stay on infrastructure you control until they reach the destination line. Compliance always depends on your full setup, including network security and access controls, but keeping the data in-house removes the third-party exposure that cloud fax services introduce.

Getting Started

If you’re ready to build against it, grab the software from the ICTFax download page and follow the installation guide to stand up your server. Questions about your specific SIP or T.38 setup? Open a support ticket.

Related Resources