Partner API

FaxSeal Partner API

Provision fax numbers for your users, send and receive faxes programmatically, extract structured data with AI, and react to delivery events via webhooks — without managing phone infrastructure.

Overview

The FaxSeal Partner API gives you full inbound and outbound fax capability under a single key.

  • Every partner gets a Partner Key — used as X-Partner-Key on all requests
  • Users are identified by externalUserId — a stable ID from your system (e.g. user_42)
  • Provision a dedicated fax number per user — numbers are owned and billed to your partner account
  • Send outbound faxes from any provisioned number, billed per credit
  • Webhook events fire for inbound (fax.received, fax.extracted) and outbound (fax.sending, fax.delivered, fax.failed)

Base URL: https://faxseal.com/api/partner

Authentication

All requests require your partner key in the X-Partner-Key header.

curl https://faxseal.com/api/partner/inbox \
  -H "X-Partner-Key: sk_partner_YOUR_KEY_HERE"

Keep your key secret — it has full access to all users in your partner account.Get a key →

Numbers API

Provision a dedicated fax number for a user. Numbers persist until you release them. See your partner dashboard for current per-number pricing.

POST /api/partner/numbers — provision

curl -X POST https://faxseal.com/api/partner/numbers \
  -H "X-Partner-Key: sk_partner_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{"externalUserId":"user_42","areaCode":"415","tier":"personal"}'

Response 200:
{
  "faxNumber": "+14155550123",
  "city": "San Francisco",
  "state": "CA",
  "tier": "personal",
  "webhookConfigured": true
}

tier: personal (default) or business. Returns 409 if the user already has a number.

GET /api/partner/numbers?externalUserId=user_42 — lookup

Response 200:
{ "active": true, "faxNumber": "+14155550123", "tier": "personal" }

DELETE /api/partner/numbers?externalUserId=user_42 — release

Response 200: { "ok": true }

Send API — File or URL New

Send an outbound fax from a user's provisioned number. Deducts credits from your partner balance. The user must have a provisioned fax number (see Numbers API) — it becomes the caller ID automatically.

Application-level sends (no provisioned number needed): If your integration sends faxes on behalf of your application rather than individual users — such as automated notifications, referrals, or outreach — use Send HTML (POST /api/v1/fax/html) instead. It does not require externalUserId or a provisioned number.

Accepts multipart/form-data (file upload) or application/json (remote URL). PDF only. Max 20 MB. Pages are counted before billing.

POST /api/partner/fax — send via file upload

curl -X POST https://faxseal.com/api/partner/fax \
  -H "X-Partner-Key: sk_partner_YOUR_KEY_HERE" \
  -F "toNumber=+12125550001" \
  -F "externalUserId=user_42" \
  -F "callbackUrl=https://yourapp.com/api/fax-status" \
  -F "[email protected]"

Response 202:
{
  "jobId": "clxyzabc",
  "status": "queued",
  "pages": 3,
  "creditsUsed": 3,
  "creditsRemaining": 97
}

POST /api/partner/fax — send via URL

curl -X POST https://faxseal.com/api/partner/fax \
  -H "X-Partner-Key: sk_partner_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{
    "toNumber": "+12125550001",
    "externalUserId": "user_42",
    "fileUrl": "https://yourapp.com/documents/form.pdf",
    "callbackUrl": "https://yourapp.com/api/fax-status",
    "callbackSecret": "whsec_..."
  }'
FieldRequiredDescription
toNumberYesDestination fax number in E.164 format
externalUserIdYesIdentifies the sender — must have a provisioned number
fileOne ofPDF file (multipart upload)
fileUrlOne ofHTTPS URL to a PDF (fetched server-side)
callbackUrlNoOverrides your default webhook for this fax only
callbackSecretNoHMAC secret for signing this fax's callbacks

Credit cost = dialing zone rate × page multiplier (same formula as regular credit sends). Returns 402 if balance is insufficient — top up via Credits API. Returns 422 if the user has no provisioned number.

Send HTML App-level

Pass raw HTML and FaxSeal renders it to a print-quality PDF server-side, then faxes it. Ideal for templated letters, referral notices, automated notifications, and any workflow where you generate document content at runtime rather than uploading a file.

