Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintfax.com/docs/llms.txt

Use this file to discover all available pages before exploring further.

Outcome

When you finish this guide, your integration sends to https://api.mintfax.com/v1 with a Bearer key, reads snake_case fields, signs an Idempotency-Key on every send, verifies Standard Webhooks signatures on every webhook, and has cleared a full fax round-trip in the sandbox. Code samples are curl and Node. Marketing positioning is at /compare/sinch; this page is the dev-execution path.

Prerequisites

  • A mintfax environment with a sandbox API key (mfx_test_...). Live keys (mfx_live_...) come later, after the sandbox round-trip.
  • A webhook endpoint configured in the mintfax dashboard. The signing secret is shown once; store it as MINTFAX_WEBHOOK_SECRET.
  • Your existing Sinch v3 (or Phaxio v2.1) integration in source control. The cutover diff is small, but you want a clean revert.
  • Node 18+ for the Node samples, or any HTTP client that speaks multipart/form-data for the curl samples.
  • Read Verify webhook signatures, Idempotency, Errors, Event types, and Sandbox before cutover. The migration leans on each one.

Step 1: Swap the base URL and authentication

Sinch v3 uses HTTP Basic with a project-scoped key and secret, and the project ID lives in the URL path. mintfax uses a Bearer token, infers the environment from the key prefix (mfx_test_ for sandbox, mfx_live_ for live), and has no project ID in the URL.
ConcernSinch Fax v3mintfax
Base URLhttps://fax.api.sinch.com/v3/projects/{projectId}https://api.mintfax.com/v1
AuthHTTP Basic (-u {KEY_ID}:{KEY_SECRET})Authorization: Bearer mfx_test_...
Env switchDifferent project IDsDifferent key prefixes
# Sinch v3
curl -X POST 'https://fax.api.sinch.com/v3/projects/{PROJECT_ID}/faxes' \
  -u {KEY_ID}:{KEY_SECRET} \
  -F 'to=+15551235656' \
  -F 'file=@document.pdf'

# mintfax
curl -X POST https://api.mintfax.com/v1/faxes \
  -H "Authorization: Bearer mfx_test_4eC39HqLyjWDarjtT1zdp7dc" \
  -F "to=+15005550001" \
  -F "file=@document.pdf"
Verify A GET https://api.mintfax.com/v1/account with your sandbox key returns 200 and your account ID. A 401 means the header is missing or malformed; see unauthenticated and api_key_invalid.

Step 2: Rename fields from camelCase to snake_case

Phaxio v2.1 used snake_case. Sinch v3 broke that convention. mintfax restores it. Most send-path fields map directly; a few drop or rename.
Sinch v3 (camelCase)mintfax (snake_case)Notes
ididmintfax IDs are fax_-prefixed
totoE.164 string, single recipient
from(not in send schema)Caller ID is environment-level, not per-fax
numberOfPagespagesNull until the document has been rasterized
statusstatusDifferent enum; see Step 4
createTimecreated_atISO 8601 UTC
completedTimecompleted_atISO 8601 UTC
callbackUrlwebhook_urlPer-fax override
maxRetriesretries0-10 in mintfax, 0-5 in Sinch v3
retryDelaySeconds(not exposed)mintfax controls retry timing
errorCodeerror_codeFree-form string in mintfax
direction(not in public API)mintfax v1 is outbound-only
projectId(not applicable)Environment is inferred from the key
serviceId(not applicable)mintfax has no Services concept
price.amount(not on fax payload)Pricing is on the environment, not the fax record
coverPageId(not in v1)Cover-page templating is roadmap, not v1
labelstagsUp to 10 keys, each up to 64 chars
The mintfax Fax resource also adds fields Sinch v3 does not have: csid, page_size (letter/legal/a4/b4), resolution (standard/fine), optimize_for (text/photo), and idempotency_key. All are optional and fall back to your environment fax settings. Verify Submit one fax, then GET /v1/faxes/{id}. Confirm every field your old code reads is present under its snake_case name.

Step 3: Add an idempotency key

