Fortnox API — authenticationAuthenticating with the Fortnox API
The Fortnox API requires OAuth 2.0 for every call — there's no static API key to paste in and forget. Human/user connections use the authorization-code flow described below; Fortnox also supports pre-authorized service accounts through the client-credentials grant. This guide focuses on the human flow, verified endpoints, scopes, and common errors.
Overview: how authentication works
Fortnox supports two OAuth 2.0 grants. Human/user connections use the authorization-code grant and customer login described in this guide. Pre-authorized service accounts use the client-credentials grant with a tenant identifier and do not run an interactive login for each token.
Both flows start in the Fortnox Developer Portal, where you register the integration and receive a Client ID and a Client Secret. They authenticate requests to the token endpoint; the resulting access token is what authorizes API resource calls.
For the human authorization-code flow, the customer — Fortnox calls them the resource owner — signs into their Fortnox account and approves the requested scopes. A service account instead has to be created and pre-authorized for the integration and tenant before client credentials can mint an access token.
A successful human authorization-code flow returns an access token and a refresh token. The client-credentials flow returns a tenant-scoped service-account access token instead. In both cases, resource calls send the access token as a Bearer token.
Step by step: the OAuth flow
1 · Your app
Authorization URL
apps.fortnox.se/oauth-v1/auth with Client ID, scopes, redirect URI and state
2 · Customer in Fortnox
Signs in and approves
Fortnox lists exactly which scopes are requested
3–4 · Your backend
Exchanges the code for a token pair
POST to apps.fortnox.se/oauth-v1/token with Basic auth
5–6 · Then
Call e.g. /3/invoices with the access token as a Bearer token, and refresh with the refresh token before it expires.
- 1
Send the customer to the authorization URL
The link points at apps.fortnox.se/oauth-v1/auth and carries your Client ID, the scopes the integration is requesting, your redirect URI, and a random state string you verify when the customer comes back. In practice this link opens in the customer's browser, but written as curl -G the parameters are easy to read:
curl -G "https://apps.fortnox.se/oauth-v1/auth" \ --data-urlencode "client_id=YOUR_CLIENT_ID" \ --data-urlencode "redirect_uri=https://your-app.example.com/callback" \ --data-urlencode "scope=invoice customer bookkeeping" \ --data-urlencode "state=RANDOM_STATE_STRING" \ --data-urlencode "response_type=code" \ --data-urlencode "access_type=offline" - 2
The customer signs in and approves the scopes
Fortnox lists exactly which scopes the integration is requesting. The customer signs in with their own Fortnox account and approves only what's shown — no scope is added later without a fresh approval.
- 3
Fortnox sends an authorization code to your redirect URI
The code is valid for ten minutes, so the exchange for a token pair has to happen in the same request cycle. Hold onto it for later and it expires before you get to use it.
- 4
Your backend exchanges the code for a token pair
A POST request to the token endpoint, authenticated with your Client ID and Client Secret as Basic auth, returns an access token and a refresh token:
curl -X POST "https://apps.fortnox.se/oauth-v1/token" \ -H "Authorization: Basic $(printf '%s' 'YOUR_CLIENT_ID:YOUR_CLIENT_SECRET' | base64)" \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=authorization_code" \ --data-urlencode "code=AUTHORIZATION_CODE_FROM_REDIRECT" \ --data-urlencode "redirect_uri=https://your-app.example.com/callback" - 5
Use the access token as a Bearer token
Every subsequent call to a resource, for example /3/invoices, sends the access token in the Authorization header. If that call succeeds, the integration is live.
- 6
Refresh before the access token expires
Same token endpoint, but with grant_type set to refresh_token. A successful refresh can hand you a new refresh token too — always keep the latest one, never the original:
curl -X POST "https://apps.fortnox.se/oauth-v1/token" \ -H "Authorization: Basic $(printf '%s' 'YOUR_CLIENT_ID:YOUR_CLIENT_SECRET' | base64)" \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=refresh_token" \ --data-urlencode "refresh_token=YOUR_REFRESH_TOKEN"
Scopes: what permissions actually exist?
Fortnox splits the API into 24 scopes, and an approved token only reaches the scopes that were listed in the authorization link and that the customer actually approved at activation — never the whole account. Need payroll data? Request salary. Only need customer invoices? invoice is enough. The list below is pulled straight from Nordsynk's own scope configuration, not from memory.
The rule is least privilege: request only the scopes the integration actually needs for the job at hand. A scope you never requested can't leak data, and an overly broad approval is harder for the customer to trust and harder for you to justify in a security review. If the integration needs more later, the customer has to approve the new scopes explicitly — it never happens quietly in the background.
| Scope | What it grants access to |
|---|---|
| archive | Files and folders in the document archive |
| article | The article register — products and services |
| assets | Fixed assets and depreciation |
| bookkeeping | Vouchers and the general ledger |
| companyinformation | Basic company details |
| connectfile | Attach archive files to invoices and vouchers |
| costcenter | Cost centers |
| currency | Currency exchange rates |
| customer | The customer register |
| inbox | The inbox for incoming supplier invoices |
| invoice | Customer invoices |
| noxfinansinvoice | Invoice purchasing via Noxfinans |
| order | Customer orders |
| payment | Incoming and outgoing payments |
| price | Price lists |
| Print templates and PDF generation | |
| profile | The signed-in user's profile |
| project | Projects |
| salary | Payroll and salary records |
| salesapi | Fortnox Sales API — orders and quotes for e-commerce |
| settings | Company settings, such as the fiscal year |
| supplier | The supplier register |
| supplierinvoice | Supplier invoices: approval, payment, bookkeeping |
| timereporting | Time reporting |
Common authentication errors
Most Fortnox authentication problems fall into three categories — and only one of them is actually a bug in your code.
401 Unauthorized
The access token is missing, malformed, or expired — it only lives for one hour. An integration that doesn't refresh proactively hits this regularly, especially in long-running background jobs.
403 Forbidden
Almost always a scope problem: the token lacks access to the resource because that scope was never listed in the authorization link, or the customer never approved it at activation. Add the scope and have the customer approve again.
Missing app license (error_missing_app_license)
If Fortnox returns error_missing_app_license or the text Resource owner does not have enough licenses, that's not a code bug — it's a Fortnox licensing requirement. The connecting company doesn't have an active license for the integration in its Fortnox Marketplace, or that specific user doesn't have an assigned seat. The customer fixes this in Fortnox: activate or buy the license and assign the user a seat, not by debugging the OAuth flow.
Token management in practice
What lives how long
| Credential | Valid for | Do this |
|---|---|---|
| Authorization code | Valid for10 minutes | Do thisExchange in the same request cycle |
| Access token | Valid for1 hour | Do thisRefresh proactively, don't wait for a 401 |
| Refresh token | Valid for— | Do thisStore the latest one returned |
Refresh before it expires
For the human authorization-code flow, don't wait for a 401. Refresh proactively before the one-hour access token expires, and always store the latest refresh token returned. Service accounts instead mint a new access token with client credentials and TenantId; they do not use a refresh token.
Store the Client Secret and tokens securely
The Client Secret and refresh tokens should never reach a browser or a mobile app — they belong encrypted on the server, protected like a password. If the Client Secret leaks, rotate it in the Developer Portal right away; if a customer's refresh token leaks, that customer has to disconnect and re-approve the integration.
Human tokens are user- and company-bound
An authorization-code token pair represents the approving user in one Fortnox company and must not be shared across users or companies. That rule is scoped to human connections: a pre-authorized service account uses client credentials and a TenantId rather than a user's authorization-code token pair.
Nordsynk runs this entire flow for you
The OAuth flow above, proactive token refresh, secure Client Secret storage, and normalizing licensing and scope errors are infrastructure you'd otherwise build and maintain before writing a line of business logic. Nordsynk runs the whole chain and exposes Fortnox through a single MCP endpoint in Claude, ChatGPT, or Cursor — the customer signs in once per company, then you ask in plain language instead of renewing tokens.
Fortnox MCP server: the whole API in one endpoint →Frequently asked questions about Fortnox authentication
Does Fortnox have API keys, or only OAuth?
Fortnox uses OAuth 2.0 rather than a standalone API key in resource requests. Client ID and Client Secret authenticate the token request for both human authorization-code connections and pre-authorized service-account client credentials; resource calls use the resulting access token.
How long does an access token last?
A Fortnox access token is valid for one hour (3,600 seconds). Renew a human authorization-code connection with its refresh token; for a service account, mint a new token with client credentials and TenantId.
Can multiple users share one token?
A human authorization-code token must not be shared: it represents one approving user in one company. This does not describe service-account tokens, which are minted with the client-credentials grant for a pre-authorized tenant context.