Skip to content

API reference

Supplier invoices

Supplier invoices: drafts, file, registering, approval with segregation of duties and payment.

List supplier invoices

GET /supplier-invoices listSupplierInvoices

Supplier invoices and credit notes with rows, payments and payment status. Needs supplier-invoices:create, supplier-invoices:approve or payments:create.

Scope

supplier-invoices:create or supplier-invoices:approve or payments:create

Parameters

  • limit integer query

    Page size, 1–200.

    Default: 50 · minimum 1 · maximum 200

  • cursor string query

    Opaque cursor from meta.next_cursor of the previous page.

    at most 2000 characters

  • updated_since string (date-time) query

    Only records whose updated_at is strictly later than this ISO 8601 time. Lists are ordered by updated_at, then id, so a sync stores the updated_at of the last record it has seen and passes it next time, walking all pages with `cursor`.

    Example: 2026-03-01T00:00:00+01:00

  • status string query

    DRAFT, REGISTERED (waiting for approval), APPROVED (to pay) or PAID.

    Values: DRAFT, REGISTERED, APPROVED, PAID

  • supplier_id integer query

    Only this supplier.

  • date_from string (date) query

    From this date (YYYY-MM-DD), inclusive.

  • date_to string (date) query

    Up to this date (YYYY-MM-DD), inclusive.

Responses

200 OK – A page of supplier invoices.

application/jsonobject

  • data array<SupplierInvoice> required
    29 fields · each item
    • id string (uuid)
    • number integer nullable
    • kind string

      Values: INVOICE, CREDIT

    • status string

      Payment status: REGISTERED waits for approval, APPROVED waits for payment. Open enum.

      Values: DRAFT, REGISTERED, APPROVED, PAID

    • supplier_id integer
    • supplier_name string
    • invoice_number string nullable
    • ocr string nullable
    • invoice_date string (date)
    • due_date string (date)
    • description string nullable
    • vat_treatment string
    • currency string
    • total_ore integer
    • vat_ore integer
    • paid_ore integer
    • remaining_ore integer
    • invoice_currency string
    • foreign_total_minor integer nullable
    • foreign_paid_minor integer nullable
    • exchange_rate_micros integer nullable

      Kronor per unit in millionths (9.8264 is 9826400).

    • file object | any
      2 fields
      • One of object
        • name string
        • mime string
        • size integer
        • sha256 string
      • One of any
    • voucher_id string (uuid) nullable
    • registered_at string (date-time) nullable
    • approved_at string (date-time) nullable
    • rows array<SupplierInvoiceRow>
      6 fields · each item
      • account integer nullable
      • description string nullable
      • amount_ore integer
      • vat_rate integer nullable
      • cost_center string nullable
      • project string nullable
    • payments array<SupplierPayment>
      11 fields · each item
      • id integer
      • supplier_invoice_id string (uuid)
      • paid_on string (date)
      • amount_ore integer

        Kronor the bank took, öre (negative for money back on a credit note).

      • currency string
      • foreign_amount_minor integer nullable

        Hundredths of the invoice's currency, for an invoice in another currency.

      • exchange_difference_ore integer
      • account integer nullable
      • voucher_id string (uuid)
      • created_at string (date-time)
      • updated_at string (date-time)
    • created_at string (date-time)
    • updated_at string (date-time)
  • meta ListMeta required
    3 fields
    • limit integer required
    • next_cursor string required nullable

      Opaque; pass as `cursor` for the next page. null on the last page.

    • has_more boolean required

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

401 Unauthorized – Missing, invalid, expired or revoked token or API key. Also returned when the person behind the credential has left the company (an OAuth grant is then revoked for good).

All error codes · application/problem+json

  • unauthenticatedNo valid credential

    Nyckeln eller token saknas, är ogiltig, har gått ut eller har återkallats.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, WWW-Authenticate

403 Forbidden – The credential may not do this: a scope is missing, the granting person no longer has the permission, the address is not allowed, the company is archived (read-only) or no longer allows the app.

All error codes · application/problem+json

  • insufficient_scopeScope missing or permission lost

    Nyckeln eller appen saknar behörigheten vouchers:post, eller så har personen som gav den inte längre den behörigheten i företaget.

  • forbiddenThe action refuses (for example changing a posted voucher)

    Behörighet saknas för det här.

  • ip_not_allowedOutside the IP allowlist

    Företaget tillåter inte anrop från den här adressen.

  • company_archivedWrite to an archived company

    Företaget är arkiverat och kan bara läsas.

  • app_not_allowedThe company no longer allows the app

    Företaget tillåter inte längre den här appen.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

404 Not Found – Not found. Also the answer for another company's ids (existence is never revealed) and for every call while NONOX Connect is turned off for the company.

All error codes · application/problem+json

  • not_foundUnknown id, other company's id, or module off

    Det du söker finns inte.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

429 Too Many Requests – Rate limit exceeded (per credential, per company or per address). Wait Retry-After seconds.

All error codes · application/problem+json

  • rate_limitedOver the limit

    För många anrop. Försök igen om 23 sekunder.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, Retry-After

Code samples

curl
curl "https://nonox.app/api/v1/supplier-invoices?limit=50&updated_since=2026-03-01T00%3A00%3A00%2B01%3A00" \
  -H "Authorization: Bearer $NONOX_TOKEN" \
  -H "Accept: application/json"
PHP (Laravel)
use Illuminate\Support\Facades\Http;

$response = Http::withToken(config('services.nonox.token'))
    ->acceptJson()
    ->get('https://nonox.app/api/v1/supplier-invoices', [
        'limit' => 50,
        'updated_since' => '2026-03-01T00:00:00+01:00',
    ]);

$response->throw();

$data = $response->json('data');
PHP (Guzzle)
$client = new \GuzzleHttp\Client(['timeout' => 30]);

$response = $client->request('GET', 'https://nonox.app/api/v1/supplier-invoices', [
    'headers' => [
        'Authorization' => 'Bearer '.getenv('NONOX_TOKEN'),
        'Accept' => 'application/json',
    ],
    'query' => [
        'limit' => 50,
        'updated_since' => '2026-03-01T00:00:00+01:00',
    ],
]);

$data = json_decode((string) $response->getBody(), true, flags: JSON_THROW_ON_ERROR)['data'];
JavaScript
const url = new URL('https://nonox.app/api/v1/supplier-invoices');
url.searchParams.set('limit', '50');
url.searchParams.set('updated_since', '2026-03-01T00:00:00+01:00');

const response = await fetch(url, {
  headers: {
    Authorization: `Bearer ${process.env.NONOX_TOKEN}`,
    Accept: 'application/json',
  },
});

if (!response.ok) {
  const problem = await response.json();
  throw new Error(`${problem.code}: ${problem.detail}`);
}

const { data } = await response.json();
Python
import os
import requests

response = requests.get(
    "https://nonox.app/api/v1/supplier-invoices",
    headers={
        "Authorization": f"Bearer {os.environ['NONOX_TOKEN']}",
        "Accept": "application/json",
    },
    params={
        "limit": 50,
        "updated_since": "2026-03-01T00:00:00+01:00",
    },
    timeout=30,
)
response.raise_for_status()

data = response.json()["data"]

Responses · 200

application/json
{
  "data": [
    {
      "id": "8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e",
      "number": 77,
      "kind": "INVOICE",
      "status": "APPROVED",
      "supplier_id": 9,
      "supplier_name": "Leverantören Exempel AB",
      "invoice_number": "F-2026-118",
      "ocr": "4005",
      "invoice_date": "2026-03-03",
      "due_date": "2026-04-02",
      "description": "Kontorsmaterial",
      "vat_treatment": "DOMESTIC",
      "currency": "SEK",
      "total_ore": 125000,
      "vat_ore": 25000,
      "paid_ore": 0,
      "remaining_ore": 125000,
      "invoice_currency": "SEK",
      "foreign_total_minor": null,
      "foreign_paid_minor": null,
      "exchange_rate_micros": null,
      "file": {
        "name": "faktura.pdf",
        "mime": "application/pdf",
        "size": 48211,
        "sha256": "3c1f0e9d8b7a6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d"
      },
      "voucher_id": "6e5d4c3b-2a19-4f8e-8d7c-6b5a49382716",
      "registered_at": "2026-03-03T14:00:00+01:00",
      "approved_at": "2026-03-04T09:30:00+01:00",
      "rows": [
        {
          "account": 5410,
          "description": "Förbrukningsinventarier",
          "amount_ore": 100000,
          "vat_rate": null,
          "cost_center": "20",
          "project": null
        }
      ],
      "payments": [],
      "created_at": "2026-03-03T13:50:00+01:00",
      "updated_at": "2026-03-04T09:30:00+01:00"
    }
  ],
  "meta": {
    "limit": 50,
    "next_cursor": null,
    "has_more": false
  }
}

Error

