Skip to main content

Authentication

Every MarginFront API call needs an API key. This doc tells you where to get one, how to use it, which type to use when, and how to keep it safe.

How to use an API key

Every request to the MarginFront API includes an x-api-key header with your key:
That’s it. No OAuth dance, no token refresh, no session cookies. One header, one key, every request. With curl:
On Windows the two are not interchangeable. See Windows and PowerShell below for why. With the Node SDK:
The SDK handles the header for you — you just pass the key to the constructor and it takes care of the rest.

Two types of API key

MarginFront issues two kinds of keys, and they have different permissions. This is the same pattern Stripe uses — if you’ve used Stripe’s API before, this will feel familiar.

When to use which

  • Server-side backend code (Node, Python, Go, Ruby, whatever) — use a secret key. Your backend is trusted, so it can do anything.
  • Browser code / mobile app / anywhere the key could leak — use a publishable key. If a visitor reads the key out of your page source, the worst they can do is read public info. They cannot create, update, or delete anything.
  • A hosted billing portal for your customers — neither. Use portal sessions instead, which issue short-lived, customer-scoped tokens.

What happens if you use the wrong one

  • Publishable key trying to POST/PATCH/DELETE → 403 Forbidden with a message telling you to use a secret key
  • Publishable key trying to read any of your organization’s own data (customers, invoices, analytics, credit balances, or your AI-spend / Spend Controls reads) → 403 Forbidden. A publishable key can reach only /v1/verify and its own /v1/me/key. Everything else is your org’s books, not public info, so it needs a secret key.
  • Secret key used in browser code → technically works, but you’ve leaked your main credential to anyone viewing the page. Don’t do this.

Where to get an API key

  1. Log into the MarginFront dashboard
  2. Go to Build → API keys
  3. Either use an existing key or click “Create new key”
  4. Pick the type: secret or publishable
  5. Copy it immediately — you can’t see the full value again after the first time. If you lose it, you’ll have to create a new one.
Keys look like mf_sk_test_... or mf_sk_live_... (secret) and mf_pk_test_... or mf_pk_live_... (publishable), depending on environment.

Checking that your key works

The simplest test is the /v1/verify endpoint. It takes no parameters, just your key. If everything’s set up right, you’ll get a 200 response with your org info.
Success response:
If you get 401: Your key is either missing, malformed, or wrong. Double-check the header name (x-api-key, not Authorization: Bearer). If it’s definitely formatted right, the key itself is probably wrong — go back to the dashboard and copy a fresh one. If you get 401 on Windows and the key is definitely good: you almost certainly sent an empty key. Read Windows and PowerShell below. This accounts for most 401 reports from Windows users. If you get 403: You’re probably using a publishable key where a secret key is required. That’s either a write (POST/PATCH/DELETE), or a read of any of your organization’s own data (customers, invoices, analytics, credit balances, or your AI-spend reads). A publishable key can reach only /v1/verify and its own /v1/me/key; every other authenticated read needs a secret key. See the table above. If you get nothing (connection refused / timeout): The API server isn’t running. If you’re hitting localhost, start it with cd apps/api-nest && npm run dev.

Windows and PowerShell