Unlike Send via file/URL, this endpoint does not require an externalUserId or a provisioned fax number. The outbound caller ID is FaxSeal's shared system number. Use externalUserIdoptionally for attribution in usage reports and credit logs.

POST /api/v1/fax/html

curl -X POST https://faxseal.com/api/v1/fax/html \
  -H "X-Partner-Key: sk_partner_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{
    "html": "<h1>Patient Referral</h1><p>Care type: Home health...</p>",
    "toNumber": "+12125550001",
    "recipientName": "Acme Home Health",
    "subject": "Patient referral notice — ZIP 84770",
    "callbackUrl": "https://yourapp.com/api/fax-status",
    "callbackSecret": "whsec_..."
  }'

Response 202:
{
  "jobId": "cmsxyzabc",
  "status": "queued",
  "pages": 1,
  "creditsUsed": 1,
  "creditsRemaining": 998
}
FieldRequiredDescription
htmlYesRaw HTML string. Max 500,000 characters. Inline styles preserved; external stylesheets not loaded.
toNumberYesDestination fax number in E.164 format (e.g. +12025551234)
recipientNameNoStored on the fax job record. Useful for delivery reports.
subjectNoStored on the fax job record. Useful for delivery reports.
externalUserIdNoOptional attribution ID for usage stats and credit logs. No provisioned number required.
callbackUrlNoOverrides your default webhook URL for this fax only.
callbackSecretNoHMAC secret for signing this fax's callbacks. Overrides account default.

HTML rendering notes

  • Rendered with a headless Chromium browser at print resolution
  • External network requests (fonts, images, stylesheets) are blocked — inline everything
  • Inline data: URIs work for images and QR codes
  • Page size: A4. Margins: 20 mm top/bottom, 15 mm left/right
  • Fax transmits in black and white — avoid relying on color alone to convey meaning
  • Returns 429 if the render queue is full — retry with exponential backoff

Check Fax Status

Poll the delivery status of any outbound fax created by your partner account — whether sent via file upload or HTML. Status progresses: queuedsendingdelivered or failed.

For webhook-driven integrations you generally don't need to poll — use webhook events instead. Poll only when you need a synchronous status check (e.g. admin dashboards, reconciliation jobs).

GET /api/partner/fax/{jobId}

curl https://faxseal.com/api/partner/fax/cmsxyzabc \
  -H "X-Partner-Key: sk_partner_YOUR_KEY_HERE"

Response 200:
{
  "jobId": "cmsxyzabc",
  "status": "delivered",
  "toNumber": "+12125550001",
  "fromNumber": "+14352700716",
  "pages": 1,
  "creditsUsed": 1,
  "externalUserId": "user_42",
  "durationSeconds": 38,
  "certReceiptUrl": "https://cdn.faxseal.com/receipts/cmsxyzabc.pdf",
  "createdAt": "2026-08-04T15:41:33Z"
}

// fax.failed also includes:
{
  "status": "failed",
  "failureReason": "no-answer"   // busy | no-answer | failed | canceled
}

Returns 404 if the job does not belong to your partner account.certReceiptUrl is only present after confirmed delivery.

Inbox API

GET /api/partner/inbox?externalUserId=user_42

Returns received faxes for a user. Supports page, q (OCR search), and from (sender filter).

GET /api/partner/inbox?externalUserId=user_42

Response 200:
{
  "faxes": [
    {
      "id": "clxyzabc",
      "externalUserId": "user_42",
      "fromNumber": "+15125551234",
      "toNumber": "+14155550123",
      "pages": 2,
      "ocrText": "Patient name: Jane Smith...",
      "ocrStatus": "done",
      "extractedData": { "patient_name": "Jane Smith", ... },
      "extractStatus": "done",
      "fileUrl": "https://faxseal.com/api/partner/inbox/clxyzabc/file?token=...",
      "receivedAt": "2026-04-18T14:22:00Z"
    }
  ],
  "total": 1,
  "page": 1,
  "pages": 1
}

Webhook events

Set your default webhook URL via the Settings API. You can also override per fax using callbackUrl on the send request. FaxSeal delivers with up to 3 retry attempts (1 min → 5 min → 25 min) and a 10-second timeout.