4011 example
No valid credential
{
  "type": "urn:nonox:problem:unauthenticated",
  "title": "Unauthorized",
  "status": 401,
  "code": "unauthenticated",
  "detail": "Nyckeln eller token saknas, är ogiltig, har gått ut eller har återkallats.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4035 examples
Scope missing or permission lost
{
  "type": "urn:nonox:problem:insufficient_scope",
  "title": "Forbidden",
  "status": 403,
  "code": "insufficient_scope",
  "detail": "Nyckeln eller appen saknar behörigheten vouchers:post, eller så har personen som gav den inte längre den behörigheten i företaget.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17",
  "required_scopes": [
    "vouchers:post"
  ]
}
The action refuses (for example changing a posted voucher)
{
  "type": "urn:nonox:problem:forbidden",
  "title": "Forbidden",
  "status": 403,
  "code": "forbidden",
  "detail": "Behörighet saknas för det här.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
Outside the IP allowlist
{
  "type": "urn:nonox:problem:ip_not_allowed",
  "title": "Forbidden",
  "status": 403,
  "code": "ip_not_allowed",
  "detail": "Företaget tillåter inte anrop från den här adressen.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
Write to an archived company
{
  "type": "urn:nonox:problem:company_archived",
  "title": "Forbidden",
  "status": 403,
  "code": "company_archived",
  "detail": "Företaget är arkiverat och kan bara läsas.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
The company no longer allows the app
{
  "type": "urn:nonox:problem:app_not_allowed",
  "title": "Forbidden",
  "status": 403,
  "code": "app_not_allowed",
  "detail": "Företaget tillåter inte längre den här appen.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4041 example
Unknown id, other company's id, or module off
{
  "type": "urn:nonox:problem:not_found",
  "title": "Not Found",
  "status": 404,
  "code": "not_found",
  "detail": "Det du söker finns inte.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4291 example
Over the limit
{
  "type": "urn:nonox:problem:rate_limited",
  "title": "Too Many Requests",
  "status": 429,
  "code": "rate_limited",
  "detail": "För många anrop. Försök igen om 23 sekunder.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}

Create a draft supplier invoice

POST /supplier-invoices createSupplierInvoice

Saves a draft as printed on the invoice: total, VAT and cost rows, which must add up. Add the invoice file with /file. Nothing is booked until it is registered. Same Action as the app (SaveSupplierInvoice).

Scope

supplier-invoices:create

Parameters

  • Idempotency-Key string header required

    Required on every POST. A unique value per intended operation (a UUID v4 is ideal), 1–255 visible ASCII characters. Kept 24 hours per credential: the same key with the same request returns the stored response (Idempotent-Replayed: true); with a different request 422 idempotency_key_reused; while the first is still running 409. Server errors (5xx) are not stored, so retry with the same key.

    at least 1 characters · at most 255 characters · Example: 3f6c1b7e-2d4a-4e8b-9c0d-5a1b2c3d4e5f

Request body

application/jsonSupplierInvoiceInput required

The rows and the VAT must add up to total_ore.

  • supplier_id integer required
  • invoice_number string nullable

    at most 50 characters

  • ocr string nullable

    pattern ^\d{2,25}$

  • invoice_date string (date) required
  • due_date string (date) nullable
  • description string nullable

    at most 255 characters

  • credit boolean

    A credit note; send positive amounts.

  • vat_treatment string nullable

    Values: DOMESTIC, EU_GOODS, EU_SERVICES, NON_EU_SERVICES, SE_REVERSE_GOODS, SE_REVERSE_SERVICES

  • currency string nullable

    pattern ^[A-Za-z]{3}$

  • total_ore integer required

    To pay, in kronor (öre).

  • vat_ore integer
  • foreign_total_minor integer nullable
  • exchange_rate_micros integer nullable
  • rows array<object> required
    6 fields · each item
    • account integer nullable

      Default: the supplier's default account.

    • amount_ore integer required
    • description string nullable
    • vat_rate integer nullable
    • cost_center string nullable
    • project string nullable

Responses

201 Created – The draft.

application/jsonobject

  • data SupplierInvoice required
    29 fields
    • id string (uuid)
    • number integer nullable
    • kind string

      Values: INVOICE, CREDIT

    • status string

      Payment status: REGISTERED waits for approval, APPROVED waits for payment. Open enum.

      Values: DRAFT, REGISTERED, APPROVED, PAID

    • supplier_id integer
    • supplier_name string
    • invoice_number string nullable
    • ocr string nullable
    • invoice_date string (date)
    • due_date string (date)
    • description string nullable
    • vat_treatment string
    • currency string
    • total_ore integer
    • vat_ore integer
    • paid_ore integer
    • remaining_ore integer
    • invoice_currency string
    • foreign_total_minor integer nullable
    • foreign_paid_minor integer nullable
    • exchange_rate_micros integer nullable

      Kronor per unit in millionths (9.8264 is 9826400).

    • file object | any
      2 fields
      • One of object
        • name string
        • mime string
        • size integer
        • sha256 string
      • One of any
    • voucher_id string (uuid) nullable
    • registered_at string (date-time) nullable
    • approved_at string (date-time) nullable
    • rows array<SupplierInvoiceRow>
      6 fields · each item
      • account integer nullable
      • description string nullable
      • amount_ore integer
      • vat_rate integer nullable
      • cost_center string nullable
      • project string nullable
    • payments array<SupplierPayment>
      11 fields · each item
      • id integer
      • supplier_invoice_id string (uuid)
      • paid_on string (date)
      • amount_ore integer

        Kronor the bank took, öre (negative for money back on a credit note).

      • currency string
      • foreign_amount_minor integer nullable

        Hundredths of the invoice's currency, for an invoice in another currency.

      • exchange_difference_ore integer
      • account integer nullable
      • voucher_id string (uuid)
      • created_at string (date-time)
      • updated_at string (date-time)
    • created_at string (date-time)
    • updated_at string (date-time)

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, Location, Idempotent-Replayed

400 Bad Request – Bad request: missing or invalid Idempotency-Key, or a body that is not a JSON object.

All error codes · application/problem+json

  • idempotency_key_requiredNo Idempotency-Key on a POST

    Skicka en unik Idempotency-Key (till exempel ett UUID) med varje POST, så att ett nytt försök aldrig bokför två gånger.

  • idempotency_key_invalidIdempotency-Key too long or not visible ASCII

    Idempotency-Key ska vara 1–255 synliga ASCII-tecken.

  • invalid_jsonMalformed JSON

    Kroppen är inte giltig JSON (ett objekt).

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

401 Unauthorized – Missing, invalid, expired or revoked token or API key. Also returned when the person behind the credential has left the company (an OAuth grant is then revoked for good).

All error codes · application/problem+json

  • unauthenticatedNo valid credential

    Nyckeln eller token saknas, är ogiltig, har gått ut eller har återkallats.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, WWW-Authenticate

403 Forbidden – The credential may not do this: a scope is missing, the granting person no longer has the permission, the address is not allowed, the company is archived (read-only) or no longer allows the app.

All error codes · application/problem+json

  • insufficient_scopeScope missing or permission lost

    Nyckeln eller appen saknar behörigheten vouchers:post, eller så har personen som gav den inte längre den behörigheten i företaget.

  • forbiddenThe action refuses (for example changing a posted voucher)

    Behörighet saknas för det här.

  • ip_not_allowedOutside the IP allowlist

    Företaget tillåter inte anrop från den här adressen.

  • company_archivedWrite to an archived company

    Företaget är arkiverat och kan bara läsas.

  • app_not_allowedThe company no longer allows the app

    Företaget tillåter inte längre den här appen.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

404 Not Found – Not found. Also the answer for another company's ids (existence is never revealed) and for every call while NONOX Connect is turned off for the company.

All error codes · application/problem+json

  • not_foundUnknown id, other company's id, or module off

    Det du söker finns inte.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

409 Conflict – A request with the same Idempotency-Key is still running.

All error codes · application/problem+json

  • idempotency_in_progressSame key, first request not finished

    En begäran med samma Idempotency-Key pågår. Vänta och försök igen.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, Retry-After

415 Unsupported Media Type – The body is not application/json (or multipart/form-data where a file is accepted).

All error codes · application/problem+json

  • unsupported_media_typeWrong Content-Type

    Skicka JSON (application/json).

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

422 Unprocessable Content – Validation failed, or the Idempotency-Key was already used for a different request.

All error codes · application/problem+json

  • validation_failedField errors

    Uppgifterna kunde inte godtas. Se errors.

  • idempotency_key_reusedSame key, different body

    Idempotency-Key har redan använts för en annan begäran. Använd en ny nyckel för en ny begäran.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

429 Too Many Requests – Rate limit exceeded (per credential, per company or per address). Wait Retry-After seconds.

All error codes · application/problem+json

  • rate_limitedOver the limit

    För många anrop. Försök igen om 23 sekunder.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, Retry-After

Code samples

curl
curl -X POST "https://nonox.app/api/v1/supplier-invoices" \
  -H "Authorization: Bearer $NONOX_TOKEN" \
  -H "Accept: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "supplier_id": 9,
  "invoice_number": "F-2026-118",
  "ocr": "4005",
  "invoice_date": "2026-03-03",
  "total_ore": 125000,
  "vat_ore": 25000,
  "rows": [
    {
      "account": 5410,
      "amount_ore": 100000,
      "description": "Förbrukningsinventarier",
      "cost_center": "20"
    }
  ]
}'
PHP (Laravel)
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;

$response = Http::withToken(config('services.nonox.token'))
    ->acceptJson()
    ->withHeaders(['Idempotency-Key' => (string) Str::uuid()])
    ->post('https://nonox.app/api/v1/supplier-invoices', [
        'supplier_id' => 9,
        'invoice_number' => 'F-2026-118',
        'ocr' => '4005',
        'invoice_date' => '2026-03-03',
        'total_ore' => 125000,
        'vat_ore' => 25000,
        'rows' => [
            [
                'account' => 5410,
                'amount_ore' => 100000,
                'description' => 'Förbrukningsinventarier',
                'cost_center' => '20',
            ],
        ],
    ]);

$response->throw();

$data = $response->json('data');
PHP (Guzzle)
$client = new \GuzzleHttp\Client(['timeout' => 30]);

$response = $client->request('POST', 'https://nonox.app/api/v1/supplier-invoices', [
    'headers' => [
        'Authorization' => 'Bearer '.getenv('NONOX_TOKEN'),
        'Accept' => 'application/json',
        'Idempotency-Key' => bin2hex(random_bytes(16)),
    ],
    'json' => [
        'supplier_id' => 9,
        'invoice_number' => 'F-2026-118',
        'ocr' => '4005',
        'invoice_date' => '2026-03-03',
        'total_ore' => 125000,
        'vat_ore' => 25000,
        'rows' => [
            [
                'account' => 5410,
                'amount_ore' => 100000,
                'description' => 'Förbrukningsinventarier',
                'cost_center' => '20',
            ],
        ],
    ],
]);

$data = json_decode((string) $response->getBody(), true, flags: JSON_THROW_ON_ERROR)['data'];
JavaScript
const response = await fetch('https://nonox.app/api/v1/supplier-invoices', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.NONOX_TOKEN}`,
    Accept: 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "supplier_id": 9,
    "invoice_number": "F-2026-118",
    "ocr": "4005",
    "invoice_date": "2026-03-03",
    "total_ore": 125000,
    "vat_ore": 25000,
    "rows": [
      {
        "account": 5410,
        "amount_ore": 100000,
        "description": "Förbrukningsinventarier",
        "cost_center": "20"
      }
    ]
  }),
});

if (!response.ok) {
  const problem = await response.json();
  throw new Error(`${problem.code}: ${problem.detail}`);
}

const { data } = await response.json();
Python
import os
import uuid
import requests

response = requests.post(
    "https://nonox.app/api/v1/supplier-invoices",
    headers={
        "Authorization": f"Bearer {os.environ['NONOX_TOKEN']}",
        "Accept": "application/json",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "supplier_id": 9,
        "invoice_number": "F-2026-118",
        "ocr": "4005",
        "invoice_date": "2026-03-03",
        "total_ore": 125000,
        "vat_ore": 25000,
        "rows": [
            {
                "account": 5410,
                "amount_ore": 100000,
                "description": "Förbrukningsinventarier",
                "cost_center": "20",
            },
        ],
    },
    timeout=30,
)
response.raise_for_status()

data = response.json()["data"]

Responses · 201

application/json
{
  "data": {
    "id": "8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e",
    "number": 77,
    "kind": "INVOICE",
    "status": "APPROVED",
    "supplier_id": 9,
    "supplier_name": "Leverantören Exempel AB",
    "invoice_number": "F-2026-118",
    "ocr": "4005",
    "invoice_date": "2026-03-03",
    "due_date": "2026-04-02",
    "description": "Kontorsmaterial",
    "vat_treatment": "DOMESTIC",
    "currency": "SEK",
    "total_ore": 125000,
    "vat_ore": 25000,
    "paid_ore": 0,
    "remaining_ore": 125000,
    "invoice_currency": "SEK",
    "foreign_total_minor": null,
    "foreign_paid_minor": null,
    "exchange_rate_micros": null,
    "file": {
      "name": "faktura.pdf",
      "mime": "application/pdf",
      "size": 48211,
      "sha256": "3c1f0e9d8b7a6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d"
    },
    "voucher_id": "6e5d4c3b-2a19-4f8e-8d7c-6b5a49382716",
    "registered_at": "2026-03-03T14:00:00+01:00",
    "approved_at": "2026-03-04T09:30:00+01:00",
    "rows": [
      {
        "account": 5410,
        "description": "Förbrukningsinventarier",
        "amount_ore": 100000,
        "vat_rate": null,
        "cost_center": "20",
        "project": null
      }
    ],
    "payments": [],
    "created_at": "2026-03-03T13:50:00+01:00",
    "updated_at": "2026-03-04T09:30:00+01:00"
  }
}

Error

4003 examples
No Idempotency-Key on a POST
{
  "type": "urn:nonox:problem:idempotency_key_required",
  "title": "Bad Request",
  "status": 400,
  "code": "idempotency_key_required",
  "detail": "Skicka en unik Idempotency-Key (till exempel ett UUID) med varje POST, så att ett nytt försök aldrig bokför två gånger.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
Idempotency-Key too long or not visible ASCII
{
  "type": "urn:nonox:problem:idempotency_key_invalid",
  "title": "Bad Request",
  "status": 400,
  "code": "idempotency_key_invalid",
  "detail": "Idempotency-Key ska vara 1–255 synliga ASCII-tecken.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
Malformed JSON
{
  "type": "urn:nonox:problem:invalid_json",
  "title": "Bad Request",
  "status": 400,
  "code": "invalid_json",
  "detail": "Kroppen är inte giltig JSON (ett objekt).",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4011 example
No valid credential
{
  "type": "urn:nonox:problem:unauthenticated",
  "title": "Unauthorized",
  "status": 401,
  "code": "unauthenticated",
  "detail": "Nyckeln eller token saknas, är ogiltig, har gått ut eller har återkallats.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4035 examples
Scope missing or permission lost
{
  "type": "urn:nonox:problem:insufficient_scope",
  "title": "Forbidden",
  "status": 403,
  "code": "insufficient_scope",
  "detail": "Nyckeln eller appen saknar behörigheten vouchers:post, eller så har personen som gav den inte längre den behörigheten i företaget.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17",
  "required_scopes": [
    "vouchers:post"
  ]
}
The action refuses (for example changing a posted voucher)
{
  "type": "urn:nonox:problem:forbidden",
  "title": "Forbidden",
  "status": 403,
  "code": "forbidden",
  "detail": "Behörighet saknas för det här.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
Outside the IP allowlist
{
  "type": "urn:nonox:problem:ip_not_allowed",
  "title": "Forbidden",
  "status": 403,
  "code": "ip_not_allowed",
  "detail": "Företaget tillåter inte anrop från den här adressen.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
Write to an archived company
{
  "type": "urn:nonox:problem:company_archived",
  "title": "Forbidden",
  "status": 403,
  "code": "company_archived",
  "detail": "Företaget är arkiverat och kan bara läsas.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
The company no longer allows the app
{
  "type": "urn:nonox:problem:app_not_allowed",
  "title": "Forbidden",
  "status": 403,
  "code": "app_not_allowed",
  "detail": "Företaget tillåter inte längre den här appen.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4041 example
Unknown id, other company's id, or module off
{
  "type": "urn:nonox:problem:not_found",
  "title": "Not Found",
  "status": 404,
  "code": "not_found",
  "detail": "Det du söker finns inte.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4091 example
Same key, first request not finished
{
  "type": "urn:nonox:problem:idempotency_in_progress",
  "title": "Conflict",
  "status": 409,
  "code": "idempotency_in_progress",
  "detail": "En begäran med samma Idempotency-Key pågår. Vänta och försök igen.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4151 example
Wrong Content-Type
{
  "type": "urn:nonox:problem:unsupported_media_type",
  "title": "Unsupported Media Type",
  "status": 415,
  "code": "unsupported_media_type",
  "detail": "Skicka JSON (application/json).",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4222 examples
Field errors
{
  "type": "urn:nonox:problem:validation_failed",
  "title": "Unprocessable Content",
  "status": 422,
  "code": "validation_failed",
  "detail": "Uppgifterna kunde inte godtas. Se errors.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17",
  "errors": {
    "rows": [
      "Verifikationen balanserar inte: debet och kredit skiljer sig åt."
    ],
    "voucher_date": [
      "Det finns inget räkenskapsår som omfattar 2031-01-01."
    ]
  }
}
Same key, different body
{
  "type": "urn:nonox:problem:idempotency_key_reused",
  "title": "Unprocessable Content",
  "status": 422,
  "code": "idempotency_key_reused",
  "detail": "Idempotency-Key har redan använts för en annan begäran. Använd en ny nyckel för en ny begäran.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4291 example
Over the limit
{
  "type": "urn:nonox:problem:rate_limited",
  "title": "Too Many Requests",
  "status": 429,
  "code": "rate_limited",
  "detail": "För många anrop. Försök igen om 23 sekunder.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}

Get a supplier invoice

GET /supplier-invoices/{supplierInvoice} getSupplierInvoice

One supplier invoice with rows, payments and payment status.

Scope

supplier-invoices:create or supplier-invoices:approve or payments:create

Parameters

  • supplierInvoice string (uuid) path required

    Supplier invoice id (UUID).

    Example: 8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e

Responses

200 OK – The supplier invoice.

application/jsonobject

  • data SupplierInvoice required
    29 fields
    • id string (uuid)
    • number integer nullable
    • kind string

      Values: INVOICE, CREDIT

    • status string

      Payment status: REGISTERED waits for approval, APPROVED waits for payment. Open enum.

      Values: DRAFT, REGISTERED, APPROVED, PAID

    • supplier_id integer
    • supplier_name string
    • invoice_number string nullable
    • ocr string nullable
    • invoice_date string (date)
    • due_date string (date)
    • description string nullable
    • vat_treatment string
    • currency string
    • total_ore integer
    • vat_ore integer
    • paid_ore integer
    • remaining_ore integer
    • invoice_currency string
    • foreign_total_minor integer nullable
    • foreign_paid_minor integer nullable
    • exchange_rate_micros integer nullable

      Kronor per unit in millionths (9.8264 is 9826400).

    • file object | any
      2 fields
      • One of object
        • name string
        • mime string
        • size integer
        • sha256 string
      • One of any
    • voucher_id string (uuid) nullable
    • registered_at string (date-time) nullable
    • approved_at string (date-time) nullable
    • rows array<SupplierInvoiceRow>
      6 fields · each item
      • account integer nullable
      • description string nullable
      • amount_ore integer
      • vat_rate integer nullable
      • cost_center string nullable
      • project string nullable
    • payments array<SupplierPayment>
      11 fields · each item
      • id integer
      • supplier_invoice_id string (uuid)
      • paid_on string (date)
      • amount_ore integer

        Kronor the bank took, öre (negative for money back on a credit note).

      • currency string
      • foreign_amount_minor integer nullable

        Hundredths of the invoice's currency, for an invoice in another currency.

      • exchange_difference_ore integer
      • account integer nullable
      • voucher_id string (uuid)
      • created_at string (date-time)
      • updated_at string (date-time)
    • created_at string (date-time)
    • updated_at string (date-time)

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

401 Unauthorized – Missing, invalid, expired or revoked token or API key. Also returned when the person behind the credential has left the company (an OAuth grant is then revoked for good).

All error codes · application/problem+json

  • unauthenticatedNo valid credential

    Nyckeln eller token saknas, är ogiltig, har gått ut eller har återkallats.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, WWW-Authenticate

403 Forbidden – The credential may not do this: a scope is missing, the granting person no longer has the permission, the address is not allowed, the company is archived (read-only) or no longer allows the app.

All error codes · application/problem+json

  • insufficient_scopeScope missing or permission lost

    Nyckeln eller appen saknar behörigheten vouchers:post, eller så har personen som gav den inte längre den behörigheten i företaget.

  • forbiddenThe action refuses (for example changing a posted voucher)

    Behörighet saknas för det här.

  • ip_not_allowedOutside the IP allowlist

    Företaget tillåter inte anrop från den här adressen.

  • company_archivedWrite to an archived company

    Företaget är arkiverat och kan bara läsas.

  • app_not_allowedThe company no longer allows the app

    Företaget tillåter inte längre den här appen.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

404 Not Found – Not found. Also the answer for another company's ids (existence is never revealed) and for every call while NONOX Connect is turned off for the company.

All error codes · application/problem+json

  • not_foundUnknown id, other company's id, or module off

    Det du söker finns inte.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

429 Too Many Requests – Rate limit exceeded (per credential, per company or per address). Wait Retry-After seconds.

All error codes · application/problem+json

  • rate_limitedOver the limit

    För många anrop. Försök igen om 23 sekunder.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, Retry-After

Code samples

curl
curl "https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e" \
  -H "Authorization: Bearer $NONOX_TOKEN" \
  -H "Accept: application/json"
PHP (Laravel)
use Illuminate\Support\Facades\Http;

$response = Http::withToken(config('services.nonox.token'))
    ->acceptJson()
    ->get('https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e');

$response->throw();

$data = $response->json('data');
PHP (Guzzle)
$client = new \GuzzleHttp\Client(['timeout' => 30]);

$response = $client->request('GET', 'https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e', [
    'headers' => [
        'Authorization' => 'Bearer '.getenv('NONOX_TOKEN'),
        'Accept' => 'application/json',
    ],
]);

$data = json_decode((string) $response->getBody(), true, flags: JSON_THROW_ON_ERROR)['data'];
JavaScript
const response = await fetch('https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e', {
  headers: {
    Authorization: `Bearer ${process.env.NONOX_TOKEN}`,
    Accept: 'application/json',
  },
});

if (!response.ok) {
  const problem = await response.json();
  throw new Error(`${problem.code}: ${problem.detail}`);
}

const { data } = await response.json();
Python
import os
import requests

response = requests.get(
    "https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e",
    headers={
        "Authorization": f"Bearer {os.environ['NONOX_TOKEN']}",
        "Accept": "application/json",
    },
    timeout=30,
)
response.raise_for_status()

data = response.json()["data"]

Responses · 200

application/json
{
  "data": {
    "id": "8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e",
    "number": 77,
    "kind": "INVOICE",
    "status": "APPROVED",
    "supplier_id": 9,
    "supplier_name": "Leverantören Exempel AB",
    "invoice_number": "F-2026-118",
    "ocr": "4005",
    "invoice_date": "2026-03-03",
    "due_date": "2026-04-02",
    "description": "Kontorsmaterial",
    "vat_treatment": "DOMESTIC",
    "currency": "SEK",
    "total_ore": 125000,
    "vat_ore": 25000,
    "paid_ore": 0,
    "remaining_ore": 125000,
    "invoice_currency": "SEK",
    "foreign_total_minor": null,
    "foreign_paid_minor": null,
    "exchange_rate_micros": null,
    "file": {
      "name": "faktura.pdf",
      "mime": "application/pdf",
      "size": 48211,
      "sha256": "3c1f0e9d8b7a6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d"
    },
    "voucher_id": "6e5d4c3b-2a19-4f8e-8d7c-6b5a49382716",
    "registered_at": "2026-03-03T14:00:00+01:00",
    "approved_at": "2026-03-04T09:30:00+01:00",
    "rows": [
      {
        "account": 5410,
        "description": "Förbrukningsinventarier",
        "amount_ore": 100000,
        "vat_rate": null,
        "cost_center": "20",
        "project": null
      }
    ],
    "payments": [],
    "created_at": "2026-03-03T13:50:00+01:00",
    "updated_at": "2026-03-04T09:30:00+01:00"
  }
}

Error

4011 example
No valid credential
{
  "type": "urn:nonox:problem:unauthenticated",
  "title": "Unauthorized",
  "status": 401,
  "code": "unauthenticated",
  "detail": "Nyckeln eller token saknas, är ogiltig, har gått ut eller har återkallats.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4035 examples
Scope missing or permission lost
{
  "type": "urn:nonox:problem:insufficient_scope",
  "title": "Forbidden",
  "status": 403,
  "code": "insufficient_scope",
  "detail": "Nyckeln eller appen saknar behörigheten vouchers:post, eller så har personen som gav den inte längre den behörigheten i företaget.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17",
  "required_scopes": [
    "vouchers:post"
  ]
}
The action refuses (for example changing a posted voucher)
{
  "type": "urn:nonox:problem:forbidden",
  "title": "Forbidden",
  "status": 403,
  "code": "forbidden",
  "detail": "Behörighet saknas för det här.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
Outside the IP allowlist
{
  "type": "urn:nonox:problem:ip_not_allowed",
  "title": "Forbidden",
  "status": 403,
  "code": "ip_not_allowed",
  "detail": "Företaget tillåter inte anrop från den här adressen.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
Write to an archived company
{
  "type": "urn:nonox:problem:company_archived",
  "title": "Forbidden",
  "status": 403,
  "code": "company_archived",
  "detail": "Företaget är arkiverat och kan bara läsas.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
The company no longer allows the app
{
  "type": "urn:nonox:problem:app_not_allowed",
  "title": "Forbidden",
  "status": 403,
  "code": "app_not_allowed",
  "detail": "Företaget tillåter inte längre den här appen.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4041 example
Unknown id, other company's id, or module off
{
  "type": "urn:nonox:problem:not_found",
  "title": "Not Found",
  "status": 404,
  "code": "not_found",
  "detail": "Det du söker finns inte.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4291 example
Over the limit
{
  "type": "urn:nonox:problem:rate_limited",
  "title": "Too Many Requests",
  "status": 429,
  "code": "rate_limited",
  "detail": "För många anrop. Försök igen om 23 sekunder.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}

Change a draft supplier invoice

PATCH /supplier-invoices/{supplierInvoice} updateSupplierInvoice

Changes a draft; a registered invoice answers 403 and never changes.

Scope

supplier-invoices:create

Parameters

  • supplierInvoice string (uuid) path required

    Supplier invoice id (UUID).

    Example: 8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e

Request body

application/jsonSupplierInvoicePatch required

Only the fields sent are changed; `rows`, when sent, replaces all rows.

  • supplier_id integer
  • invoice_number string nullable

    at most 50 characters

  • ocr string nullable

    pattern ^\d{2,25}$

  • invoice_date string (date)
  • due_date string (date) nullable
  • description string nullable

    at most 255 characters

  • credit boolean

    A credit note; send positive amounts.

  • vat_treatment string nullable

    Values: DOMESTIC, EU_GOODS, EU_SERVICES, NON_EU_SERVICES, SE_REVERSE_GOODS, SE_REVERSE_SERVICES

  • currency string nullable

    pattern ^[A-Za-z]{3}$

  • total_ore integer

    To pay, in kronor (öre).

  • vat_ore integer
  • foreign_total_minor integer nullable
  • exchange_rate_micros integer nullable
  • rows array<object>
    6 fields · each item
    • account integer nullable

      Default: the supplier's default account.

    • amount_ore integer required
    • description string nullable
    • vat_rate integer nullable
    • cost_center string nullable
    • project string nullable

Responses

200 OK – The draft.

application/jsonobject

  • data SupplierInvoice required
    29 fields
    • id string (uuid)
    • number integer nullable
    • kind string

      Values: INVOICE, CREDIT

    • status string

      Payment status: REGISTERED waits for approval, APPROVED waits for payment. Open enum.

      Values: DRAFT, REGISTERED, APPROVED, PAID

    • supplier_id integer
    • supplier_name string
    • invoice_number string nullable
    • ocr string nullable
    • invoice_date string (date)
    • due_date string (date)
    • description string nullable
    • vat_treatment string
    • currency string
    • total_ore integer
    • vat_ore integer
    • paid_ore integer
    • remaining_ore integer
    • invoice_currency string
    • foreign_total_minor integer nullable
    • foreign_paid_minor integer nullable
    • exchange_rate_micros integer nullable

      Kronor per unit in millionths (9.8264 is 9826400).

    • file object | any
      2 fields
      • One of object
        • name string
        • mime string
        • size integer
        • sha256 string
      • One of any
    • voucher_id string (uuid) nullable
    • registered_at string (date-time) nullable
    • approved_at string (date-time) nullable
    • rows array<SupplierInvoiceRow>
      6 fields · each item
      • account integer nullable
      • description string nullable
      • amount_ore integer
      • vat_rate integer nullable
      • cost_center string nullable
      • project string nullable
    • payments array<SupplierPayment>
      11 fields · each item
      • id integer
      • supplier_invoice_id string (uuid)
      • paid_on string (date)
      • amount_ore integer

        Kronor the bank took, öre (negative for money back on a credit note).

      • currency string
      • foreign_amount_minor integer nullable

        Hundredths of the invoice's currency, for an invoice in another currency.

      • exchange_difference_ore integer
      • account integer nullable
      • voucher_id string (uuid)
      • created_at string (date-time)
      • updated_at string (date-time)
    • created_at string (date-time)
    • updated_at string (date-time)

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

401 Unauthorized – Missing, invalid, expired or revoked token or API key. Also returned when the person behind the credential has left the company (an OAuth grant is then revoked for good).

All error codes · application/problem+json

  • unauthenticatedNo valid credential

    Nyckeln eller token saknas, är ogiltig, har gått ut eller har återkallats.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, WWW-Authenticate

403 Forbidden – The credential may not do this: a scope is missing, the granting person no longer has the permission, the address is not allowed, the company is archived (read-only) or no longer allows the app.

All error codes · application/problem+json

  • insufficient_scopeScope missing or permission lost

    Nyckeln eller appen saknar behörigheten vouchers:post, eller så har personen som gav den inte längre den behörigheten i företaget.

  • forbiddenThe action refuses (for example changing a posted voucher)

    Behörighet saknas för det här.

  • ip_not_allowedOutside the IP allowlist

    Företaget tillåter inte anrop från den här adressen.

  • company_archivedWrite to an archived company

    Företaget är arkiverat och kan bara läsas.

  • app_not_allowedThe company no longer allows the app

    Företaget tillåter inte längre den här appen.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

404 Not Found – Not found. Also the answer for another company's ids (existence is never revealed) and for every call while NONOX Connect is turned off for the company.

All error codes · application/problem+json

  • not_foundUnknown id, other company's id, or module off

    Det du söker finns inte.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

415 Unsupported Media Type – The body is not application/json (or multipart/form-data where a file is accepted).

All error codes · application/problem+json

  • unsupported_media_typeWrong Content-Type

    Skicka JSON (application/json).

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

422 Unprocessable Content – Validation failed, or the Idempotency-Key was already used for a different request.

All error codes · application/problem+json

  • validation_failedField errors

    Uppgifterna kunde inte godtas. Se errors.

  • idempotency_key_reusedSame key, different body

    Idempotency-Key har redan använts för en annan begäran. Använd en ny nyckel för en ny begäran.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

429 Too Many Requests – Rate limit exceeded (per credential, per company or per address). Wait Retry-After seconds.

All error codes · application/problem+json

  • rate_limitedOver the limit

    För många anrop. Försök igen om 23 sekunder.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, Retry-After

Code samples

curl
curl -X PATCH "https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e" \
  -H "Authorization: Bearer $NONOX_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
  "description": "Kontorsmaterial"
}'
PHP (Laravel)
use Illuminate\Support\Facades\Http;

$response = Http::withToken(config('services.nonox.token'))
    ->acceptJson()
    ->patch('https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e', [
        'description' => 'Kontorsmaterial',
    ]);

$response->throw();

$data = $response->json('data');
PHP (Guzzle)
$client = new \GuzzleHttp\Client(['timeout' => 30]);

$response = $client->request('PATCH', 'https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e', [
    'headers' => [
        'Authorization' => 'Bearer '.getenv('NONOX_TOKEN'),
        'Accept' => 'application/json',
    ],
    'json' => [
        'description' => 'Kontorsmaterial',
    ],
]);

$data = json_decode((string) $response->getBody(), true, flags: JSON_THROW_ON_ERROR)['data'];
JavaScript
const response = await fetch('https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.NONOX_TOKEN}`,
    Accept: 'application/json',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "description": "Kontorsmaterial"
  }),
});

if (!response.ok) {
  const problem = await response.json();
  throw new Error(`${problem.code}: ${problem.detail}`);
}

const { data } = await response.json();
Python
import os
import requests

response = requests.patch(
    "https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e",
    headers={
        "Authorization": f"Bearer {os.environ['NONOX_TOKEN']}",
        "Accept": "application/json",
    },
    json={
        "description": "Kontorsmaterial",
    },
    timeout=30,
)
response.raise_for_status()

data = response.json()["data"]

Responses · 200

application/json
{
  "data": {
    "id": "8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e",
    "number": 77,
    "kind": "INVOICE",
    "status": "APPROVED",
    "supplier_id": 9,
    "supplier_name": "Leverantören Exempel AB",
    "invoice_number": "F-2026-118",
    "ocr": "4005",
    "invoice_date": "2026-03-03",
    "due_date": "2026-04-02",
    "description": "Kontorsmaterial",
    "vat_treatment": "DOMESTIC",
    "currency": "SEK",
    "total_ore": 125000,
    "vat_ore": 25000,
    "paid_ore": 0,
    "remaining_ore": 125000,
    "invoice_currency": "SEK",
    "foreign_total_minor": null,
    "foreign_paid_minor": null,
    "exchange_rate_micros": null,
    "file": {
      "name": "faktura.pdf",
      "mime": "application/pdf",
      "size": 48211,
      "sha256": "3c1f0e9d8b7a6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d"
    },
    "voucher_id": "6e5d4c3b-2a19-4f8e-8d7c-6b5a49382716",
    "registered_at": "2026-03-03T14:00:00+01:00",
    "approved_at": "2026-03-04T09:30:00+01:00",
    "rows": [
      {
        "account": 5410,
        "description": "Förbrukningsinventarier",
        "amount_ore": 100000,
        "vat_rate": null,
        "cost_center": "20",
        "project": null
      }
    ],
    "payments": [],
    "created_at": "2026-03-03T13:50:00+01:00",
    "updated_at": "2026-03-04T09:30:00+01:00"
  }
}

Error

4011 example
No valid credential
{
  "type": "urn:nonox:problem:unauthenticated",
  "title": "Unauthorized",
  "status": 401,
  "code": "unauthenticated",
  "detail": "Nyckeln eller token saknas, är ogiltig, har gått ut eller har återkallats.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4035 examples
Scope missing or permission lost
{
  "type": "urn:nonox:problem:insufficient_scope",
  "title": "Forbidden",
  "status": 403,
  "code": "insufficient_scope",
  "detail": "Nyckeln eller appen saknar behörigheten vouchers:post, eller så har personen som gav den inte längre den behörigheten i företaget.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17",
  "required_scopes": [
    "vouchers:post"
  ]
}
The action refuses (for example changing a posted voucher)
{
  "type": "urn:nonox:problem:forbidden",
  "title": "Forbidden",
  "status": 403,
  "code": "forbidden",
  "detail": "Behörighet saknas för det här.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
Outside the IP allowlist
{
  "type": "urn:nonox:problem:ip_not_allowed",
  "title": "Forbidden",
  "status": 403,
  "code": "ip_not_allowed",
  "detail": "Företaget tillåter inte anrop från den här adressen.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
Write to an archived company
{
  "type": "urn:nonox:problem:company_archived",
  "title": "Forbidden",
  "status": 403,
  "code": "company_archived",
  "detail": "Företaget är arkiverat och kan bara läsas.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
The company no longer allows the app
{
  "type": "urn:nonox:problem:app_not_allowed",
  "title": "Forbidden",
  "status": 403,
  "code": "app_not_allowed",
  "detail": "Företaget tillåter inte längre den här appen.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4041 example
Unknown id, other company's id, or module off
{
  "type": "urn:nonox:problem:not_found",
  "title": "Not Found",
  "status": 404,
  "code": "not_found",
  "detail": "Det du söker finns inte.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4151 example
Wrong Content-Type
{
  "type": "urn:nonox:problem:unsupported_media_type",
  "title": "Unsupported Media Type",
  "status": 415,
  "code": "unsupported_media_type",
  "detail": "Skicka JSON (application/json).",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4222 examples
Field errors
{
  "type": "urn:nonox:problem:validation_failed",
  "title": "Unprocessable Content",
  "status": 422,
  "code": "validation_failed",
  "detail": "Uppgifterna kunde inte godtas. Se errors.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17",
  "errors": {
    "rows": [
      "Verifikationen balanserar inte: debet och kredit skiljer sig åt."
    ],
    "voucher_date": [
      "Det finns inget räkenskapsår som omfattar 2031-01-01."
    ]
  }
}
Same key, different body
{
  "type": "urn:nonox:problem:idempotency_key_reused",
  "title": "Unprocessable Content",
  "status": 422,
  "code": "idempotency_key_reused",
  "detail": "Idempotency-Key har redan använts för en annan begäran. Använd en ny nyckel för en ny begäran.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4291 example
Over the limit
{
  "type": "urn:nonox:problem:rate_limited",
  "title": "Too Many Requests",
  "status": 429,
  "code": "rate_limited",
  "detail": "För många anrop. Försök igen om 23 sekunder.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}

Delete a draft supplier invoice

DELETE /supplier-invoices/{supplierInvoice} deleteSupplierInvoice

Removes a draft and its file. A registered invoice can never be removed (403).

Scope

supplier-invoices:create

Parameters

  • supplierInvoice string (uuid) path required

    Supplier invoice id (UUID).

    Example: 8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e

Responses

204 No Content – Deleted.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

401 Unauthorized – Missing, invalid, expired or revoked token or API key. Also returned when the person behind the credential has left the company (an OAuth grant is then revoked for good).

All error codes · application/problem+json

  • unauthenticatedNo valid credential

    Nyckeln eller token saknas, är ogiltig, har gått ut eller har återkallats.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, WWW-Authenticate

403 Forbidden – The credential may not do this: a scope is missing, the granting person no longer has the permission, the address is not allowed, the company is archived (read-only) or no longer allows the app.

All error codes · application/problem+json

  • insufficient_scopeScope missing or permission lost

    Nyckeln eller appen saknar behörigheten vouchers:post, eller så har personen som gav den inte längre den behörigheten i företaget.

  • forbiddenThe action refuses (for example changing a posted voucher)

    Behörighet saknas för det här.

  • ip_not_allowedOutside the IP allowlist

    Företaget tillåter inte anrop från den här adressen.

  • company_archivedWrite to an archived company

    Företaget är arkiverat och kan bara läsas.

  • app_not_allowedThe company no longer allows the app

    Företaget tillåter inte längre den här appen.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

404 Not Found – Not found. Also the answer for another company's ids (existence is never revealed) and for every call while NONOX Connect is turned off for the company.

All error codes · application/problem+json

  • not_foundUnknown id, other company's id, or module off

    Det du söker finns inte.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

429 Too Many Requests – Rate limit exceeded (per credential, per company or per address). Wait Retry-After seconds.

All error codes · application/problem+json

  • rate_limitedOver the limit

    För många anrop. Försök igen om 23 sekunder.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, Retry-After

Code samples

curl
curl -X DELETE "https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e" \
  -H "Authorization: Bearer $NONOX_TOKEN" \
  -H "Accept: application/json"
PHP (Laravel)
use Illuminate\Support\Facades\Http;

$response = Http::withToken(config('services.nonox.token'))
    ->acceptJson()
    ->delete('https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e');

$response->throw();
PHP (Guzzle)
$client = new \GuzzleHttp\Client(['timeout' => 30]);

$response = $client->request('DELETE', 'https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e', [
    'headers' => [
        'Authorization' => 'Bearer '.getenv('NONOX_TOKEN'),
        'Accept' => 'application/json',
    ],
]);
JavaScript
const response = await fetch('https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e', {
  method: 'DELETE',
  headers: {
    Authorization: `Bearer ${process.env.NONOX_TOKEN}`,
    Accept: 'application/json',
  },
});

if (!response.ok) {
  const problem = await response.json();
  throw new Error(`${problem.code}: ${problem.detail}`);
}
Python
import os
import requests

response = requests.delete(
    "https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e",
    headers={
        "Authorization": f"Bearer {os.environ['NONOX_TOKEN']}",
        "Accept": "application/json",
    },
    timeout=30,
)
response.raise_for_status()

Error

4011 example
No valid credential
{
  "type": "urn:nonox:problem:unauthenticated",
  "title": "Unauthorized",
  "status": 401,
  "code": "unauthenticated",
  "detail": "Nyckeln eller token saknas, är ogiltig, har gått ut eller har återkallats.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4035 examples
Scope missing or permission lost
{
  "type": "urn:nonox:problem:insufficient_scope",
  "title": "Forbidden",
  "status": 403,
  "code": "insufficient_scope",
  "detail": "Nyckeln eller appen saknar behörigheten vouchers:post, eller så har personen som gav den inte längre den behörigheten i företaget.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17",
  "required_scopes": [
    "vouchers:post"
  ]
}
The action refuses (for example changing a posted voucher)
{
  "type": "urn:nonox:problem:forbidden",
  "title": "Forbidden",
  "status": 403,
  "code": "forbidden",
  "detail": "Behörighet saknas för det här.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
Outside the IP allowlist
{
  "type": "urn:nonox:problem:ip_not_allowed",
  "title": "Forbidden",
  "status": 403,
  "code": "ip_not_allowed",
  "detail": "Företaget tillåter inte anrop från den här adressen.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
Write to an archived company
{
  "type": "urn:nonox:problem:company_archived",
  "title": "Forbidden",
  "status": 403,
  "code": "company_archived",
  "detail": "Företaget är arkiverat och kan bara läsas.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
The company no longer allows the app
{
  "type": "urn:nonox:problem:app_not_allowed",
  "title": "Forbidden",
  "status": 403,
  "code": "app_not_allowed",
  "detail": "Företaget tillåter inte längre den här appen.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4041 example
Unknown id, other company's id, or module off
{
  "type": "urn:nonox:problem:not_found",
  "title": "Not Found",
  "status": 404,
  "code": "not_found",
  "detail": "Det du söker finns inte.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4291 example
Over the limit
{
  "type": "urn:nonox:problem:rate_limited",
  "title": "Too Many Requests",
  "status": 429,
  "code": "rate_limited",
  "detail": "För många anrop. Försök igen om 23 sekunder.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}

Add the invoice file

POST /supplier-invoices/{supplierInvoice}/file addSupplierInvoiceFile

Uploads the invoice itself to a draft as multipart/form-data in the field `file`: a PDF or an image (JPG, PNG, WebP), at most 10 MB, the type detected from the content. It becomes the underlag of the voucher when the invoice is registered. Replaces an earlier file.

Scope

supplier-invoices:create

Parameters

  • supplierInvoice string (uuid) path required

    Supplier invoice id (UUID).

    Example: 8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e

  • Idempotency-Key string header required

    Required on every POST. A unique value per intended operation (a UUID v4 is ideal), 1–255 visible ASCII characters. Kept 24 hours per credential: the same key with the same request returns the stored response (Idempotent-Replayed: true); with a different request 422 idempotency_key_reused; while the first is still running 409. Server errors (5xx) are not stored, so retry with the same key.

    at least 1 characters · at most 255 characters · Example: 3f6c1b7e-2d4a-4e8b-9c0d-5a1b2c3d4e5f

Request body

multipart/form-dataobject required

  • file string (binary) required

Responses

200 OK – The draft with its file.

application/jsonobject

  • data SupplierInvoice required
    29 fields
    • id string (uuid)
    • number integer nullable
    • kind string

      Values: INVOICE, CREDIT

    • status string

      Payment status: REGISTERED waits for approval, APPROVED waits for payment. Open enum.

      Values: DRAFT, REGISTERED, APPROVED, PAID

    • supplier_id integer
    • supplier_name string
    • invoice_number string nullable
    • ocr string nullable
    • invoice_date string (date)
    • due_date string (date)
    • description string nullable
    • vat_treatment string
    • currency string
    • total_ore integer
    • vat_ore integer
    • paid_ore integer
    • remaining_ore integer
    • invoice_currency string
    • foreign_total_minor integer nullable
    • foreign_paid_minor integer nullable
    • exchange_rate_micros integer nullable

      Kronor per unit in millionths (9.8264 is 9826400).

    • file object | any
      2 fields
      • One of object
        • name string
        • mime string
        • size integer
        • sha256 string
      • One of any
    • voucher_id string (uuid) nullable
    • registered_at string (date-time) nullable
    • approved_at string (date-time) nullable
    • rows array<SupplierInvoiceRow>
      6 fields · each item
      • account integer nullable
      • description string nullable
      • amount_ore integer
      • vat_rate integer nullable
      • cost_center string nullable
      • project string nullable
    • payments array<SupplierPayment>
      11 fields · each item
      • id integer
      • supplier_invoice_id string (uuid)
      • paid_on string (date)
      • amount_ore integer

        Kronor the bank took, öre (negative for money back on a credit note).

      • currency string
      • foreign_amount_minor integer nullable

        Hundredths of the invoice's currency, for an invoice in another currency.

      • exchange_difference_ore integer
      • account integer nullable
      • voucher_id string (uuid)
      • created_at string (date-time)
      • updated_at string (date-time)
    • created_at string (date-time)
    • updated_at string (date-time)

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, Idempotent-Replayed

400 Bad Request – Bad request: missing or invalid Idempotency-Key, or a body that is not a JSON object.

All error codes · application/problem+json

  • idempotency_key_requiredNo Idempotency-Key on a POST

    Skicka en unik Idempotency-Key (till exempel ett UUID) med varje POST, så att ett nytt försök aldrig bokför två gånger.

  • idempotency_key_invalidIdempotency-Key too long or not visible ASCII

    Idempotency-Key ska vara 1–255 synliga ASCII-tecken.

  • invalid_jsonMalformed JSON

    Kroppen är inte giltig JSON (ett objekt).

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

401 Unauthorized – Missing, invalid, expired or revoked token or API key. Also returned when the person behind the credential has left the company (an OAuth grant is then revoked for good).

All error codes · application/problem+json

  • unauthenticatedNo valid credential

    Nyckeln eller token saknas, är ogiltig, har gått ut eller har återkallats.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, WWW-Authenticate

403 Forbidden – The credential may not do this: a scope is missing, the granting person no longer has the permission, the address is not allowed, the company is archived (read-only) or no longer allows the app.

All error codes · application/problem+json

  • insufficient_scopeScope missing or permission lost

    Nyckeln eller appen saknar behörigheten vouchers:post, eller så har personen som gav den inte längre den behörigheten i företaget.

  • forbiddenThe action refuses (for example changing a posted voucher)

    Behörighet saknas för det här.

  • ip_not_allowedOutside the IP allowlist

    Företaget tillåter inte anrop från den här adressen.

  • company_archivedWrite to an archived company

    Företaget är arkiverat och kan bara läsas.

  • app_not_allowedThe company no longer allows the app

    Företaget tillåter inte längre den här appen.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

404 Not Found – Not found. Also the answer for another company's ids (existence is never revealed) and for every call while NONOX Connect is turned off for the company.

All error codes · application/problem+json

  • not_foundUnknown id, other company's id, or module off

    Det du söker finns inte.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

409 Conflict – A request with the same Idempotency-Key is still running.

All error codes · application/problem+json

  • idempotency_in_progressSame key, first request not finished

    En begäran med samma Idempotency-Key pågår. Vänta och försök igen.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, Retry-After

415 Unsupported Media Type – The body is not application/json (or multipart/form-data where a file is accepted).

All error codes · application/problem+json

  • unsupported_media_typeWrong Content-Type

    Skicka JSON (application/json).

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

422 Unprocessable Content – Validation failed, or the Idempotency-Key was already used for a different request.

All error codes · application/problem+json

  • validation_failedField errors

    Uppgifterna kunde inte godtas. Se errors.

  • idempotency_key_reusedSame key, different body

    Idempotency-Key har redan använts för en annan begäran. Använd en ny nyckel för en ny begäran.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

429 Too Many Requests – Rate limit exceeded (per credential, per company or per address). Wait Retry-After seconds.

All error codes · application/problem+json

  • rate_limitedOver the limit

    För många anrop. Försök igen om 23 sekunder.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, Retry-After

Code samples

curl
curl -X POST "https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e/file" \
  -H "Authorization: Bearer $NONOX_TOKEN" \
  -H "Accept: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -F "file=@kvitto.pdf;type=application/pdf"
PHP (Laravel)
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;

$response = Http::withToken(config('services.nonox.token'))
    ->acceptJson()
    ->withHeaders(['Idempotency-Key' => (string) Str::uuid()])
    ->attach('file', file_get_contents('kvitto.pdf'), 'kvitto.pdf')
    ->post('https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e/file');

$response->throw();

$data = $response->json('data');
PHP (Guzzle)
$client = new \GuzzleHttp\Client(['timeout' => 30]);

$response = $client->request('POST', 'https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e/file', [
    'headers' => [
        'Authorization' => 'Bearer '.getenv('NONOX_TOKEN'),
        'Accept' => 'application/json',
        'Idempotency-Key' => bin2hex(random_bytes(16)),
    ],
    'multipart' => [
        ['name' => 'file', 'contents' => fopen('kvitto.pdf', 'r'), 'filename' => 'kvitto.pdf'],
    ],
]);

$data = json_decode((string) $response->getBody(), true, flags: JSON_THROW_ON_ERROR)['data'];
JavaScript
import { readFile } from 'node:fs/promises';

const form = new FormData();
form.append('file', new Blob([await readFile('kvitto.pdf')], { type: 'application/pdf' }), 'kvitto.pdf');

const response = await fetch('https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e/file', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.NONOX_TOKEN}`,
    Accept: 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: form,
});

if (!response.ok) {
  const problem = await response.json();
  throw new Error(`${problem.code}: ${problem.detail}`);
}

const { data } = await response.json();
Python
import os
import uuid
import requests

response = requests.post(
    "https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e/file",
    headers={
        "Authorization": f"Bearer {os.environ['NONOX_TOKEN']}",
        "Accept": "application/json",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    files={"file": ("kvitto.pdf", open("kvitto.pdf", "rb"), "application/pdf")},
    timeout=30,
)
response.raise_for_status()

data = response.json()["data"]

Responses · 200

application/json
{
  "data": {
    "id": "8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e",
    "number": 77,
    "kind": "INVOICE",
    "status": "APPROVED",
    "supplier_id": 9,
    "supplier_name": "Leverantören Exempel AB",
    "invoice_number": "F-2026-118",
    "ocr": "4005",
    "invoice_date": "2026-03-03",
    "due_date": "2026-04-02",
    "description": "Kontorsmaterial",
    "vat_treatment": "DOMESTIC",
    "currency": "SEK",
    "total_ore": 125000,
    "vat_ore": 25000,
    "paid_ore": 0,
    "remaining_ore": 125000,
    "invoice_currency": "SEK",
    "foreign_total_minor": null,
    "foreign_paid_minor": null,
    "exchange_rate_micros": null,
    "file": {
      "name": "faktura.pdf",
      "mime": "application/pdf",
      "size": 48211,
      "sha256": "3c1f0e9d8b7a6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d"
    },
    "voucher_id": "6e5d4c3b-2a19-4f8e-8d7c-6b5a49382716",
    "registered_at": "2026-03-03T14:00:00+01:00",
    "approved_at": "2026-03-04T09:30:00+01:00",
    "rows": [
      {
        "account": 5410,
        "description": "Förbrukningsinventarier",
        "amount_ore": 100000,
        "vat_rate": null,
        "cost_center": "20",
        "project": null
      }
    ],
    "payments": [],
    "created_at": "2026-03-03T13:50:00+01:00",
    "updated_at": "2026-03-04T09:30:00+01:00"
  }
}

Error

4003 examples
No Idempotency-Key on a POST
{
  "type": "urn:nonox:problem:idempotency_key_required",
  "title": "Bad Request",
  "status": 400,
  "code": "idempotency_key_required",
  "detail": "Skicka en unik Idempotency-Key (till exempel ett UUID) med varje POST, så att ett nytt försök aldrig bokför två gånger.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
Idempotency-Key too long or not visible ASCII
{
  "type": "urn:nonox:problem:idempotency_key_invalid",
  "title": "Bad Request",
  "status": 400,
  "code": "idempotency_key_invalid",
  "detail": "Idempotency-Key ska vara 1–255 synliga ASCII-tecken.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
Malformed JSON
{
  "type": "urn:nonox:problem:invalid_json",
  "title": "Bad Request",
  "status": 400,
  "code": "invalid_json",
  "detail": "Kroppen är inte giltig JSON (ett objekt).",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4011 example
No valid credential
{
  "type": "urn:nonox:problem:unauthenticated",
  "title": "Unauthorized",
  "status": 401,
  "code": "unauthenticated",
  "detail": "Nyckeln eller token saknas, är ogiltig, har gått ut eller har återkallats.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4035 examples
Scope missing or permission lost
{
  "type": "urn:nonox:problem:insufficient_scope",
  "title": "Forbidden",
  "status": 403,
  "code": "insufficient_scope",
  "detail": "Nyckeln eller appen saknar behörigheten vouchers:post, eller så har personen som gav den inte längre den behörigheten i företaget.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17",
  "required_scopes": [
    "vouchers:post"
  ]
}
The action refuses (for example changing a posted voucher)
{
  "type": "urn:nonox:problem:forbidden",
  "title": "Forbidden",
  "status": 403,
  "code": "forbidden",
  "detail": "Behörighet saknas för det här.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
Outside the IP allowlist
{
  "type": "urn:nonox:problem:ip_not_allowed",
  "title": "Forbidden",
  "status": 403,
  "code": "ip_not_allowed",
  "detail": "Företaget tillåter inte anrop från den här adressen.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
Write to an archived company
{
  "type": "urn:nonox:problem:company_archived",
  "title": "Forbidden",
  "status": 403,
  "code": "company_archived",
  "detail": "Företaget är arkiverat och kan bara läsas.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
The company no longer allows the app
{
  "type": "urn:nonox:problem:app_not_allowed",
  "title": "Forbidden",
  "status": 403,
  "code": "app_not_allowed",
  "detail": "Företaget tillåter inte längre den här appen.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4041 example
Unknown id, other company's id, or module off
{
  "type": "urn:nonox:problem:not_found",
  "title": "Not Found",
  "status": 404,
  "code": "not_found",
  "detail": "Det du söker finns inte.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4091 example
Same key, first request not finished
{
  "type": "urn:nonox:problem:idempotency_in_progress",
  "title": "Conflict",
  "status": 409,
  "code": "idempotency_in_progress",
  "detail": "En begäran med samma Idempotency-Key pågår. Vänta och försök igen.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4151 example
Wrong Content-Type
{
  "type": "urn:nonox:problem:unsupported_media_type",
  "title": "Unsupported Media Type",
  "status": 415,
  "code": "unsupported_media_type",
  "detail": "Skicka JSON (application/json).",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4222 examples
Field errors
{
  "type": "urn:nonox:problem:validation_failed",
  "title": "Unprocessable Content",
  "status": 422,
  "code": "validation_failed",
  "detail": "Uppgifterna kunde inte godtas. Se errors.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17",
  "errors": {
    "rows": [
      "Verifikationen balanserar inte: debet och kredit skiljer sig åt."
    ],
    "voucher_date": [
      "Det finns inget räkenskapsår som omfattar 2031-01-01."
    ]
  }
}
Same key, different body
{
  "type": "urn:nonox:problem:idempotency_key_reused",
  "title": "Unprocessable Content",
  "status": 422,
  "code": "idempotency_key_reused",
  "detail": "Idempotency-Key har redan använts för en annan begäran. Använd en ny nyckel för en ny begäran.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4291 example
Over the limit
{
  "type": "urn:nonox:problem:rate_limited",
  "title": "Too Many Requests",
  "status": 429,
  "code": "rate_limited",
  "detail": "För många anrop. Försök igen om 23 sekunder.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}

Register a supplier invoice

POST /supplier-invoices/{supplierInvoice}/register registerSupplierInvoice

Gives the draft the next number, freezes the supplier and under fakturametoden books it in series D with the file as underlag. From here on it never changes; it waits for approval. Books a voucher as the person behind the credential, so the credential also needs vouchers:create and vouchers:post. Same Action as the app (RegisterSupplierInvoice).

Scope

supplier-invoices:create + vouchers:create + vouchers:post

Parameters

  • supplierInvoice string (uuid) path required

    Supplier invoice id (UUID).

    Example: 8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e

  • Idempotency-Key string header required

    Required on every POST. A unique value per intended operation (a UUID v4 is ideal), 1–255 visible ASCII characters. Kept 24 hours per credential: the same key with the same request returns the stored response (Idempotent-Replayed: true); with a different request 422 idempotency_key_reused; while the first is still running 409. Server errors (5xx) are not stored, so retry with the same key.

    at least 1 characters · at most 255 characters · Example: 3f6c1b7e-2d4a-4e8b-9c0d-5a1b2c3d4e5f

Responses

200 OK – The registered invoice.

application/jsonobject

  • data SupplierInvoice required
    29 fields
    • id string (uuid)
    • number integer nullable
    • kind string

      Values: INVOICE, CREDIT

    • status string

      Payment status: REGISTERED waits for approval, APPROVED waits for payment. Open enum.

      Values: DRAFT, REGISTERED, APPROVED, PAID

    • supplier_id integer
    • supplier_name string
    • invoice_number string nullable
    • ocr string nullable
    • invoice_date string (date)
    • due_date string (date)
    • description string nullable
    • vat_treatment string
    • currency string
    • total_ore integer
    • vat_ore integer
    • paid_ore integer
    • remaining_ore integer
    • invoice_currency string
    • foreign_total_minor integer nullable
    • foreign_paid_minor integer nullable
    • exchange_rate_micros integer nullable

      Kronor per unit in millionths (9.8264 is 9826400).

    • file object | any
      2 fields
      • One of object
        • name string
        • mime string
        • size integer
        • sha256 string
      • One of any
    • voucher_id string (uuid) nullable
    • registered_at string (date-time) nullable
    • approved_at string (date-time) nullable
    • rows array<SupplierInvoiceRow>
      6 fields · each item
      • account integer nullable
      • description string nullable
      • amount_ore integer
      • vat_rate integer nullable
      • cost_center string nullable
      • project string nullable
    • payments array<SupplierPayment>
      11 fields · each item
      • id integer
      • supplier_invoice_id string (uuid)
      • paid_on string (date)
      • amount_ore integer

        Kronor the bank took, öre (negative for money back on a credit note).

      • currency string
      • foreign_amount_minor integer nullable

        Hundredths of the invoice's currency, for an invoice in another currency.

      • exchange_difference_ore integer
      • account integer nullable
      • voucher_id string (uuid)
      • created_at string (date-time)
      • updated_at string (date-time)
    • created_at string (date-time)
    • updated_at string (date-time)

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, Idempotent-Replayed

400 Bad Request – Bad request: missing or invalid Idempotency-Key, or a body that is not a JSON object.

All error codes · application/problem+json

  • idempotency_key_requiredNo Idempotency-Key on a POST

    Skicka en unik Idempotency-Key (till exempel ett UUID) med varje POST, så att ett nytt försök aldrig bokför två gånger.

  • idempotency_key_invalidIdempotency-Key too long or not visible ASCII

    Idempotency-Key ska vara 1–255 synliga ASCII-tecken.

  • invalid_jsonMalformed JSON

    Kroppen är inte giltig JSON (ett objekt).

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

401 Unauthorized – Missing, invalid, expired or revoked token or API key. Also returned when the person behind the credential has left the company (an OAuth grant is then revoked for good).

All error codes · application/problem+json

  • unauthenticatedNo valid credential

    Nyckeln eller token saknas, är ogiltig, har gått ut eller har återkallats.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, WWW-Authenticate

403 Forbidden – The credential may not do this: a scope is missing, the granting person no longer has the permission, the address is not allowed, the company is archived (read-only) or no longer allows the app.

All error codes · application/problem+json

  • insufficient_scopeScope missing or permission lost

    Nyckeln eller appen saknar behörigheten vouchers:post, eller så har personen som gav den inte längre den behörigheten i företaget.

  • forbiddenThe action refuses (for example changing a posted voucher)

    Behörighet saknas för det här.

  • ip_not_allowedOutside the IP allowlist

    Företaget tillåter inte anrop från den här adressen.

  • company_archivedWrite to an archived company

    Företaget är arkiverat och kan bara läsas.

  • app_not_allowedThe company no longer allows the app

    Företaget tillåter inte längre den här appen.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

404 Not Found – Not found. Also the answer for another company's ids (existence is never revealed) and for every call while NONOX Connect is turned off for the company.

All error codes · application/problem+json

  • not_foundUnknown id, other company's id, or module off

    Det du söker finns inte.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

409 Conflict – A request with the same Idempotency-Key is still running.

All error codes · application/problem+json

  • idempotency_in_progressSame key, first request not finished

    En begäran med samma Idempotency-Key pågår. Vänta och försök igen.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, Retry-After

415 Unsupported Media Type – The body is not application/json (or multipart/form-data where a file is accepted).

All error codes · application/problem+json

  • unsupported_media_typeWrong Content-Type

    Skicka JSON (application/json).

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

422 Unprocessable Content – Validation failed, or the Idempotency-Key was already used for a different request.

All error codes · application/problem+json

  • validation_failedField errors

    Uppgifterna kunde inte godtas. Se errors.

  • idempotency_key_reusedSame key, different body

    Idempotency-Key har redan använts för en annan begäran. Använd en ny nyckel för en ny begäran.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

429 Too Many Requests – Rate limit exceeded (per credential, per company or per address). Wait Retry-After seconds.

All error codes · application/problem+json

  • rate_limitedOver the limit

    För många anrop. Försök igen om 23 sekunder.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, Retry-After

Code samples

curl
curl -X POST "https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e/register" \
  -H "Authorization: Bearer $NONOX_TOKEN" \
  -H "Accept: application/json" \
  -H "Idempotency-Key: $(uuidgen)"
PHP (Laravel)
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;

$response = Http::withToken(config('services.nonox.token'))
    ->acceptJson()
    ->withHeaders(['Idempotency-Key' => (string) Str::uuid()])
    ->post('https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e/register');

$response->throw();

$data = $response->json('data');
PHP (Guzzle)
$client = new \GuzzleHttp\Client(['timeout' => 30]);

$response = $client->request('POST', 'https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e/register', [
    'headers' => [
        'Authorization' => 'Bearer '.getenv('NONOX_TOKEN'),
        'Accept' => 'application/json',
        'Idempotency-Key' => bin2hex(random_bytes(16)),
    ],
]);

$data = json_decode((string) $response->getBody(), true, flags: JSON_THROW_ON_ERROR)['data'];
JavaScript
const response = await fetch('https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e/register', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.NONOX_TOKEN}`,
    Accept: 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
});

if (!response.ok) {
  const problem = await response.json();
  throw new Error(`${problem.code}: ${problem.detail}`);
}

const { data } = await response.json();
Python
import os
import uuid
import requests

response = requests.post(
    "https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e/register",
    headers={
        "Authorization": f"Bearer {os.environ['NONOX_TOKEN']}",
        "Accept": "application/json",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    timeout=30,
)
response.raise_for_status()

data = response.json()["data"]

Responses · 200

application/json
{
  "data": {
    "id": "8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e",
    "number": 77,
    "kind": "INVOICE",
    "status": "APPROVED",
    "supplier_id": 9,
    "supplier_name": "Leverantören Exempel AB",
    "invoice_number": "F-2026-118",
    "ocr": "4005",
    "invoice_date": "2026-03-03",
    "due_date": "2026-04-02",
    "description": "Kontorsmaterial",
    "vat_treatment": "DOMESTIC",
    "currency": "SEK",
    "total_ore": 125000,
    "vat_ore": 25000,
    "paid_ore": 0,
    "remaining_ore": 125000,
    "invoice_currency": "SEK",
    "foreign_total_minor": null,
    "foreign_paid_minor": null,
    "exchange_rate_micros": null,
    "file": {
      "name": "faktura.pdf",
      "mime": "application/pdf",
      "size": 48211,
      "sha256": "3c1f0e9d8b7a6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d"
    },
    "voucher_id": "6e5d4c3b-2a19-4f8e-8d7c-6b5a49382716",
    "registered_at": "2026-03-03T14:00:00+01:00",
    "approved_at": "2026-03-04T09:30:00+01:00",
    "rows": [
      {
        "account": 5410,
        "description": "Förbrukningsinventarier",
        "amount_ore": 100000,
        "vat_rate": null,
        "cost_center": "20",
        "project": null
      }
    ],
    "payments": [],
    "created_at": "2026-03-03T13:50:00+01:00",
    "updated_at": "2026-03-04T09:30:00+01:00"
  }
}

Error

4003 examples
No Idempotency-Key on a POST
{
  "type": "urn:nonox:problem:idempotency_key_required",
  "title": "Bad Request",
  "status": 400,
  "code": "idempotency_key_required",
  "detail": "Skicka en unik Idempotency-Key (till exempel ett UUID) med varje POST, så att ett nytt försök aldrig bokför två gånger.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
Idempotency-Key too long or not visible ASCII
{
  "type": "urn:nonox:problem:idempotency_key_invalid",
  "title": "Bad Request",
  "status": 400,
  "code": "idempotency_key_invalid",
  "detail": "Idempotency-Key ska vara 1–255 synliga ASCII-tecken.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
Malformed JSON
{
  "type": "urn:nonox:problem:invalid_json",
  "title": "Bad Request",
  "status": 400,
  "code": "invalid_json",
  "detail": "Kroppen är inte giltig JSON (ett objekt).",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4011 example
No valid credential
{
  "type": "urn:nonox:problem:unauthenticated",
  "title": "Unauthorized",
  "status": 401,
  "code": "unauthenticated",
  "detail": "Nyckeln eller token saknas, är ogiltig, har gått ut eller har återkallats.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4035 examples
Scope missing or permission lost
{
  "type": "urn:nonox:problem:insufficient_scope",
  "title": "Forbidden",
  "status": 403,
  "code": "insufficient_scope",
  "detail": "Nyckeln eller appen saknar behörigheten vouchers:post, eller så har personen som gav den inte längre den behörigheten i företaget.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17",
  "required_scopes": [
    "vouchers:post"
  ]
}
The action refuses (for example changing a posted voucher)
{
  "type": "urn:nonox:problem:forbidden",
  "title": "Forbidden",
  "status": 403,
  "code": "forbidden",
  "detail": "Behörighet saknas för det här.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
Outside the IP allowlist
{
  "type": "urn:nonox:problem:ip_not_allowed",
  "title": "Forbidden",
  "status": 403,
  "code": "ip_not_allowed",
  "detail": "Företaget tillåter inte anrop från den här adressen.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
Write to an archived company
{
  "type": "urn:nonox:problem:company_archived",
  "title": "Forbidden",
  "status": 403,
  "code": "company_archived",
  "detail": "Företaget är arkiverat och kan bara läsas.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
The company no longer allows the app
{
  "type": "urn:nonox:problem:app_not_allowed",
  "title": "Forbidden",
  "status": 403,
  "code": "app_not_allowed",
  "detail": "Företaget tillåter inte längre den här appen.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4041 example
Unknown id, other company's id, or module off
{
  "type": "urn:nonox:problem:not_found",
  "title": "Not Found",
  "status": 404,
  "code": "not_found",
  "detail": "Det du söker finns inte.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4091 example
Same key, first request not finished
{
  "type": "urn:nonox:problem:idempotency_in_progress",
  "title": "Conflict",
  "status": 409,
  "code": "idempotency_in_progress",
  "detail": "En begäran med samma Idempotency-Key pågår. Vänta och försök igen.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4151 example
Wrong Content-Type
{
  "type": "urn:nonox:problem:unsupported_media_type",
  "title": "Unsupported Media Type",
  "status": 415,
  "code": "unsupported_media_type",
  "detail": "Skicka JSON (application/json).",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4222 examples
Field errors
{
  "type": "urn:nonox:problem:validation_failed",
  "title": "Unprocessable Content",
  "status": 422,
  "code": "validation_failed",
  "detail": "Uppgifterna kunde inte godtas. Se errors.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17",
  "errors": {
    "rows": [
      "Verifikationen balanserar inte: debet och kredit skiljer sig åt."
    ],
    "voucher_date": [
      "Det finns inget räkenskapsår som omfattar 2031-01-01."
    ]
  }
}
Same key, different body
{
  "type": "urn:nonox:problem:idempotency_key_reused",
  "title": "Unprocessable Content",
  "status": 422,
  "code": "idempotency_key_reused",
  "detail": "Idempotency-Key har redan använts för en annan begäran. Använd en ny nyckel för en ny begäran.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4291 example
Over the limit
{
  "type": "urn:nonox:problem:rate_limited",
  "title": "Too Many Requests",
  "status": 429,
  "code": "rate_limited",
  "detail": "För många anrop. Försök igen om 23 sekunder.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}

Approve (attest) a supplier invoice

POST /supplier-invoices/{supplierInvoice}/approve approveSupplierInvoice

Approves a registered invoice so it may be paid, and fires supplier_invoice.approved. Segregation of duties: while the company requires approval by someone else (the default), the person who created or registered the invoice gets 422, whichever app or key they use. Same Action as the app (ApproveSupplierInvoice).

Scope

supplier-invoices:approve

Parameters

  • supplierInvoice string (uuid) path required

    Supplier invoice id (UUID).

    Example: 8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e

  • Idempotency-Key string header required

    Required on every POST. A unique value per intended operation (a UUID v4 is ideal), 1–255 visible ASCII characters. Kept 24 hours per credential: the same key with the same request returns the stored response (Idempotent-Replayed: true); with a different request 422 idempotency_key_reused; while the first is still running 409. Server errors (5xx) are not stored, so retry with the same key.

    at least 1 characters · at most 255 characters · Example: 3f6c1b7e-2d4a-4e8b-9c0d-5a1b2c3d4e5f

Responses

200 OK – The approved invoice.

application/jsonobject

  • data SupplierInvoice required
    29 fields
    • id string (uuid)
    • number integer nullable
    • kind string

      Values: INVOICE, CREDIT

    • status string

      Payment status: REGISTERED waits for approval, APPROVED waits for payment. Open enum.

      Values: DRAFT, REGISTERED, APPROVED, PAID

    • supplier_id integer
    • supplier_name string
    • invoice_number string nullable
    • ocr string nullable
    • invoice_date string (date)
    • due_date string (date)
    • description string nullable
    • vat_treatment string
    • currency string
    • total_ore integer
    • vat_ore integer
    • paid_ore integer
    • remaining_ore integer
    • invoice_currency string
    • foreign_total_minor integer nullable
    • foreign_paid_minor integer nullable
    • exchange_rate_micros integer nullable

      Kronor per unit in millionths (9.8264 is 9826400).

    • file object | any
      2 fields
      • One of object
        • name string
        • mime string
        • size integer
        • sha256 string
      • One of any
    • voucher_id string (uuid) nullable
    • registered_at string (date-time) nullable
    • approved_at string (date-time) nullable
    • rows array<SupplierInvoiceRow>
      6 fields · each item
      • account integer nullable
      • description string nullable
      • amount_ore integer
      • vat_rate integer nullable
      • cost_center string nullable
      • project string nullable
    • payments array<SupplierPayment>
      11 fields · each item
      • id integer
      • supplier_invoice_id string (uuid)
      • paid_on string (date)
      • amount_ore integer

        Kronor the bank took, öre (negative for money back on a credit note).

      • currency string
      • foreign_amount_minor integer nullable

        Hundredths of the invoice's currency, for an invoice in another currency.

      • exchange_difference_ore integer
      • account integer nullable
      • voucher_id string (uuid)
      • created_at string (date-time)
      • updated_at string (date-time)
    • created_at string (date-time)
    • updated_at string (date-time)

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, Idempotent-Replayed

400 Bad Request – Bad request: missing or invalid Idempotency-Key, or a body that is not a JSON object.

All error codes · application/problem+json

  • idempotency_key_requiredNo Idempotency-Key on a POST

    Skicka en unik Idempotency-Key (till exempel ett UUID) med varje POST, så att ett nytt försök aldrig bokför två gånger.

  • idempotency_key_invalidIdempotency-Key too long or not visible ASCII

    Idempotency-Key ska vara 1–255 synliga ASCII-tecken.

  • invalid_jsonMalformed JSON

    Kroppen är inte giltig JSON (ett objekt).

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

401 Unauthorized – Missing, invalid, expired or revoked token or API key. Also returned when the person behind the credential has left the company (an OAuth grant is then revoked for good).

All error codes · application/problem+json

  • unauthenticatedNo valid credential

    Nyckeln eller token saknas, är ogiltig, har gått ut eller har återkallats.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, WWW-Authenticate

403 Forbidden – The credential may not do this: a scope is missing, the granting person no longer has the permission, the address is not allowed, the company is archived (read-only) or no longer allows the app.

All error codes · application/problem+json

  • insufficient_scopeScope missing or permission lost

    Nyckeln eller appen saknar behörigheten vouchers:post, eller så har personen som gav den inte längre den behörigheten i företaget.

  • forbiddenThe action refuses (for example changing a posted voucher)

    Behörighet saknas för det här.

  • ip_not_allowedOutside the IP allowlist

    Företaget tillåter inte anrop från den här adressen.

  • company_archivedWrite to an archived company

    Företaget är arkiverat och kan bara läsas.

  • app_not_allowedThe company no longer allows the app

    Företaget tillåter inte längre den här appen.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

404 Not Found – Not found. Also the answer for another company's ids (existence is never revealed) and for every call while NONOX Connect is turned off for the company.

All error codes · application/problem+json

  • not_foundUnknown id, other company's id, or module off

    Det du söker finns inte.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

409 Conflict – A request with the same Idempotency-Key is still running.

All error codes · application/problem+json

  • idempotency_in_progressSame key, first request not finished

    En begäran med samma Idempotency-Key pågår. Vänta och försök igen.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, Retry-After

415 Unsupported Media Type – The body is not application/json (or multipart/form-data where a file is accepted).

All error codes · application/problem+json

  • unsupported_media_typeWrong Content-Type

    Skicka JSON (application/json).

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

422 Unprocessable Content – Validation failed, or the Idempotency-Key was already used for a different request.

All error codes · application/problem+json

  • validation_failedField errors

    Uppgifterna kunde inte godtas. Se errors.

  • idempotency_key_reusedSame key, different body

    Idempotency-Key har redan använts för en annan begäran. Använd en ny nyckel för en ny begäran.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

429 Too Many Requests – Rate limit exceeded (per credential, per company or per address). Wait Retry-After seconds.

All error codes · application/problem+json

  • rate_limitedOver the limit

    För många anrop. Försök igen om 23 sekunder.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, Retry-After

Code samples

curl
curl -X POST "https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e/approve" \
  -H "Authorization: Bearer $NONOX_TOKEN" \
  -H "Accept: application/json" \
  -H "Idempotency-Key: $(uuidgen)"
PHP (Laravel)
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;

$response = Http::withToken(config('services.nonox.token'))
    ->acceptJson()
    ->withHeaders(['Idempotency-Key' => (string) Str::uuid()])
    ->post('https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e/approve');

$response->throw();

$data = $response->json('data');
PHP (Guzzle)
$client = new \GuzzleHttp\Client(['timeout' => 30]);

$response = $client->request('POST', 'https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e/approve', [
    'headers' => [
        'Authorization' => 'Bearer '.getenv('NONOX_TOKEN'),
        'Accept' => 'application/json',
        'Idempotency-Key' => bin2hex(random_bytes(16)),
    ],
]);

$data = json_decode((string) $response->getBody(), true, flags: JSON_THROW_ON_ERROR)['data'];
JavaScript
const response = await fetch('https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e/approve', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.NONOX_TOKEN}`,
    Accept: 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
});

if (!response.ok) {
  const problem = await response.json();
  throw new Error(`${problem.code}: ${problem.detail}`);
}

const { data } = await response.json();
Python
import os
import uuid
import requests

response = requests.post(
    "https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e/approve",
    headers={
        "Authorization": f"Bearer {os.environ['NONOX_TOKEN']}",
        "Accept": "application/json",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    timeout=30,
)
response.raise_for_status()

data = response.json()["data"]

Responses · 200

application/json
{
  "data": {
    "id": "8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e",
    "number": 77,
    "kind": "INVOICE",
    "status": "APPROVED",
    "supplier_id": 9,
    "supplier_name": "Leverantören Exempel AB",
    "invoice_number": "F-2026-118",
    "ocr": "4005",
    "invoice_date": "2026-03-03",
    "due_date": "2026-04-02",
    "description": "Kontorsmaterial",
    "vat_treatment": "DOMESTIC",
    "currency": "SEK",
    "total_ore": 125000,
    "vat_ore": 25000,
    "paid_ore": 0,
    "remaining_ore": 125000,
    "invoice_currency": "SEK",
    "foreign_total_minor": null,
    "foreign_paid_minor": null,
    "exchange_rate_micros": null,
    "file": {
      "name": "faktura.pdf",
      "mime": "application/pdf",
      "size": 48211,
      "sha256": "3c1f0e9d8b7a6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d"
    },
    "voucher_id": "6e5d4c3b-2a19-4f8e-8d7c-6b5a49382716",
    "registered_at": "2026-03-03T14:00:00+01:00",
    "approved_at": "2026-03-04T09:30:00+01:00",
    "rows": [
      {
        "account": 5410,
        "description": "Förbrukningsinventarier",
        "amount_ore": 100000,
        "vat_rate": null,
        "cost_center": "20",
        "project": null
      }
    ],
    "payments": [],
    "created_at": "2026-03-03T13:50:00+01:00",
    "updated_at": "2026-03-04T09:30:00+01:00"
  }
}

Error

4003 examples
No Idempotency-Key on a POST
{
  "type": "urn:nonox:problem:idempotency_key_required",
  "title": "Bad Request",
  "status": 400,
  "code": "idempotency_key_required",
  "detail": "Skicka en unik Idempotency-Key (till exempel ett UUID) med varje POST, så att ett nytt försök aldrig bokför två gånger.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
Idempotency-Key too long or not visible ASCII
{
  "type": "urn:nonox:problem:idempotency_key_invalid",
  "title": "Bad Request",
  "status": 400,
  "code": "idempotency_key_invalid",
  "detail": "Idempotency-Key ska vara 1–255 synliga ASCII-tecken.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
Malformed JSON
{
  "type": "urn:nonox:problem:invalid_json",
  "title": "Bad Request",
  "status": 400,
  "code": "invalid_json",
  "detail": "Kroppen är inte giltig JSON (ett objekt).",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4011 example
No valid credential
{
  "type": "urn:nonox:problem:unauthenticated",
  "title": "Unauthorized",
  "status": 401,
  "code": "unauthenticated",
  "detail": "Nyckeln eller token saknas, är ogiltig, har gått ut eller har återkallats.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4035 examples
Scope missing or permission lost
{
  "type": "urn:nonox:problem:insufficient_scope",
  "title": "Forbidden",
  "status": 403,
  "code": "insufficient_scope",
  "detail": "Nyckeln eller appen saknar behörigheten vouchers:post, eller så har personen som gav den inte längre den behörigheten i företaget.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17",
  "required_scopes": [
    "vouchers:post"
  ]
}
The action refuses (for example changing a posted voucher)
{
  "type": "urn:nonox:problem:forbidden",
  "title": "Forbidden",
  "status": 403,
  "code": "forbidden",
  "detail": "Behörighet saknas för det här.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
Outside the IP allowlist
{
  "type": "urn:nonox:problem:ip_not_allowed",
  "title": "Forbidden",
  "status": 403,
  "code": "ip_not_allowed",
  "detail": "Företaget tillåter inte anrop från den här adressen.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
Write to an archived company
{
  "type": "urn:nonox:problem:company_archived",
  "title": "Forbidden",
  "status": 403,
  "code": "company_archived",
  "detail": "Företaget är arkiverat och kan bara läsas.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
The company no longer allows the app
{
  "type": "urn:nonox:problem:app_not_allowed",
  "title": "Forbidden",
  "status": 403,
  "code": "app_not_allowed",
  "detail": "Företaget tillåter inte längre den här appen.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4041 example
Unknown id, other company's id, or module off
{
  "type": "urn:nonox:problem:not_found",
  "title": "Not Found",
  "status": 404,
  "code": "not_found",
  "detail": "Det du söker finns inte.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4091 example
Same key, first request not finished
{
  "type": "urn:nonox:problem:idempotency_in_progress",
  "title": "Conflict",
  "status": 409,
  "code": "idempotency_in_progress",
  "detail": "En begäran med samma Idempotency-Key pågår. Vänta och försök igen.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4151 example
Wrong Content-Type
{
  "type": "urn:nonox:problem:unsupported_media_type",
  "title": "Unsupported Media Type",
  "status": 415,
  "code": "unsupported_media_type",
  "detail": "Skicka JSON (application/json).",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4222 examples
Field errors
{
  "type": "urn:nonox:problem:validation_failed",
  "title": "Unprocessable Content",
  "status": 422,
  "code": "validation_failed",
  "detail": "Uppgifterna kunde inte godtas. Se errors.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17",
  "errors": {
    "rows": [
      "Verifikationen balanserar inte: debet och kredit skiljer sig åt."
    ],
    "voucher_date": [
      "Det finns inget räkenskapsår som omfattar 2031-01-01."
    ]
  }
}
Same key, different body
{
  "type": "urn:nonox:problem:idempotency_key_reused",
  "title": "Unprocessable Content",
  "status": 422,
  "code": "idempotency_key_reused",
  "detail": "Idempotency-Key har redan använts för en annan begäran. Använd en ny nyckel för en ny begäran.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4291 example
Over the limit
{
  "type": "urn:nonox:problem:rate_limited",
  "title": "Too Many Requests",
  "status": 429,
  "code": "rate_limited",
  "detail": "För många anrop. Försök igen om 23 sekunder.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}

Register a supplier payment

POST /supplier-invoices/{supplierInvoice}/payments paySupplierInvoice

Registers money paid for an approved invoice, booked in series E; PAID when nothing remains. For an invoice in another currency send foreign_amount_minor too; the exchange difference is booked on 3960 or 7960. Books a voucher as the person behind the credential, so the credential also needs vouchers:create and vouchers:post. Same Action as the app (PaySupplierInvoice).

Scope

payments:create + vouchers:create + vouchers:post

Parameters

  • supplierInvoice string (uuid) path required

    Supplier invoice id (UUID).

    Example: 8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e

  • Idempotency-Key string header required

    Required on every POST. A unique value per intended operation (a UUID v4 is ideal), 1–255 visible ASCII characters. Kept 24 hours per credential: the same key with the same request returns the stored response (Idempotent-Replayed: true); with a different request 422 idempotency_key_reused; while the first is still running 409. Server errors (5xx) are not stored, so retry with the same key.

    at least 1 characters · at most 255 characters · Example: 3f6c1b7e-2d4a-4e8b-9c0d-5a1b2c3d4e5f

Request body

application/jsonSupplierPaymentInput required

  • paid_on string (date) required
  • amount_ore integer required

    Kronor the bank took; negative for money back on a credit note.

  • foreign_amount_minor integer nullable
  • account integer nullable

    minimum 1900 · maximum 1999

Responses

201 Created – The payment.

application/jsonobject

  • data SupplierPayment required
    11 fields
    • id integer
    • supplier_invoice_id string (uuid)
    • paid_on string (date)
    • amount_ore integer

      Kronor the bank took, öre (negative for money back on a credit note).

    • currency string
    • foreign_amount_minor integer nullable

      Hundredths of the invoice's currency, for an invoice in another currency.

    • exchange_difference_ore integer
    • account integer nullable
    • voucher_id string (uuid)
    • created_at string (date-time)
    • updated_at string (date-time)

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, Location, Idempotent-Replayed

400 Bad Request – Bad request: missing or invalid Idempotency-Key, or a body that is not a JSON object.

All error codes · application/problem+json

  • idempotency_key_requiredNo Idempotency-Key on a POST

    Skicka en unik Idempotency-Key (till exempel ett UUID) med varje POST, så att ett nytt försök aldrig bokför två gånger.

  • idempotency_key_invalidIdempotency-Key too long or not visible ASCII

    Idempotency-Key ska vara 1–255 synliga ASCII-tecken.

  • invalid_jsonMalformed JSON

    Kroppen är inte giltig JSON (ett objekt).

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

401 Unauthorized – Missing, invalid, expired or revoked token or API key. Also returned when the person behind the credential has left the company (an OAuth grant is then revoked for good).

All error codes · application/problem+json

  • unauthenticatedNo valid credential

    Nyckeln eller token saknas, är ogiltig, har gått ut eller har återkallats.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, WWW-Authenticate

403 Forbidden – The credential may not do this: a scope is missing, the granting person no longer has the permission, the address is not allowed, the company is archived (read-only) or no longer allows the app.

All error codes · application/problem+json

  • insufficient_scopeScope missing or permission lost

    Nyckeln eller appen saknar behörigheten vouchers:post, eller så har personen som gav den inte längre den behörigheten i företaget.

  • forbiddenThe action refuses (for example changing a posted voucher)

    Behörighet saknas för det här.

  • ip_not_allowedOutside the IP allowlist

    Företaget tillåter inte anrop från den här adressen.

  • company_archivedWrite to an archived company

    Företaget är arkiverat och kan bara läsas.

  • app_not_allowedThe company no longer allows the app

    Företaget tillåter inte längre den här appen.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

404 Not Found – Not found. Also the answer for another company's ids (existence is never revealed) and for every call while NONOX Connect is turned off for the company.

All error codes · application/problem+json

  • not_foundUnknown id, other company's id, or module off

    Det du söker finns inte.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

409 Conflict – A request with the same Idempotency-Key is still running.

All error codes · application/problem+json

  • idempotency_in_progressSame key, first request not finished

    En begäran med samma Idempotency-Key pågår. Vänta och försök igen.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, Retry-After

415 Unsupported Media Type – The body is not application/json (or multipart/form-data where a file is accepted).

All error codes · application/problem+json

  • unsupported_media_typeWrong Content-Type

    Skicka JSON (application/json).

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

422 Unprocessable Content – Validation failed, or the Idempotency-Key was already used for a different request.

All error codes · application/problem+json

  • validation_failedField errors

    Uppgifterna kunde inte godtas. Se errors.

  • idempotency_key_reusedSame key, different body

    Idempotency-Key har redan använts för en annan begäran. Använd en ny nyckel för en ny begäran.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

429 Too Many Requests – Rate limit exceeded (per credential, per company or per address). Wait Retry-After seconds.

All error codes · application/problem+json

  • rate_limitedOver the limit

    För många anrop. Försök igen om 23 sekunder.

Response headers: X-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, Retry-After

Code samples

curl
curl -X POST "https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e/payments" \
  -H "Authorization: Bearer $NONOX_TOKEN" \
  -H "Accept: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "paid_on": "2026-03-31",
  "amount_ore": 125000
}'
PHP (Laravel)
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;

$response = Http::withToken(config('services.nonox.token'))
    ->acceptJson()
    ->withHeaders(['Idempotency-Key' => (string) Str::uuid()])
    ->post('https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e/payments', [
        'paid_on' => '2026-03-31',
        'amount_ore' => 125000,
    ]);

$response->throw();

$data = $response->json('data');
PHP (Guzzle)
$client = new \GuzzleHttp\Client(['timeout' => 30]);

$response = $client->request('POST', 'https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e/payments', [
    'headers' => [
        'Authorization' => 'Bearer '.getenv('NONOX_TOKEN'),
        'Accept' => 'application/json',
        'Idempotency-Key' => bin2hex(random_bytes(16)),
    ],
    'json' => [
        'paid_on' => '2026-03-31',
        'amount_ore' => 125000,
    ],
]);

$data = json_decode((string) $response->getBody(), true, flags: JSON_THROW_ON_ERROR)['data'];
JavaScript
const response = await fetch('https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e/payments', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.NONOX_TOKEN}`,
    Accept: 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "paid_on": "2026-03-31",
    "amount_ore": 125000
  }),
});

if (!response.ok) {
  const problem = await response.json();
  throw new Error(`${problem.code}: ${problem.detail}`);
}

const { data } = await response.json();
Python
import os
import uuid
import requests

response = requests.post(
    "https://nonox.app/api/v1/supplier-invoices/8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e/payments",
    headers={
        "Authorization": f"Bearer {os.environ['NONOX_TOKEN']}",
        "Accept": "application/json",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "paid_on": "2026-03-31",
        "amount_ore": 125000,
    },
    timeout=30,
)
response.raise_for_status()

data = response.json()["data"]

Responses · 201

application/json
{
  "data": {
    "id": 41,
    "supplier_invoice_id": "8d1f4b2a-3c5e-4a7b-9d0e-6f1a2b3c4d5e",
    "paid_on": "2026-03-31",
    "amount_ore": 125000,
    "currency": "SEK",
    "foreign_amount_minor": null,
    "exchange_difference_ore": 0,
    "account": 1930,
    "voucher_id": "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
    "created_at": "2026-03-31T08:00:00+02:00",
    "updated_at": "2026-03-31T08:00:00+02:00"
  }
}

Error

4003 examples
No Idempotency-Key on a POST
{
  "type": "urn:nonox:problem:idempotency_key_required",
  "title": "Bad Request",
  "status": 400,
  "code": "idempotency_key_required",
  "detail": "Skicka en unik Idempotency-Key (till exempel ett UUID) med varje POST, så att ett nytt försök aldrig bokför två gånger.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
Idempotency-Key too long or not visible ASCII
{
  "type": "urn:nonox:problem:idempotency_key_invalid",
  "title": "Bad Request",
  "status": 400,
  "code": "idempotency_key_invalid",
  "detail": "Idempotency-Key ska vara 1–255 synliga ASCII-tecken.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
Malformed JSON
{
  "type": "urn:nonox:problem:invalid_json",
  "title": "Bad Request",
  "status": 400,
  "code": "invalid_json",
  "detail": "Kroppen är inte giltig JSON (ett objekt).",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4011 example
No valid credential
{
  "type": "urn:nonox:problem:unauthenticated",
  "title": "Unauthorized",
  "status": 401,
  "code": "unauthenticated",
  "detail": "Nyckeln eller token saknas, är ogiltig, har gått ut eller har återkallats.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4035 examples
Scope missing or permission lost
{
  "type": "urn:nonox:problem:insufficient_scope",
  "title": "Forbidden",
  "status": 403,
  "code": "insufficient_scope",
  "detail": "Nyckeln eller appen saknar behörigheten vouchers:post, eller så har personen som gav den inte längre den behörigheten i företaget.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17",
  "required_scopes": [
    "vouchers:post"
  ]
}
The action refuses (for example changing a posted voucher)
{
  "type": "urn:nonox:problem:forbidden",
  "title": "Forbidden",
  "status": 403,
  "code": "forbidden",
  "detail": "Behörighet saknas för det här.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
Outside the IP allowlist
{
  "type": "urn:nonox:problem:ip_not_allowed",
  "title": "Forbidden",
  "status": 403,
  "code": "ip_not_allowed",
  "detail": "Företaget tillåter inte anrop från den här adressen.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
Write to an archived company
{
  "type": "urn:nonox:problem:company_archived",
  "title": "Forbidden",
  "status": 403,
  "code": "company_archived",
  "detail": "Företaget är arkiverat och kan bara läsas.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
The company no longer allows the app
{
  "type": "urn:nonox:problem:app_not_allowed",
  "title": "Forbidden",
  "status": 403,
  "code": "app_not_allowed",
  "detail": "Företaget tillåter inte längre den här appen.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4041 example
Unknown id, other company's id, or module off
{
  "type": "urn:nonox:problem:not_found",
  "title": "Not Found",
  "status": 404,
  "code": "not_found",
  "detail": "Det du söker finns inte.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4091 example
Same key, first request not finished
{
  "type": "urn:nonox:problem:idempotency_in_progress",
  "title": "Conflict",
  "status": 409,
  "code": "idempotency_in_progress",
  "detail": "En begäran med samma Idempotency-Key pågår. Vänta och försök igen.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4151 example
Wrong Content-Type
{
  "type": "urn:nonox:problem:unsupported_media_type",
  "title": "Unsupported Media Type",
  "status": 415,
  "code": "unsupported_media_type",
  "detail": "Skicka JSON (application/json).",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4222 examples
Field errors
{
  "type": "urn:nonox:problem:validation_failed",
  "title": "Unprocessable Content",
  "status": 422,
  "code": "validation_failed",
  "detail": "Uppgifterna kunde inte godtas. Se errors.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17",
  "errors": {
    "rows": [
      "Verifikationen balanserar inte: debet och kredit skiljer sig åt."
    ],
    "voucher_date": [
      "Det finns inget räkenskapsår som omfattar 2031-01-01."
    ]
  }
}
Same key, different body
{
  "type": "urn:nonox:problem:idempotency_key_reused",
  "title": "Unprocessable Content",
  "status": 422,
  "code": "idempotency_key_reused",
  "detail": "Idempotency-Key har redan använts för en annan begäran. Använd en ny nyckel för en ny begäran.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}
4291 example
Over the limit
{
  "type": "urn:nonox:problem:rate_limited",
  "title": "Too Many Requests",
  "status": 429,
  "code": "rate_limited",
  "detail": "För många anrop. Försök igen om 23 sekunder.",
  "instance": "urn:nonox:request:7f3c2a9e-4b1d-4c8e-9a51-2d6f0e8b3c17"
}