VOOZH about

URL: https://www.apideck.com/blog/visma-eaccounting-api-integration.md


--- title: "How to Integrate with the Visma eAccounting API" description: "A developer guide to Visma eAccounting API integration. Covers OAuth 2.0 scopes, token management, pagination, fiscal year handling, and country-specific VAT with JavaScript code examples." author: "Saurabh Rai" published: "2026-05-09T09:00:00.000Z" updated: "2026-05-09T20:20:16.459Z" url: "https://www.apideck.com/blog/visma-eaccounting-api-integration" category: "Accounting" tags: ["Accounting", "Guides & Tutorials"] --- # How to Integrate with the Visma eAccounting API Visma eAccounting is one of the more widely deployed cloud accounting platforms in Northern Europe, particularly in Norway and the Netherlands. If your product targets SMEs in those markets, sooner or later a customer will ask you to sync invoices, pull expense data, or push payments into their eAccounting company. The API handles all of that, but the path to a working integration has a few non-obvious steps that are worth mapping out clearly. One naming note before anything else: Visma has rebranded eAccounting to "Spiris" in some markets, and the official developer portal now refers to it as the Spiris API. The underlying API, authentication server, and endpoints are the same product. If you see the two names used interchangeably in documentation and forum posts, that's why. If your customers are specifically on Spiris in Sweden, see our [Spiris / Visma eAccounting API integration guide](https://www.apideck.com/blog/spiris-visma-eaccounting-api-integration) for the Sweden-specific differences around BAS accounts, Moms VAT, and ROT/RUT invoice handling. ## Getting Registered Access to the Visma eAccounting API requires joining the [Spiris Partner Programme](https://selfservice.developer.vismaonline.com/). Registration is self-service for sandbox access: fill in the form at the Visma Developer Self-Service portal, and you'll receive a `client_id`, a `client_secret`, and credentials for a sandbox eAccounting company by email. Use a business email. That address becomes the contact point for your developer account and the address Visma API support will reach out to. When you register, you also set your `redirect_uri`. This value is fixed unless you contact Visma support to update it, so plan ahead rather than hardcoding `localhost` and hoping to change it later. Production access requires a separate request to API support after sandbox testing is complete. Visma will never ask you for your `client_secret`, not during registration and not in any support context. If that ever comes up, treat it as a red flag. ## Authentication: OAuth 2.0 with a Resource-Level Scope Model The API uses the OAuth 2.0 authorization code flow, with the identity server hosted at `https://identity.vismaonline.com`. The two required scopes for any eAccounting integration are `ea:api` (grants access to the Bookkeeping & Invoicing/eAccounting API) and `offline_access` (required to receive a refresh token). Beyond those, each resource area has its own scope. | Scope | Access | |---|---| | `ea:sales` | Full access to sales resources (invoices, customers) | | `ea:sales_readonly` | Read-only access to sales resources | | `ea:purchase` | Full access to purchase resources (bills, suppliers) | | `ea:purchase_readonly` | Read-only access to purchase resources | | `ea:accounting` | Full access to accounting resources (journal entries, accounts) | | `ea:accounting_readonly` | Read-only access to accounting resources | The scope model creates friction that isn't obvious until you've already shipped. You define the maximum set of scopes your app can request when registering in the developer portal. During the OAuth flow, you must explicitly include every scope you need in the authorization request. Your customer sees and approves those scopes as part of connecting their account. If you omit a scope in the authorization request, even if your app registration permits it, the API returns an authorization error for that resource area. Correcting it requires your customer to go through the OAuth flow again. Get your scope list right the first time, and default to the `_readonly` variants unless your integration genuinely needs write access. A sample authorization request looks like this: ``` GET https://identity.vismaonline.com/connect/authorize ?client_id=