EventDirectionWhen fired
fax.receivedInboundOCR complete (~30s after receipt)
fax.extractedInboundAI extraction complete (~60s after receipt)
fax.sendingOutboundJob accepted by carrier
fax.deliveredOutboundCarrier confirmed delivery
fax.failedOutboundDelivery failed after all retries

Outbound status callback payload

{
  "event": "fax.delivered",
  "jobId": "clxyzabc",
  "status": "delivered",
  "toNumber": "+12125550001",
  "externalUserId": "user_42",
  "pages": 3,
  "creditsUsed": 3,
  "timestamp": "2026-04-18T14:25:00Z"
}

// fax.failed also includes:
{
  "failureReason": "no-answer"   // busy | no-answer | failed | canceled
}

Securing your endpoint

FaxSeal signs every delivery with HMAC-SHA256 in the x-faxseal-signature header. Format: t=<unix_ts>,v1=<hex>. Verify by recomputing the HMAC over <timestamp>.<rawBody>.

// Node.js webhook verification
import { createHmac, timingSafeEqual } from 'crypto';

const rawBody = await req.text();
const sig = req.headers.get('x-faxseal-signature'); // "t=1234567890,v1=abc123..."
const [tPart, v1Part] = sig.split(',');
const ts = tPart?.slice(2);
const expected = createHmac('sha256', process.env.FAXSEAL_WEBHOOK_SECRET)
  .update(`${ts}.${rawBody}`)
  .digest('hex');
const received = v1Part?.slice(3) ?? '';

if (!ts || !received || !timingSafeEqual(Buffer.from(expected), Buffer.from(received))) {
  return Response.json({ error: 'Unauthorized' }, { status: 401 });
}
// Respond 2xx quickly — process asynchronously

Credits New

Outbound faxes are charged in credits using a destination × page formula. Destination zone: 1 credit (US/Canada), 2 credits (Europe, AU, JP, and major markets), 3 credits (worldwide). Page tier multiplier: ×1 (1–5 pages), ×2 (6–15 pages), ×3 (16+ pages). Example: a 10-page fax to Germany costs 2 × 2 = 4 credits. Maximum is 9 credits per fax. Credits never expire.

GET /api/partner/credits — check balance

curl https://faxseal.com/api/partner/credits \
  -H "X-Partner-Key: sk_partner_YOUR_KEY_HERE"

Response 200:
{
  "credits": 97,
  "bundles": [
    { "id": "partner_100",  "label": "100 credits",   "amountCents": 3900,  "credits": 100  },
    { "id": "partner_500",  "label": "500 credits",   "amountCents": 14900, "credits": 500,  "popular": true },
    { "id": "partner_2000", "label": "2,000 credits", "amountCents": 49900, "credits": 2000 }
  ]
}
// Note: bundle prices are configured per-partner and may differ from the defaults shown above.

POST /api/partner/credits — create top-up checkout

Returns a Stripe Checkout URL. Redirect the operator (or your billing page) to complete payment. Credits are added immediately after payment confirmation.

curl -X POST https://faxseal.com/api/partner/credits \
  -H "X-Partner-Key: sk_partner_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{"bundle":"partner_500","successUrl":"https://yourapp.com/billing/success"}'

Response 200:
{
  "url": "https://checkout.stripe.com/c/pay/...",
  "sessionId": "cs_live_..."
}

A low-balance warning email is sent to your billing address when credits drop below 10.

AI Extraction

When an extractionSchema is set on your account, FaxSeal runs GPT-4o-mini over OCR text and fires a fax.extracted webhook with structured JSON.

Schema format

{
  "type": "patient_referral",
  "fields": [
    { "name": "patient_name",  "type": "string"  },
    { "name": "dob",           "type": "string"  },
    { "name": "diagnosis",     "type": "string"  },
    { "name": "medications",   "type": "array"   },
    { "name": "referring_npi", "type": "string"  }
  ]
}

fax.extracted event payload