Examples throughout these docs are written for Bash. If you are on Windows PowerShell, three things change. Miss any of them and you get a 401 that looks like a bad key but is actually an empty one. Why the $env: prefix matters. PowerShell keeps shell variables and environment variables in separate namespaces. $MF_API_SECRET_KEY refers to a PowerShell variable that was never assigned, so it expands to an empty string. The request goes out with an empty x-api-key header and comes back 401. Nothing warns you first. Why curl.exe and not curl. In Windows PowerShell (the 5.1 version preinstalled on every Windows machine), curl is a built-in alias for Invoke-WebRequest, a different command whose parameters do not match curl’s. It fails with a parameter-binding error rather than doing what you meant. PowerShell 7 dropped that alias, but curl.exe is correct in both, so write curl.exe and it works everywhere. Windows 10 and later ship real curl. Sending a JSON body. Put the JSON in a literal here-string, then pipe it to curl. The @' and '@ markers stop PowerShell from touching the contents, and the closing '@ must start at column 0 on its own line.
Pipe the body. Do not pass it as an argument. -d '@-' tells curl to read the body from standard input, which is what the pipe feeds it. The obvious-looking -d $body is broken on Windows PowerShell 5.1 and on PowerShell 7.0 through 7.2. When those versions pass an argument containing spaces to a native program, they strip its embedded double quotes, so your JSON arrives as {name: Acme Inc, externalId: acme-001} and the API rejects it. PowerShell 7.3 changed that behavior, but piping is correct on every version, so use it and stop thinking about versions. Check yours with $PSVersionTable.PSVersion if you want to know. Only arguments carrying embedded double quotes are affected. Headers like -H "x-api-key: ..." and quoted URLs pass through every version unchanged. Quote any URL with a query string. & is reserved in PowerShell, and what an unquoted ...?startDate=X&endDate=Y does depends on your version. Windows PowerShell 5.1 refuses to run the line at all: “The ampersand (&) character is not allowed.” PowerShell 7 treats & as the background-job operator, cuts the command there, runs the truncated URL as a background job, and errors on the rest. Quote the URL and neither happens. Checking the variable is set. Run $env:MF_API_SECRET_KEY.Length. A number means it’s set in this window. A blank line or an error means it isn’t. This tells you what you need without printing the key. Environment variables set this way live only in the terminal window that set them. A new window starts clean. Prefer Bash on Windows? WSL and Git Bash both run the Bash examples as written, including export, $VAR, plain curl, and \ line continuations.

The API key alone identifies your organization

You don’t need to pass your organization ID anywhere. The API key alone tells MarginFront which org you are — the backend looks up your key, finds the org it belongs to, and uses that for the request. Every endpoint under /v1/<resource> works this way. This is the Stripe / Anthropic / OpenAI pattern. One credential, one identity, no redundant IDs to pass around. (If you need to find your org ID for support or debugging purposes, hit /v1/verify with your key and look at the organization.id field in the response.)

Every API call is logged

MarginFront automatically logs every API-key-authenticated request. The audit log captures:
  • The key ID (a non-secret reference to which of your keys was used)
  • Your organization ID
  • The endpoint (HTTP method + URL path)
  • The response status code
  • The IP address of the caller
  • The timestamp
The full secret value of the key is never logged — only its non-secret ID. The audit log is stored in your own database and is only visible to you.

What this means for compliance

If you need to answer “who did what when” for a compliance audit or incident investigation, this is where you look. The data accumulates from the moment your key makes its first call and is retained indefinitely.

Checking your own audit log

(The Usage History UI on Build → API keys is the intended way to view this. If it’s not there yet, you can query the api_key_usage_logs table directly via Prisma Studio in development.)

Keeping your API key safe

The short version

  • Never commit keys to git. Not in code, not in config files, not in comments. Use environment variables.
  • Never ship secret keys to browser code. Secret keys belong on your backend. If you need to show billing data to a customer in their browser, use a publishable key for reads, or portal sessions for customer-specific views.
  • Never share keys across environments. Use separate keys for development, staging, and production.
  • Rotate keys if you suspect a leak. Create a new key in the dashboard, update your code to use it, then revoke the old one.

The longer version

Environment variables are the right pattern. Load the key from an environment variable at runtime:
Both forms last only for the current terminal session. For something permanent, use your deployment platform’s secret manager, or on Windows setx MF_API_SECRET_KEY "mf_sk_your_key_here" followed by a new terminal. This way the key never hard-codes into files that could end up in git or a Docker image layer. For local development: put the key in .env.local (or whatever your framework uses) and make sure that file is in .gitignore. Never put it in .env that gets committed. For production: use your platform’s secret management (Vercel env vars, AWS Secrets Manager, Doppler, etc.). Don’t put production keys in plaintext config files. If a key does leak:
  1. Go to the dashboard immediately
  2. Create a replacement key
  3. Update your code/deployment to use the new key
  4. Revoke the leaked key from the dashboard
  5. Check the audit log (see above) for any unusual activity using the old key