How to Integrate with the Fortnox API

Complete guide to Fortnox API integration: OAuth 2.0 auth, invoice and payment endpoints with code examples, pagination, rate limits, and webhooks.

Kateryna PoryvayKateryna Poryvay

Kateryna Poryvay · Growth Marketer, Apideck

8 min read
How to Integrate with the Fortnox API

Fortnox is the dominant accounting platform in Sweden, used by more than 600,000 businesses across the Nordics. For SaaS companies, fintechs, and vertical software vendors targeting that market, a Fortnox API integration is not a nice-to-have. It's a table-stakes requirement.

This guide walks through everything you need to build a working Fortnox API integration: OAuth 2.0 authentication, token management, key endpoints with real request and response examples, the pagination model, webhook limitations, and the rate limits that will bite you at scale if you don't plan for them.

Setting up your Fortnox developer account

Before you can make any Fortnox API calls, you need to register as a developer at apps.fortnox.se/developer. This gives you access to the Developer Portal where you create your integration and receive your Client ID and Client Secret.

One constraint for international teams: Fortnox requires a Swedish personal or organization ID to complete registration. If your company is not Swedish, you'll need to partner with a local entity or use a third-party sandbox provider to start testing. This is worth confirming before you schedule any development sprints.

Once registered, you create an integration record. This is where you configure your redirect URI, define your requested scopes, and optionally submit for Fortnox Marketplace listing.

OAuth 2.0 authentication

Fortnox uses the OAuth 2.0 Authorization Code Flow. The old fixed access token authentication was fully deprecated on April 30, 2025. Any integration still using the legacy Client-Secret / Access-Token header combination needs to migrate.

Step one is sending the user to the Fortnox authorization URL:

GET https://apps.fortnox.se/oauth-v1/auth
  ?client_id=YOUR_CLIENT_ID
  &redirect_uri=https://yourapp.com/callback
  &scope=invoice%20customer%20companyinformation
  &state=RANDOM_CSRF_TOKEN
  &access_type=offline
  &response_type=code

access_type=offline is required to receive a refresh token. The state value should be a random string you verify on callback. For a service account not tied to a specific user, add account_type=service.

After the user authorizes your app, Fortnox redirects to your redirect URI with an authorization code valid for 10 minutes. Exchange it immediately:

curl -X POST https://apps.fortnox.se/oauth-v1/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
  -d "grant_type=authorization_code&code=AUTH_CODE&redirect_uri=https://yourapp.com/callback"

The response:

{
  "access_token": "eyJhbGc...",
  "refresh_token": "def50200...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "invoice customer companyinformation"
}

Access tokens expire in one hour. Refresh tokens are valid for 45 days and are rotated on every use. When you refresh, store the new refresh token immediately and discard the old one:

curl -X POST https://apps.fortnox.se/oauth-v1/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
  -d "grant_type=refresh_token&refresh_token=YOUR_REFRESH_TOKEN"

All subsequent API requests use the access token as a Bearer token:

Authorization: Bearer YOUR_ACCESS_TOKEN

Scopes

Fortnox scopes grant both read and write access per resource. There is no read-only variant. Requesting invoice gives write access to invoices, even if your use case is read-only sync. Be explicit in your consent screen about what write permissions you're granting.

Common scopes: invoice, supplierinvoice, customer, supplier, payment, bookkeeping, companyinformation, salary.

One planning note: scopes cannot be silently added to existing connections. If you need to expand scope after users have connected, they must reactivate. Get this right before go-live.

The invoice API

Invoices are the core of most Fortnox integrations. The invoice endpoint is at https://api.fortnox.se/3/invoices.

List invoices

A GET to the list endpoint returns a summary object per invoice, not the full record. This is by design. Here's what a list response looks like:

curl https://api.fortnox.se/3/invoices?limit=10&filter=unpaid \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
{
  "MetaInformation": {
    "@TotalResources": 142,
    "@TotalPages": 15,
    "@CurrentPage": 1
  },
  "Invoices": [
    {
      "@url": "https://api.fortnox.se/3/invoices/204",
      "Balance": "5000",
      "CustomerName": "Acme AB",
      "CustomerNumber": "100",
      "DocumentNumber": "204",
      "DueDate": "2025-06-15",
      "InvoiceDate": "2025-05-15",
      "Total": "5000"
    }
  ]
}

Notice what's missing: line items, tax codes, payment terms, address fields. To get the full invoice, you need a second call:

curl https://api.fortnox.se/3/invoices/204 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
{
  "Invoice": {
    "@url": "https://api.fortnox.se/3/invoices/204",
    "Address1": "Industrivägen 1",
    "Balance": "5000",
    "CustomerName": "Acme AB",
    "CustomerNumber": "100",
    "DocumentNumber": "204",
    "DueDate": "2025-06-15",
    "InvoiceDate": "2025-05-15",
    "InvoiceRows": [
      {
        "ArticleNumber": "ART-001",
        "Description": "Consulting services",
        "DeliveredQuantity": "10.00",
        "Price": "500",
        "Total": "5000"
      }
    ],
    "Total": "5000",
    "VAT": "1250",
    "VATIncluded": false
  }
}

