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 and are billed monthly ($2.50/mo personal, $7.50/mo business).

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 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 — it becomes the caller ID automatically.

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.

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 } 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');

if (!ts || !v1Part || expected !== v1Part.slice(3)) {
  return Response.json({ error: 'Unauthorized' }, { status: 401 });
}
// Respond 2xx quickly — process asynchronously

Credits New

Outbound faxes are charged in credits. 1 credit covers 1 fax page to a domestic number. International and multi-page faxes use more credits. 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 }
  ]
}

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. PDF only.
  • 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.
  • Outbound caller ID is always the user's provisioned number — cannot be overridden.
  • 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 →