mintfax has three kinds of API key. The prefix is a structural promise: every request is routed based on the prefix the key carries. You pick a key, the routing follows.
The three key kinds
| Prefix | Kind | Scope | Can send faxes | Can manage account |
|---|
mfx_test_ | Sandbox | One sandbox environment | Yes (sandbox) | No |
mfx_live_ | Live | One live environment | Yes (live) | No |
mfx_acct_ | Account | The whole account (no environment operations) | No | Yes |
mfx_test_... - sandbox environment key
Operational key scoped to a single sandbox environment. Sends sandbox faxes (no carrier dispatch). Reads that environment’s sandbox fax history, events, webhooks, and credit balance. Cannot touch live data and cannot call /account/*.
mfx_live_... - live environment key
Operational key scoped to a single live environment. Sends real faxes through the carrier. Reads that environment’s live data. Requires the account to be activated - until the first live credit purchase succeeds, a live key returns account_not_activated on operational routes.
mfx_acct_... - account management key
Account-scoped admin key. Reads and manages account-level resources: balances across all pools, the pool-wide transaction ledger, and the list of API keys. Cannot send faxes and cannot call /environment/*. Think of it as the keys-and-money key, not the send-a-fax key.
The prefix is the sandbox/live wall
mintfax follows Stripe’s sk_test_ / sk_live_ pattern, with a third acct kind for the account namespace. Because the gateway routes on prefix, the wall between sandbox and live is structural, not configurational:
A key never spans sandbox and live. Each environment is one type for its life. To send live, use a live key against a live environment. To send sandbox, use a sandbox key against a sandbox environment. There is no environment toggle in the API.
This is the type-homogeneity rule. If you accidentally point a sandbox key at production code, you do not silently send real faxes - the worst case is that nothing happens, because sandbox keys cannot reach live resources by construction.
Registration provisions two keys
POST /account/register/verify returns both a mfx_test_... key and a mfx_acct_... key in the same response. Store both. They have different jobs:
- Use the sandbox key for your first sandbox fax in dev. It is scoped to the sandbox environment created at registration.
- Use the acct key when you need to programmatically manage environments, balances, or other keys. For example: listing all your API keys, reading the live and sandbox balances side by side, or creating a new live environment key after you activate.
{
"account": { "id": "acct_4HGhJ7K2pNm9XqL3VtR8Wz", "name": "Acme Corp" },
"user": { "id": 42, "email": "dev@acme.com" },
"environment": { "id": "env_2BcD3eF4gH5iJ6kL7mN8oP", "name": "Sandbox", "type": "sandbox" },
"api_key": "mfx_test_abc123def456...",
"api_key_account": "mfx_acct_qpr456stv789..."
}
Both plain-text keys are shown once only. Save them to a secrets manager or .env file before navigating away.
Creating more keys
Environment keys (test or live)
POST /account/keys with scope_kind=test or scope_kind=live creates an environment-scoped key. Use an mfx_acct_... key to authenticate the call:
curl -X POST https://api.mintfax.com/v1/account/keys \
-H "Authorization: Bearer mfx_acct_qpr456stv789..." \
-H "Content-Type: application/json" \
-d '{
"name": "CI pipeline key",
"scope_kind": "test",
"environment_id": "env_2BcD3eF4gH5iJ6kL7mN8oP"
}'
The response includes the plain-text key once. Store it immediately.
Account keys
Subsequent mfx_acct_... keys cannot be created via the API. POST /account/keys with scope_kind=account returns HTTP 403 with acct_key_creation_dashboard_only:
{
"error": "acct_key_creation_dashboard_only",
"message": "Account-scoped keys can only be created via the dashboard.",
"action": "use_dashboard",
"docs": "https://mintfax.com/docs/errors/acct-key-creation-dashboard-only"
}
Rationale: account keys are admin-equivalent for the account. Restricting their lifecycle to the dashboard adds a human-loop attestation step, so an API-only chain of compromise cannot escalate from a stolen environment key to full account control.
Revocation
| Key kind | Dashboard | API (DELETE /account/keys/{id} with acct key) |
|---|
mfx_test_ (sandbox) | Yes | Yes |
mfx_live_ (live) | Yes | Yes |
mfx_acct_ (account) | Yes | Dashboard only (API returns 403) |
Deleting an environment key via the API:
curl -X DELETE https://api.mintfax.com/v1/account/keys/key_8aZqRm4yT3vK7pNxJ2bH9c \
-H "Authorization: Bearer mfx_acct_qpr456stv789..."
Revocation is effective on the next request - there is no caching layer to wait on. A revoked key returns api_key_invalid.
Which key for which endpoint
The API splits cleanly into two surfaces:
- Operational (
/faxes, /environment/*, /webhooks, /events) - environment keys only.
- Account (
/account/*) - account keys only.
| Endpoint | mfx_test_ | mfx_live_ | mfx_acct_ |
|---|
POST /faxes | yes | yes | no |
GET /faxes/{id} | yes | yes | no |
GET /environment/balance | yes | yes | no |
GET /environment/* | yes | yes | no |
GET /account | no | no | yes |
GET /account/balance | no | no | yes |
GET /account/transactions | no | no | yes |
GET /account/keys | no | no | yes |
POST /account/keys | no | no | yes |
When a key hits a route it is not scoped for, the API returns HTTP 403 with a specific code:
These codes are stable. Match on them programmatically if you need to fall back to a different key in your client.
Worked example: agent registration flow
A coding agent registers, stores both keys, sends a sandbox fax, then later switches to live.
# 1. Start registration. Verification code goes to the email.
curl -X POST https://api.mintfax.com/v1/account/register \
-H "Content-Type: application/json" \
-d '{"name": "Acme Corp", "email": "dev@acme.com"}'
# 2. Verify with the 6-digit code. Returns BOTH keys.
curl -X POST https://api.mintfax.com/v1/account/register/verify \
-H "Content-Type: application/json" \
-d '{"email": "dev@acme.com", "code": "482901"}'
# Response includes:
# "api_key": "mfx_test_abc123def456..." <- sandbox environment
# "api_key_account": "mfx_acct_qpr456stv789..." <- account admin
# 3. Store both. The agent persists them to its secrets store.
echo "MINTFAX_TEST_KEY=mfx_test_abc123def456..." >> .env
echo "MINTFAX_ACCT_KEY=mfx_acct_qpr456stv789..." >> .env
# 4. Send a first sandbox fax with the sandbox key.
# Sandbox accepts only the +1 (500) 555-XXXX magic numbers - see /sandbox.
curl -X POST https://api.mintfax.com/v1/faxes \
-H "Authorization: Bearer mfx_test_abc123def456..." \
-F "to=+15005550001" \
-F "file=@invoice.pdf"
# 5. Later, after activating live in the dashboard, mint a live environment key
# using the account key.
curl -X POST https://api.mintfax.com/v1/account/keys \
-H "Authorization: Bearer mfx_acct_qpr456stv789..." \
-H "Content-Type: application/json" \
-d '{
"name": "production",
"scope_kind": "live",
"environment_id": "env_LiVe9AbC8DeF7GhI6JkL5M"
}'
# 6. Swap the sandbox key for the new live key in production code.
curl -X POST https://api.mintfax.com/v1/faxes \
-H "Authorization: Bearer mfx_live_xyz789wvu654..." \
-F "to=+15125550100" \
-F "file=@invoice.pdf"
The acct key never sends faxes. The environment keys never read account-level data. The prefix tells the gateway everything it needs to know.
Related pages