Sinch v3 has no documented idempotency feature. A retried POST /faxes against Sinch can double-bill silently. mintfax accepts an Idempotency-Key header on POST /v1/faxes and POST /v1/faxes/{id}/resend; duplicate keys return the original response without re-processing, and keys expire after 24 hours. Generate a UUID v4 per submission. Reuse the same key on retry.
curl -X POST https://api.mintfax.com/v1/faxes \
  -H "Authorization: Bearer mfx_test_4eC39HqLyjWDarjtT1zdp7dc" \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -F "to=+15005550001" \
  -F "file=@document.pdf"
Verify Submit the same request twice with the same Idempotency-Key. Both responses carry the same id. Read the Idempotency guide for full semantics.

Step 4: Translate status and retry behavior

Sinch v3 status names are uppercase; mintfax statuses are lowercase, and add one extra value (submitted) to distinguish “in queue” from “handed to the carrier.”
Sinch v3 statusmintfax status
QUEUEDqueued
IN_PROGRESSsubmitted, in_progress
COMPLETEDdelivered
FAILUREfailed
Retry caps differ. Sinch v3 caps at 5 retries (maxRetries 0-5); mintfax caps at 10 (retries 0-10), with a default of 3. Configure it per fax or as an environment default at PUT /v1/environment/fax-settings. Retry timing is controlled by mintfax, so there is no per-request retryDelaySeconds to map. If you previously cancelled queued faxes, note that neither product exposes a cancel endpoint. A queued fax in mintfax runs to a terminal state (delivered or failed) before it can be deleted via DELETE /v1/faxes/{id}. A delete attempt against an in-flight fax returns fax_not_terminal. Verify Submit a fax to +15005550004 (the transient-failure magic number) and watch the lifecycle: fax.queued, fax.sending (attempt 1), fax.sending (attempt 2 after the transient failure), then fax.delivered. fax.sending fires once per delivery attempt, so the repeat is how you observe mid-flight retries. See Sandbox.

Step 5: Replace the v3 webhook scheme with Standard Webhooks

This is the highest-value step in the migration. Sinch v3 dropped the Phaxio v2.1 X-Phaxio-Signature (HMAC-SHA1) header. The v2-to-v3 guide states verbatim: “Webhook signatures: Removed due to low usage, at the moment V3 does not support.” What replaced it is HTTP Basic credentials inside the callback URL. That mechanism leaks the credential to any reverse proxy in the path and to Sinch’s own request logs, and it does not authenticate the payload. mintfax follows the Standard Webhooks specification and sends three headers on every delivery:
HeaderValue
webhook-idEvent identifier (evt_-prefixed). Stable across retry attempts; use it to deduplicate.
webhook-timestampUnix epoch seconds, re-stamped per delivery attempt.
webhook-signatureOne or more space-separated v1,<base64-hmac> tokens.
The signature is HMAC-SHA256(secret, "{webhook-id}.{webhook-timestamp}.{body}"), base64-encoded, emitted as v1,<base64>. Use the Standard Webhooks reference library for your language - it handles constant-time comparison, the 5-minute timestamp tolerance, and parsing multi-secret rotation tokens.
import express from 'express';
import { Webhook } from 'standardwebhooks';

const app = express();
const wh = new Webhook(process.env.MINTFAX_WEBHOOK_SECRET);

app.post(
  '/webhooks/mintfax',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    try {
      const event = wh.verify(req.body, {
        'webhook-id': req.headers['webhook-id'],
        'webhook-timestamp': req.headers['webhook-timestamp'],
        'webhook-signature': req.headers['webhook-signature'],
      });
      // event.type, event.id, event.data.object.id, event.data.object.status, ...
      res.sendStatus(200);
    } catch (err) {
      res.status(401).send('Invalid signature');
    }
  },
);
Verify Send a fax to +15005550001 (success) and confirm your handler returns 200. Tamper with one byte of the body in a replayed request and confirm 401. Read the full verify webhook signatures page for the multi-language reference and rotation guidance before you ship.

Step 6: Adjust webhook event handling

