Skip to content

Authentication

The API authenticates servers with organization API keys. A key belongs to an organization (not to a person), carries a set of permissions, and works in exactly one mode: test or live.

Use either header:

Terminal window
# Authorization header (recommended)
curl https://api.bonafidesign.com/v1/envelopes -H "Authorization: Bearer bf_live_…"
# or x-api-key
curl https://api.bonafidesign.com/v1/envelopes -H "x-api-key: bf_live_…"

Keys look like bf_live_… or bf_test_…. A missing key is a 401 (unauthorized); a malformed, revoked or expired key is a 401 too. Keep keys on your server: never ship them to a browser or a mobile app. For signing in the browser, mint a signing session instead.

Test (bf_test_…) Live (bf_live_…)
Documents Rendered with a TEST watermark Clean
Email Delivered only to members of your organization Delivered to every recipient
Billing Free and unlimited, never counted Completed envelopes count toward your plan
Data Envelopes, links and webhook endpoints created with the key are test-mode Live-mode

The mode comes from the key, never from the request: ?test_mode=true on POST /v1/envelopes only applies to dashboard sessions. Lists (GET /v1/envelopes, GET /v1/signing-links) only return the key’s mode, and an envelope’s events go only to webhook endpoints of the same mode. Every envelope and webhook payload says which mode it is in (testMode).

Keys are managed in the dashboard, Settings › API keys, by members with the apikey:manage permission (owners and admins). The secret is shown once, when the key is created. You can give a key a name, a mode, an expiry (1 to 365 days, or none) and scopes.

A key’s scopes are the organization permissions it may use. By default a new key gets the creator’s permissions minus the administrative ones (org:manage, member:manage, sso:manage, apikey:manage, billing:manage), and nobody can grant a key a permission they do not have. A call outside the key’s scopes is a 403 naming the missing permission:

{
"type": "https://bonafidesign.com/problems/forbidden",
"title": "Forbidden",
"status": 403,
"detail": "Missing permission: envelope:send.",
"requestId": "req_V1StGXR8Z5jdHi6BmyT2"
}
Permission Lets a key
template:read List and read templates, versions and their PDFs
template:write Create templates, upload files, save drafts, delete unused templates
template:publish Publish a draft as a new version
envelope:read List and read envelopes, documents and audit trails; list signing links
envelope:send Create envelopes, signing sessions and signing links; resend links
envelope:void Void envelopes in progress
org:manage Manage webhook endpoints and redeliver events; read organization settings
billing:manage Read usage (GET /v1/usage) and invoice amounts

Managing webhook endpoints needs org:manage, which keys do not get by default: register endpoints in the dashboard, or create a key with that scope explicitly.

Each operation in the API reference lists its security schemes. In short:

  • API key or dashboard session: templates, envelopes, signing sessions, signing links, webhook endpoints, organization settings, billing overview and usage.
  • Dashboard session only: API keys, SCIM tokens, SSO connection settings, organization logo upload, billing checkout and portal, revealing a webhook secret.
  • Signer token: /v1/signing/*, used by the hosted page and <bf-signing> with a session token (bfs_…) or the access token it is exchanged for (bfa_…).
  • Public: /v1/links/{id} (always-on links), /v1/sso/discover, and the verification API (/v1/public/*, /.well-known/bona-fide/keys.json).

People (not servers) sign in to the dashboard at https://app.bonafidesign.com/sign-in. The default is an email and password; the alternatives are an emailed six-digit code (Email me a code instead), a magic link, a passkey, Google or Microsoft, and your company’s SSO.

  • Creating an account with a password sends one email: a code that confirms the address is yours. Until then the password does not sign you in. Signing up with an address that already has an account changes nothing and answers exactly like a new sign-up; the owner of the address gets an email explaining how to sign in instead.
  • Passwords are 12 to 128 characters and stored as salted scrypt hashes. A new password that appears in a known data breach is refused: only the first five characters of its SHA-1 hash are sent to Have I Been Pwned (k-anonymity), never the password. If that service does not answer within two seconds, the password is accepted without the check.
  • Forgot your password? The reset email links to a page where you choose a new one. The link works once and expires after one hour, and using it signs you out on every device.
  • No password yet? Accounts created with a code, a link, a passkey or Google / Microsoft keep signing in that way, and can add a password in Settings › Account. Setting the first password needs a sign-in in the last 15 minutes (the page emails you a code to confirm it is you); changing it needs the current one. Both sign out your other sessions.
  • Errors never reveal accounts: a wrong password, an unknown address and an account without a password get the same answer, and a reset request always says the email is on its way.
  • Rate limits: password sign-in, sign-up and reset requests are limited per IP address and per email address; past the limit the answer is 429 until the window passes.
  • Company SSO: when your organization enforces SSO, members at its email domains sign in through the identity provider only. Passwords, codes and magic links are refused for them; owners keep the email code as a break-glass sign-in.

Every response carries an x-request-id header, and every error body a requestId. You may send your own x-request-id (8 to 128 characters of A-Z a-z 0-9 . _ -) to correlate logs; otherwise the API generates one. Quote it when you contact support.