Authentication

Personal Access Tokens

All new integrations authenticate with a company-specific Personal Access Token, sent in the standard header:

Authorization: Bearer edk_live_<public_id>.<secret>

Properties of a key:

  • Bound to one company. It cannot reach any other company, whatever the owner’s own access is.

  • Stored as an HMAC digest. The server never holds the secret, so it cannot be recovered or re-displayed.

  • Expires after 30, 90, 180 or 365 days, chosen at creation.

  • Limited by explicit scopes. A request outside the key’s scopes is rejected.

  • Re-checked on every request against the owner’s current company access. If the owner loses access to the company, the key stops working.

Keys are created, rotated and revoked in the application under API-nycklar. See Getting started.

Scopes

Each route requires exactly one scope. Request the narrowest set that covers your integration.

Scope

Access

Covers

company.profile:read

Read

Company record and version information.

company.bank_details:read

Read, sensitive

Bank fields on the company record. Required in addition to company.profile:read.

invoices:read

Read

Customer invoices.

invoices:write

Write

Create, update and delete customer invoices.

suppliers:read

Read, sensitive

Supplier register, including contact and bank details.

siri:read

Read

SIRI document drafts.

siri:write

Write

Create, update and delete SIRI documents.

accounting:read

Read, sensitive

Fiscal years, chart of accounts, bank and tax account items, and reading verifications.

verifications:write

Write

Create, update and delete verifications.

Company list behaviour

GET /user-companies behaves differently depending on how you authenticate.

With a Bearer key, which is bound to one company, it returns a one-element list containing that company’s full record. With legacy headers it returns every company the user can reach, as short {company_id, name} entries.

To get the same response either way, address the company directly:

GET /user-companies/{company_id}

Rate limits

Exceeding a limit returns 429 with a Retry-After header (seconds) and the body {"message": "rate_limited"}. Honour Retry-After rather than retrying immediately.

Bucket

Limit

Counted per

Requests with a Bearer key

600 per 5 minutes

Key

Write requests (POST, PUT, DELETE)

120 per hour

Key

Failed authentication attempts

100 per 5 minutes

Client IP

Requests with legacy credentials

600 per 5 minutes

Token

The write limit is deliberately tight. Batch your work rather than issuing one request per row.

Key rotation

Rotation issues a new secret for an existing key, keeping its name and scopes. You can choose an overlap of 0, 1, 6 or 24 hours during which the old secret still authenticates, so you can deploy the new one without downtime.

Pick zero overlap if you believe the key has leaked.

Revocation takes effect immediately, and a revoked key is never reinstated.

Legacy credentials

Older clients authenticate with a pair of headers:

Access-Token: <token>
Client-Secret: <secret>

These continue to work, but:

  • Issuance is closed. POST /edapi/v1/tokens answers 410 Gone. No new pairs are handed out.

  • Do not mix. Sending Bearer and legacy headers in the same request is rejected.

  • Migrate. Legacy pairs have no scopes and no expiry, which is exactly why they are being retired. Move to a Bearer key with explicit scopes.

Note

Legacy credentials are not bound to a device at request time, and they do not expire on their own. Treat any legacy pair still in production as a standing risk and plan its replacement.