Sinch v3 fires one FAX_COMPLETED event whose fax.status is either COMPLETED or FAILURE. mintfax fires distinct event types, plus events Sinch does not have: fax.queued, fax.sending, fax.delivered, fax.failed, balance.low, and balance.topup.
Sinch v3 eventmintfax event
FAX_COMPLETED (success)fax.delivered
FAX_COMPLETED (failure)fax.failed
(no equivalent)fax.queued
inProgressNotificationsfax.sending (fires once per delivery attempt - this is also the mid-attempt signal)
(no equivalent)balance.low, balance.topup
INCOMING_FAX(not supported; mintfax v1 is outbound-only)
Two consequences for your handler. First, code that branched on fax.status inside one event now branches on event.type at the top of the payload; a switch over event.type is clearer. Second, if your Sinch v3 integration consumes INCOMING_FAX, mintfax has no equivalent today. Treat that as a known gap, and do not migrate inbound flows yet. The mintfax envelope is the same across all event types: id (evt_-prefixed, also delivered as the webhook-id header; use it for deduplication), type (the event name), created (Unix timestamp in seconds), and data.object (event-specific payload). The full catalog is at Event types. Verify Confirm your handler routes fax.delivered and fax.failed correctly. Replay protection plus id deduplication means you can replay any captured event safely while testing.

Step 7: Translate error codes

Sinch v3 returns an integer errorCode plus a type field on failure (one of DOCUMENT_CONVERSION_ERROR, CALL_ERROR, FAX_ERROR, FATAL_ERROR, GENERAL_ERROR), and uses HTTP statuses for top-level rejections. mintfax returns a stable string error field in a consistent envelope with message, action, and docs.
Sinch v3 (HTTP / errorCode)Meaningmintfax error code
HTTP 401Authentication failureunauthenticated, api_key_invalid, api_key_missing
HTTP 402Account balance at zeroinsufficient_balance
HTTP 404Resource not foundnot_found
HTTP 422Invalid parametersvalidation_failed, fax_data_purged
HTTP 429Rate limitrate_limit_exceeded
HTTP 500Temporary server errorinternal_server_error
HTTP 800URL fetch failure (contentUrl)(no equivalent; mintfax accepts multipart only)
128-130 DOCUMENT_CONVERSIONBad / unsupported MIMEvalidation_failed at submit time
17 CALL_ERRORDestination busySurfaces as fax.failed with error_code: line_busy
16 CALL_ERRORRing timeout / no answerSurfaces as fax.failed with carrier-supplied error_code
4 / 54 / 133 (conversion)Document conversion failuresSurfaces as fax.failed
The per-failure-reason error catalog for the fax record itself (the error_code field on fax.failed) is intentionally compact in v1; treat unfamiliar values as opaque-but-logged. Top-level API errors (the envelope above) are stable, documented at Errors, and machine-matchable on the error field. Verify Submit to +15005550005 (the permanent-failure magic number) and confirm your fax.failed handler logs data.error_code and routes to your alerting path.

Step 8: Adapt sandbox tests

Sinch v3 ships a single magic number for sandbox testing. mintfax ships per-failure-mode magic numbers so you can exercise each branch deterministically.
Scenariomintfax magic number
Always succeeds+15005550001
Always busy+15005550002
Rings out, no pickup+15005550003
Fails once, then OK+15005550004
Always fails+15005550005
Insufficient balance+15005550006
Validation failure+15005550099
Existing test fixtures built around Sinch’s single number expand here: one fixture per failure mode, deterministic, no carrier dependency. The full list and behavior are on Sandbox. Verify Run your full integration suite against the sandbox using these numbers before any traffic moves to live keys.

Verify

End-to-end check before you flip live traffic:
  1. Send a fax to +15005550001 with a sandbox key and an Idempotency-Key. The response carries status: "queued" and a fax_-prefixed id.
  2. Your webhook endpoint receives fax.queued, then fax.sending, then fax.delivered. Each request passes Standard Webhooks verification via the reference library.
  3. Repeat the same POST /v1/faxes with the same idempotency key. The response is identical; no second fax is created.
  4. Send to +15005550005 and confirm a fax.failed event with a populated error_code.
  5. Replace mfx_test_ with mfx_live_ in your config, send one real fax to a known-good destination, and watch the same event sequence land.

What to do next

  • Verify webhook signatures - Standard Webhooks library snippets in five languages, plus key rotation guidance.
  • Idempotency - retry semantics, key lifetime, and what happens on replay.
  • Errors - the stable error catalog with HTTP statuses, causes, and next actions.
  • Event types - every event type, payload shape, and delivery behavior.
  • Sandbox - magic numbers and simulated failure scenarios.
Last modified on May 14, 2026