{
  "event": "fax.extracted",
  "faxId": "clxyzabc",
  "externalUserId": "user_42",
  "fromNumber": "+15125551234",
  "fileUrl": "https://faxseal.com/api/partner/inbox/clxyzabc/file?token=...",
  "ocrText": "PATIENT: Jane Smith DOB: 1980-03-15...",
  "extracted": {
    "patient_name": "Jane Smith",
    "dob": "1980-03-15",
    "diagnosis": "Type 2 Diabetes",
    "medications": ["Metformin 500mg", "Lisinopril 10mg"],
    "referring_npi": "1234567890"
  }
}

Usage API New

Query inbound and outbound activity for your partner account or for a specific user. Defaults to the current calendar month.

curl "https://faxseal.com/api/partner/usage?externalUserId=user_42&from=2026-04-01" \
  -H "X-Partner-Key: sk_partner_YOUR_KEY_HERE"

Response 200:
{
  "from": "2026-04-01T00:00:00.000Z",
  "to": "2026-04-30T23:59:59.999Z",
  "externalUserId": "user_42",
  "credits": { "remaining": 97 },
  "outbound": {
    "total": 12,
    "delivered": 10,
    "failed": 1,
    "queued": 1,
    "sending": 0,
    "creditsUsed": 34
  },
  "inbound": {
    "total": 8,
    "pages": 22
  }
}

Settings API

GET /api/partner/settings

Response 200:
{
  "name": "AcmeHealth",
  "webhookUrl": "https://acmehealth.com/api/faxseal/webhook",
  "webhookSecret": "***",
  "billingEmail": "[email protected]",
  "extractionSchema": { ... },
  "createdAt": "2026-01-01T00:00:00Z"
}

PATCH /api/partner/settings

Update webhookUrl, webhookSecret, billingEmail, and/or extractionSchema. All fields optional.

curl -X PATCH https://faxseal.com/api/partner/settings \
  -H "X-Partner-Key: sk_partner_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{"billingEmail":"[email protected]","webhookUrl":"https://acmehealth.com/api/faxseal/webhook"}'

Response 200: { "ok": true }

billingEmail is required before purchasing credits via the Credits API.

Partner Portal

Every partner account includes a self-serve web dashboard at /partner. Portal access is separate from the API — it is granted by an admin to specific FaxSeal user accounts.

Accessing the portal

Your FaxSeal account manager assigns you a role on your partner account. Once assigned, a Partner Portal link appears in your FaxSeal dashboard. You can also navigate directly to /partner.

Portal pages

PageWhat you can seeMin. role
OverviewActive numbers, credit balance, this-month sent/received countsAll
UsageJob history, delivery rates, inbound totals, top usersAll
CreditsCredit balance, full transaction logAll
BillingBilling status, grace period, past-due noticesbilling
SettingsAPI key reveal/copy, webhook URL configurationdeveloper
TeamMember list, add/remove members, change rolesowner

Team roles

RolePermissions
ownerFull access + team membership management + ownership transfer
adminOverview, usage, credits, billing, settings, team roster (read-only)
billingOverview, usage, credits, billing status, team roster (read-only)
developerOverview, usage, credits, settings (webhook), team roster (read-only)

Ownership transfer demotes all current owners to admin atomically. An account can only have one owner at a time.

Limits & best practices

  • Rate limit: 60 API requests per minute per partner key.
  • Max file size: 20 MB per fax (file/URL sends). PDF only.
  • Max HTML size: 500,000 characters per /api/v1/fax/html request.
  • HTML rendering: external network requests are blocked — inline all assets as data: URIs.
  • Webhook delivery timeout: 10 seconds. Respond 2xx immediately; process asynchronously.
  • Retry schedule: 1 min → 5 min → 25 min. After 3 failures the delivery is marked failed.
  • Fax files are stored for 30 days, then purged. Download PDFs before expiry.
  • Credits never expire. Low-balance warning sent automatically below 10 credits.
  • Caller ID for file/URL sends: always the user's provisioned number.
  • Caller ID for HTML sends: FaxSeal shared system number (no provisioned number needed).
  • Extraction adds ~30s latency. fax.received fires first; fax.extracted fires after.

Need a partner key?

Verify your email and your key is issued instantly — no waiting, no back-and-forth.

Get a partner key →