Skip to content
FortnoxFortnox API — authentication

Authenticating 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.

The code only travels one way: from Fortnox to your redirect URI, and is exchanged for tokens straight away.
  1. 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. 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. 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. 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. 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. 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.

ScopeWhat it grants access to
archiveFiles and folders in the document archive
articleThe article register — products and services
assetsFixed assets and depreciation
bookkeepingVouchers and the general ledger
companyinformationBasic company details
connectfileAttach archive files to invoices and vouchers
costcenterCost centers
currencyCurrency exchange rates
customerThe customer register
inboxThe inbox for incoming supplier invoices
invoiceCustomer invoices
noxfinansinvoiceInvoice purchasing via Noxfinans
orderCustomer orders
paymentIncoming and outgoing payments
pricePrice lists
printPrint templates and PDF generation
profileThe signed-in user's profile
projectProjects
salaryPayroll and salary records
salesapiFortnox Sales API — orders and quotes for e-commerce
settingsCompany settings, such as the fiscal year
supplierThe supplier register
supplierinvoiceSupplier invoices: approval, payment, bookkeeping
timereportingTime 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

Authorization codeValid for10 minutesDo thisExchange in the same request cycle
Access tokenValid for1 hourDo thisRefresh proactively, don't wait for a 401
Refresh tokenValid for—Do thisStore the latest one returned
Short-lived credentials are refreshed proactively. Always keep the latest refresh token.

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.