This two-call pattern applies to most list endpoints. For any sync that needs complete data, your request volume doubles.

Create an invoice

curl -X POST https://api.fortnox.se/3/invoices \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "Invoice": {
      "CustomerNumber": "100",
      "InvoiceDate": "2025-05-01",
      "DueDate": "2025-05-30",
      "InvoiceRows": [
        {
          "ArticleNumber": "ART-001",
          "DeliveredQuantity": "5.00",
          "Price": "1000"
        }
      ]
    }
  }'

The customer (CustomerNumber) must already exist in Fortnox. If you're creating invoices for new customers, create the customer record first via POST /3/customers.

Record a payment

Once an invoice is paid, mark it with an invoice payment:

curl -X POST https://api.fortnox.se/3/invoicepayments \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "InvoicePayment": {
      "InvoiceNumber": "204",
      "Amount": 5000,
      "PaymentDate": "2025-05-28",
      "ModeOfPaymentAccount": "1930"
    }
  }'

ModeOfPaymentAccount is the bank account number in Fortnox's chart of accounts where the payment lands.

Pagination

The Fortnox API pagination uses limit (default 100, max 500) and offset parameters. MetaInformation in list responses tells you total pages and current position:

# First page
GET /3/invoices?limit=500&offset=0

# Second page
GET /3/invoices?limit=500&offset=500

The @TotalResources field gives you the total count. For a complete sync of 2,500 invoices at limit=500, that's 5 pages (5 list calls) plus one detail call per invoice (2,500 calls), totaling 2,505 requests.

Rate limits

Fortnox enforces a limit of 25 requests per 5-second window, which equals 300 requests per minute. Exceeding this returns a 429 status. Given the two-call pattern, a full sync of 2,500 invoices requires 2,505 requests, which takes at minimum 8-9 minutes at max throughput even without any throttling.

Implement exponential backoff on 429 responses. Avoid running parallel sync loops against the same connection. For initial historical syncs, schedule them during off-peak hours.

Error handling

Common HTTP status codes you'll encounter:

StatusMeaning
200Success
400Bad request (missing required field, invalid value)
401Unauthorized (expired or invalid token)
403Forbidden (insufficient scope)
404Resource not found
429Rate limit exceeded

Fortnox returns error details in the response body:

{
  "ErrorInformation": {
    "Error": 2000552,
    "Message": "Customer not found",
    "Code": 404
  }
}

Always check ErrorInformation in non-2xx responses.

Webhooks

Fortnox supports webhooks natively for customers and invoices. For most other objects (supplier invoices, vouchers, payments, accounts), there's no native webhook. If your integration needs to react to changes across the full accounting object model, you'll need a polling layer for the gaps.

The Fortnox Marketplace

If you want to distribute your integration to Fortnox users, publishing on the Fortnox Marketplace is the route. The process involves submitting your integration for review, providing a security whitepaper, and meeting Fortnox's technical requirements. Integrations can be kept hidden for private use while still using your Client ID for direct activation, which is useful during testing.

Marketplace pricing can be handled by Fortnox directly (they invoice your customers and handle revenue sharing) or by redirecting users to your own website for billing.

Direct integration vs. a unified API

Building directly against Fortnox makes sense when Fortnox is the only accounting platform you need. The documentation is solid, the REST structure is predictable, and the OAuth 2.0 implementation is standard enough that most of it won't surprise you.

The calculus shifts when Fortnox is one of several platforms on your roadmap. The two-call pattern, partial webhook support, all-or-nothing scopes, and the Swedish registration requirement all add up. If you're also integrating with other European accounting platforms like Pennylane or SnelStart alongside Fortnox, maintaining each integration independently is a real ongoing commitment. Teams that take that route often find the second or third connector is when the maintenance overhead catches up.

Apideck's unified accounting API is designed for that scenario. You write once against a normalized data model for invoices, payments, customers, and ledger entries, and the platform handles the per-connector translation across 12+ accounting platforms. Fortnox is on the roadmap. You can request Fortnox support or explore the existing connector coverage with a 30-day free trial.

Ready to get started?

Scale your integration strategy and deliver the integrations your customers need in record time.

Ready to get started?
Talk to an expert

Frequently asked questions

Trusted by fast-moving product & engineering teams

JobNimbus
Blue Zinc
Exact
Drata
Octa
Apideck Blog

Insights, guides, and updates from Apideck

Discover company news, API insights, and expert blog posts. Explore practical integration guides and tech articles to make the most of Apideck's platform.