NuminaDevelopers
Dashboard
Build an integration

Build an integration

Connect your product to your customers' books in Numina, from a first test connection to pilot customers and a reviewed partner app.

An integration lets your customers connect their Numina company to your product, so you can read their books or create invoices, contacts and journal entries on their behalf. Each customer approves the connection once with OAuth, and from then on you hold a token for that one company.

For a script against your own company, an API key is enough. This guide is for a product that many Numina companies connect to.

How it fits together

  1. You register an app in Numina and get a client ID and secret.
  2. Your customer clicks a “Connect Numina” button in your product, signs in to Numina, picks a company and approves your scopes.
  3. You store the tokens for that customer and call the API as the user who approved.
  4. The customer can disconnect at any time under Settings → API & apps in Numina.

Build it

  1. Get a company to test against

    Use your own Numina company, or write to support@numina.app for a sandbox company. A sandbox behaves like a real company, except that it cannot send invoices by email (Send invoice by email returns 403 forbidden) and it expires on a set date.

  2. Create a developer app

    A company admin opens Settings → API & apps in that company and creates a new OAuth application. Give it the name your customers will see on the consent screen, your redirect URIs (https, or http://localhost while you develop) and the scopes you need. The client secret is shown once.

    You can connect the app to the company that owns it and to a sandbox you are a member of, with any scope, booking included. That is all you need to build and test the whole flow.

  3. Connect a company

    Send the user through the authorization code flow with PKCE described in Authentication, then check the connection:

    cURL
    curl https://api.numina.app/v1/me \
      -H "Authorization: Bearer $ACCESS_TOKEN"
    Response
    {
      "user": { "id": "7d0c…", "email": "anna@example.dk", "first_name": "Anna", "last_name": "Holm" },
      "company": { "id": "1b9a…", "name": "Fjordlys ApS", "slug": "fjordlys" },
      "scopes": ["setup:read", "contacts:read", "contacts:write", "invoices:read", "invoices:write"],
      "auth_type": "oauth"
    }

    Store the access token, the refresh token and company.id with your customer’s account. Show the company name in your product, so the user can see which company is connected.

  4. Keep the connection alive

    Access tokens last an hour and refresh tokens 30 days. Refresh tokens rotate, so save the new one every time you refresh, and make sure two of your workers never refresh the same token at once. A second refresh within 30 seconds gets the same new tokens, but after that a retired refresh token ends the connection.

    A user can disconnect your app, and a refresh token can expire unused. Either way an API call returns 401 invalid_token and the refresh is refused. Mark the connection as broken in your product and ask the user to connect again; do not retry.

  5. Read the company's setup before you write

    Account numbers, VAT codes and bank accounts differ from one company to the next. Read them from List accounts, List VAT codes and List bank accounts when a company connects, and let the user choose where your data goes rather than hard-coding numbers. The accounting model explains signs, VAT and drafts.

  6. Write safely

    • Send an Idempotency-Key on every create that supports one, and store Numina’s id next to your own record.
    • Search before you create a contact: Create contact does not detect duplicates.
    • Branch on the error code, not the status or the description. See Errors.
    • There are no webhooks yet. To follow changes in Numina, poll the list endpoints and page through them.

Go live

Make the app available to other companies in its settings, and your customers can connect it. An admin of the customer’s company approves, the consent screen says that Numina has not reviewed the app, and it works for up to 10 companies. It can hold booking scopes too: the consent screen then tells the admin that the app can book and that booking cannot be undone.

When more than 10 companies should connect it, write to support@numina.app with:

  • the app’s name, website and a short description of what it does in Numina
  • your production redirect URIs
  • the scopes it needs, and why it needs each write or booking scope

We review the app and mark it as reviewed. It keeps its client ID, secret and the connections it has, so you change nothing. After that any number of Numina companies can connect it, any of their users can approve, and the consent screen shows you as a verified Numina partner instead of the note about review. Adding a redirect URI later sends the app back to unreviewed until we have looked at it again.

Good to know

  • Limits are counted per connection, so each company connected to your app has its own 600 requests per minute, and your host’s shared IP addresses do not matter. See Rate limits.
  • One connection is one company: a customer with several companies connects each one, and you hold a token per company.
  • If the user who connected your app leaves the company, its requests return 404 not_found, and another member has to connect again.
  • Fuld Service customers cannot book through the API: booking calls return 403 booking_not_available, while drafts still work.