Outcome
You finish this guide with your eFax integration replaced by mintfax. eFax has two developer surfaces and the migration treats them separately: Universal Outbound (the legacy XML-over-HTTPS-POST API branded “eFax Developer”) and Enterprise API (the newer REST + JSON + OAuth2 product). Both shapes map onto the same mintfax endpoints, auth becomes a single Bearer token, callbacks become Standard Webhooks signed deliveries, and you verify delivery in the sandbox before any live key gets touched.Which eFax interface are you on?
If your code POSTs an XML body tohttps://secure.efaxdeveloper.com/EFax_WebFax.serv with <AccessControl><UserName> credentials, you are on Universal Outbound. If your code mints OAuth2 bearer tokens via POST /tokens, sends a user-id: <fax-number> header, and calls resource-shaped paths like GET /faxes/sent, you are on Enterprise API. mintfax is the same target for both.
Prerequisites
- A mintfax account with a sandbox API key (prefix
mfx_test_). Register here if you do not have one. - Your existing eFax integration code and webhook handler.
curlor Node.js 18+ available.- For Enterprise API migrations: access to your eFax developer portal. The request schema for
POST /faxes, the HMAC settings on inbound webhooks, the API base URL, and any error code catalog are gated behind a portal login and are not in public eFax documentation.1
Step 1: Replace your credentials
mintfax authenticates with a single Bearer token in theAuthorization header. Drop the body-embedded <AccessControl> block from Universal Outbound payloads, and stop minting and refreshing OAuth2 tokens for Enterprise API. There is no token expiration on the mintfax side; rotate keys from the dashboard when you need to.
status: "queued" and a fax id. A wrong key returns HTTP 401 with error: "api_key_invalid".
Step 2: Map your endpoints
Both eFax interfaces map onto resource-shaped routes under/v1/faxes. Universal Outbound used a single URL with the operation determined by the XML root element; Enterprise API split operations across paths. mintfax follows the same resource shape Enterprise API does, with a few additions and one removal.
A few mapping notes:
- Disposition URL is per-account, not per-call. Universal Outbound let you set
<DispositionURL>on each send. mintfax registers webhook endpoints once viaPOST /v1/webhooksand routes every event there. - Status query becomes an idempotent GET. Universal Outbound asked you to re-POST an XML body with
<DOCID>to learn the current state. mintfax exposes state onGET /v1/faxes/{id}. Use it as a fallback; webhooks are the recommended path.
Step 3: Translate status and error semantics
Universal Outbound returns a numericStatusCode and a free-text StatusDescription. Enterprise API returns HTTP-level errors documented in the gated portal. mintfax returns one error envelope on synchronous failures and one event payload on terminal async outcomes:
error field. It is a stable machine code; the full catalog is in the error catalog.
Terminal delivery outcomes arrive as events on your webhook endpoint, not as XML POSTs:
Two changes in your handler logic:
- Match on
error_code, not the human string. eFax’sStatusDescriptionis convenient right up to the day carrier wording shifts. mintfax’sdata.object.error_codeonfax.failedis stable. See the event catalog for the full envelope and per-event payloads. - Each attempt fires its own event. mintfax retries transient failures automatically (default three attempts, configurable per fax or via account fax settings) and fires a fresh
fax.sendingevent at the start of each attempt - the repeatedfax.sendingis your in-flight retry signal.fax.failedis terminal and fires once after all attempts exhaust.
Step 4: Replace credential-echoed callbacks with Standard Webhooks
This is the security swap that matters. Universal Outbound disposition POSTs are authenticated by checking that the eFax credentials echoed back inside the XML body match what you stored. No signature, no timestamp, no replay window. mintfax follows the Standard Webhooks specification: every delivery is signed HMAC-SHA256 over{webhook-id}.{webhook-timestamp}.{body} with your endpoint’s secret, and arrives with three headers (webhook-id, webhook-timestamp, webhook-signature).
Register an endpoint once per environment:
signing_secret in the response, shown once. Store it as MINTFAX_WEBHOOK_SECRET.
Replace your eFax disposition handler. Use the Standard Webhooks reference library for your language - it handles signature comparison, the 5-minute timestamp tolerance, and parsing multi-secret rotation tokens. Do not hand-roll the HMAC.
webhook-timestamp is ten minutes old and confirm the reference library rejects it (the default tolerance is 5 minutes).
For the full multi-language reference, including key rotation handling, see verify webhook signatures.
Step 5: Add idempotency keys
Universal Outbound has no idempotency primitive.TransmissionID is an audit identifier, not duplicate suppression: a retried POST after a network blip can send the same fax twice. The Enterprise API public Quick Start Guide does not document an idempotency key either.
mintfax accepts Idempotency-Key on every mutating request. Send the same key on retry and you get the same response back; the side effect (a credit hold and a fax submission) happens once.
Step 6: Verify in the sandbox before flipping a live key
mintfax’s sandbox is a structurally separate environment. Use amfx_test_ key and the magic recipient numbers below to drive every code path you care about without sending a real fax.
Run through the integration once per number:
fax.queued then fax.delivered for +15005550001, fax.queued then fax.failed with data.object.error_code: "line_busy" for +15005550002, and an HTTP 402 with error: "insufficient_balance" for +15005550006. Full coverage of magic numbers and other deterministic scenarios is in the sandbox guide.
Verify
You are ready to swap the live key when:- The sandbox run produces the expected event for every magic number, and your handler stores the top-level
idfor deduplication. - Your webhook handler rejects tampered payloads (HTTP 401) and stale timestamps (HTTP 403).
- Synchronous error paths return the documented
errorcodes with the correctcontextshape, and your code matches onerror, not onmessage. - Retried requests with the same
Idempotency-Keyreturn the same faxidand do not create a duplicate. - Your eFax
<DispositionURL>and any Enterprise API webhook destination are pointed at the new mintfax handler, or removed once mintfax is the only sender.
Rollback
If you need to roll back during cutover, the eFax side is unchanged: your old credentials still work, your<DispositionURL> still receives XML POSTs, and your OAuth2 tokens still mint. The change is in your code, not in eFax. Keep the eFax integration deployed and feature-flagged behind a runtime switch until you have a clean signal that the mintfax path is producing the events and outcomes you expect.
What to do next
- Verify webhook signatures - full multi-language reference for the Standard Webhooks scheme.
- Idempotency keys - retry semantics and the 24-hour replay window.
- Error catalog - every machine code with HTTP status, action, and context shape.
- Event catalog - every event type and the exact
datapayload. - Sandbox - magic numbers and deterministic failure scenarios.
Footnotes
-
The Enterprise API JSON request body shape, the HMAC algorithm and header names for inbound webhooks, the file size and page-count limits, and the exact base URL for
/faxesall live behind a login on the eFax Enterprise Developer Portal. The endpoint table below names the operations that map; the field-level details on the eFax side come from your portal docs. ↩ -
The eFax Enterprise API Quick Start Guide v2.0 states “Webhook notifications can be set up to require HMAC authentication.” The algorithm, header names, signing input, and replay window are not in the public Quick Start Guide. mintfax follows the Standard Webhooks specification: HMAC-SHA256 over
{webhook-id}.{webhook-timestamp}.{body}, signed with your endpoint’s secret, with a documented five-minute replay window enforced by the reference libraries. ↩