Send and Receive Faxes with the ICTFax REST API
ICTFax Community Edition has a JSON REST API at /api on your fax server. You log in once to get a token, upload a document, create a fax transmission and tell it to send. Then you poll that transmission for the result, because there are no webhooks. Received faxes come from the same API.
Every example below uses curl and was checked against a running ICTFax server. Swap fax.example.com for your own host. If you haven’t installed ICTFax yet, start with the ICTFax installation guide.
Base URL and authentication
All calls go to http(s)://<your server>/api. Requests and responses are JSON. First, swap a username and password for a token:
curl -s -X POST https://fax.example.com/api/authenticate \
-H "Content-Type: application/json" \
-d '{"username":"admin@ictcore.org","password":"helloAdmin"}'
The reply carries a JWT in the token field. admin@ictcore.org / helloAdmin is the default login on a fresh install. Change that password straight away (top-right user menu, Change Password) and never ship it in your code.
Store the token in a shell variable. The examples use jq to pull fields out of the JSON:
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_NEW_PASSWORD"}' | jq -r .token)
Send Authorization: Bearer $TOKEN on every call after that.
Send a fax in four calls
1. Create a document
A document is the file you’ll fax. Create its record first. The reply is the new document id.
DOC_ID=$(curl -s -X POST $API/documents \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"invoice-1001","description":"Invoice 1001 for Example Corp","quality":"standard"}')
echo $DOC_ID
2. Upload the file
Upload the file with a PUT, and send the raw file as the request body. Don’t use a multipart form. Set Content-Type to match the file: application/pdf, image/tiff, image/png or image/jpeg.
curl -s -X PUT $API/documents/$DOC_ID/media \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/pdf" \
--data-binary @invoice-1001.pdf
Use --data-binary, not -d. Plain -d strips line breaks and corrupts the PDF.
3. Create the transmission
This shortcut creates the send-fax program and the transmission in one call. Put the document id in program_data and the destination in transmission_data. The reply is the transmission id.
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"}}')
echo $TX_ID
4. Send it
curl -s -X POST $API/transmissions/$TX_ID/send \
-H "Authorization: Bearer $TOKEN"
That’s it. ICTFax dials out through your active provider, and your provider Prefix rules are applied to the number on the way out.
The two-step alternative
Sending the same document to lots of numbers? Create the send-fax program once, then add one transmission per number to it:
PROGRAM_ID=$(curl -s -X POST $API/programs/sendfax \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"document_id":'$DOC_ID'}')
TX_ID=$(curl -s -X POST $API/transmissions \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"program_id":'$PROGRAM_ID',"phone":"15550199002","title":"Invoice 1001, copy 2"}')
curl -s -X POST $API/transmissions/$TX_ID/send \
-H "Authorization: Bearer $TOKEN"
Check status and retry
Read one transmission:
curl -s $API/transmissions/$TX_ID -H "Authorization: Bearer $TOKEN" \
| jq '{status, response, try_allowed, last_run}'
The status field says where the fax stands, and response holds the detail when something goes wrong. To see every outbound fax at once:
curl -s "$API/transmissions?direction=outbound" -H "Authorization: Bearer $TOKEN"
If a fax failed, you can try it again without creating a new transmission:
curl -s -X POST $API/transmissions/$TX_ID/retry -H "Authorization: Bearer $TOKEN"
There are no webhooks, so poll
ICTFax doesn’t call your application back when a fax finishes. There’s no callback URL to register. Your code has to ask. A fax is a real-time call that can run a minute or more, so check every few seconds and stop once it leaves the pending and processing states:
for i in $(seq 1 60); do
STATUS=$(curl -s $API/transmissions/$TX_ID -H "Authorization: Bearer $TOKEN" | jq -r .status)
echo "$(date +%T) $STATUS"
if [ "$STATUS" != "pending" ] && [ "$STATUS" != "processing" ]; then
break
fi
sleep 10
done
In a real app, run that check from a background job rather than holding a user’s request open while the pages go through.
Numbers on the DNC list
Numbers you’ve added under Contact DNC are never faxed, and that includes faxes sent through the API. A transmission to a DNC number is marked failed(dnc) and nothing is dialled. Treat that status as final. Retrying won’t help until the number comes off the list.
Receive faxes
List received faxes with either call:
curl -s "$API/transmissions?direction=inbound" -H "Authorization: Bearer $TOKEN"
curl -s $API/faxes -H "Authorization: Bearer $TOKEN"
To view or download a received fax itself, open Receive Fax in the web panel. If you already know a document’s id, say one you uploaded, you can download its file from the API:
curl -s $API/documents/$DOC_ID/media \
-H "Authorization: Bearer $TOKEN" -o invoice-1001.pdf
Only faxes that ICTFax answers and stores show up here. If a DID is set to Forward to Extension under My DIDs, the call goes live to the fax machine and isn’t stored, so it won’t appear in this list.
Extension settings for devices
If you connect fax machines or ATAs, you can fetch an extension’s device settings from the API:
curl -s $API/accounts/$ACCOUNT_ID/provisioning -H "Authorization: Bearer $TOKEN"
It returns the username, password, callerid, host and port a device needs to register. The host value comes from [provisioning] host in /usr/ictcore/etc/ictcore.conf. That defaults to localhost, which is no use to a device on your network, so set it to the server’s public IP or hostname. You create the extension itself under Administration > Extensions. The fax machine and ATA guide covers the device side.
Full API reference
This page covers the fax flow. For every endpoint and field, read the ICTCore API guide on GitHub, docs/ApiGuide.md in ictinnovations/ictcore, or open the ICT Innovations APIs collection on Postman, whose Fax folder walks through the same steps. For screen-by-screen help with the web interface, see the ICTFax admin guide. Stuck? Open a support ticket.
FAQ
Which file types can I upload?
PDF, TIFF, PNG and JPEG. Set the matching Content-Type on the PUT to /api/documents/{id}/media.
Can ICTFax call my URL when a fax is done?
No. There are no webhooks or callbacks. Poll GET /api/transmissions/{id} until the status settles.
Do I need a separate API user or API key?
No. You log in with an ICTFax username and password at /api/authenticate and use the JWT it returns as a Bearer token.
Can I send one document to many numbers?
Yes. Create one send-fax program for the document, then add a transmission per number with POST /api/transmissions and send each one.
Why did my fax fail with failed(dnc)?
The destination number is on your Contact DNC list. ICTFax refuses to dial it, whether you send from the web interface or the API.
Is the API part of the free Community Edition?
Yes. The REST API ships with ICTFax Community Edition on your own server, with no per-page fee.