---
title: Billing
description: Deliver an organization's approved invoices through your ERP or invoicing platform, and report each outcome back.
---

The billing API is for an invoicing connector: an app that connects an organization to its ERP or invoicing platform. Hydda One builds the invoices, and an operator approves them. Your connector handles each approved invoice. It delivers the invoice, collects payment, and books it in the customer's own system.

Your connector does all the calling. Hydda One never calls it. The connector reads work from queues and reports each outcome on its own route.

## What the token needs

The caller is an installed connector, with a token from the installation-token grant. See [Authentication](/guides/authentication).

The installation must hold the `Billing Integration` role at the organization. Hydda One grants it when an administrator saves the connector's configuration at the organization. An organization has one invoicing connector at a time. See [Roles](/guides/roles).

The queues stay closed until the connector reports that configuration `ready`. Until then, the queues and reports answer `BILLING_INTEGRATION_NOT_CONFIGURED`. See [Integrations](/guides/surfaces/integrations) for how to report it.

A token without an organization answers `403`.

### Gates

The connector's manifest declares which parts of the invoicing it handles. Hydda One calls these parts gates.

| Gate | Required | What the connector does |
| --- | --- | --- |
| `sending` | Yes | Delivers each approved invoice, then reports it sent or failed. Reports payments and write-offs. Discards deleted invoices. |
| `basisVerification` | No | Checks each proposed invoice before it becomes an invoice, then passes or rejects it. |

A route for a gate the connector did not declare answers `BILLING_INTEGRATION_GATE_NOT_DECLARED`. It never answers an empty queue instead.

## The flow

```mermaid
sequenceDiagram
  participant C as Your connector
  participant H as Hydda One
  participant E as Your ERP
  opt basisVerification gate
    C->>H: GET /v1/billing/verification-queue
    H-->>C: proposals
    C->>E: check the proposal
    C->>H: POST /v1/billing/invoice-proposals/{id}/pass
    H-->>C: the proposal and the invoice it became
  end
  C->>H: GET /v1/billing/send-queue
  H-->>C: approved invoices due today
  C->>E: issue the invoice
  C->>H: POST /v1/billing/invoices/{id}/report-sent
  E-->>C: a payment arrives
  C->>H: POST /v1/billing/invoices/{id}/report-payment
  C->>H: GET /v1/billing/withdrawal-queue
  H-->>C: deleted invoices
  C->>E: discard the copy
  C->>H: POST /v1/billing/invoice-withdrawals/{invoiceId}/report-discarded
```

Each call is described in the [billing reference](/reference/billing).

1. **Verify the proposals.** Only with the `basisVerification` gate. Poll `GET /v1/billing/verification-queue`. For each proposal, call `pass` with your `externalReference`, or `reject` with a `reason`. A pass answers with the proposal and the invoice it became. The invoice carries your reference. It reaches the send queue once an operator approves it.
2. **Send the invoices.** Poll `GET /v1/billing/send-queue`. It lists approved invoices whose requested invoice date has come, oldest approval first. "Today" is the current calendar day in UTC. Issue each invoice in your ERP. Then call `report-sent`, or `report-failed` with a `reason`.
3. **Report payments.** Call `report-payment` once for each payment your platform receives. Call `report-payment-reversed` when a payment is taken back. Both routes also exist under `/v1/billing/invoices/by-external-reference/{ref}/`, addressed by your own reference.
4. **Report write-offs.** Call `report-write-off` when your platform gives up collecting an amount. It is addressed by the invoice id only.
5. **Discard deleted invoices.** Poll `GET /v1/billing/withdrawal-queue`. It lists invoices an operator deleted after you attached your reference to them. Discard your copy, then call `report-discarded`.

### What report-sent carries

`report-sent` brings back what your ERP issued. Your ERP decides the legal invoice, so Hydda One records what it issued.

| Field | Required | What it is |
| --- | --- | --- |
| `externalReference` | Yes | Your own id for the invoice, 1 to 200 characters. |
| `issuedInvoiceNumber` | Yes | The legal invoice number your ERP gave it. Hydda One's own `invoiceNumber` is internal. |
| `issuedInvoiceDate` | Yes | The date the invoice was issued. |
| `dueDate` | Yes | The date payment is due. Hydda One judges an invoice overdue against it. |
| `issuedTotalGrossMinorUnits` | Yes | The total including VAT that your ERP issued. Every payment is measured against it. |
| `paymentReference`, `paymentReferenceType` | No | The reference the payer quotes, such as an OCR number. Send both or neither. |
| `documentUrl` | No | An `https` link where the issued document can be fetched. |

If the invoice came from a proposal you passed, it already carries your reference. `externalReference` must then match it.

### Which company an invoice belongs to

One connector serves the whole organization. The organization can run several companies, each with its own ERP account.

Every queue item names `businessContextId`. It is the business that owns the invoice's contract. Use the credentials of your configuration at that business. When that business has no configuration, use the organization's configuration. See [Integrations](/guides/surfaces/integrations).

`businessContextId` is `null` when Hydda One does not know the business. Do not guess a company. Report the invoice failed instead.

### What an item carries

An item carries the invoice or proposal, its lines in `position` order, and its VAT subtotals. The `recipient` block holds the customer's name, type, identifiers, invoice address and delivery method. Every field is frozen when the document is written, so two reads of one item always agree.

Issue the invoice with `requestedInvoiceDate` and `requestedDueDate` as they stand. Do not compute other dates.

A credit note rides the same queues. Its `documentKind` is `credit-note`, and `creditedInvoiceId` names the invoice it credits. Its totals are negative, and it is settled with a negative payment.

An identifier with the scheme `se-personnummer` or `se-samordningsnummer` is personal data. Keep it out of your logs.

## Statuses

An invoice moves through these statuses on this API:

| `status` | Meaning |
| --- | --- |
| `approved` | An operator approved it. It is on the send queue from its requested invoice date. |
| `sent` | You reported it sent. A payment reversal can also return it here. |
| `failed` | You reported it failed. An operator corrects the material and builds the invoice again. |
| `paid` | Its payments reached or passed the issued total. |
| `written-off` | It is settled, and part of it was written off. |

The balance is the issued total, less every payment that was not reversed and every write-off. The invoice becomes `paid` when the balance reaches zero or goes past it. An overpayment is accepted. If any write-off counted, it becomes `written-off` instead. A partial payment or write-off leaves it `sent`.

A proposal is `proposed` until you pass or reject it. It never expires. Only an operator can cancel it.

## Rules for callers

### A queue is not consumed by reading it

Reading a queue claims nothing. An item stays on its queue until a report changes its status. If your connector crashes, poll again and you get the same items.

Work can therefore arrive twice. Your connector may reach your ERP and fail before it reports. It then gets the item again. Check your own reference before you issue a document a second time.

An item you cannot act on leaves the queue only when you report it failed. Nothing else clears it.

### What a repeated report does

| Report | Sent again |
| --- | --- |
| `report-sent`, `report-failed` | `400` with `BILLING_INVOICE_STATUS_INVALID`. The invoice is no longer `approved`, for example because your first report landed. |
| `pass`, `reject` | `400` with `BILLING_PROPOSAL_STATUS_INVALID`. The proposal is no longer `proposed`. |
| `report-payment` | Changes nothing and answers the unchanged invoice, when the amount and date match. |
| `report-payment-reversed` | Changes nothing and answers the unchanged invoice. |
| `report-write-off` | Changes nothing and answers the unchanged invoice, when the amount and date match. |
| `report-discarded` | Answers `204` and changes nothing. |

`platformPaymentId` and `platformWriteOffId` are your payment platform's own ids, 1 to 200 characters. They make the payment and write-off reports safe to repeat.

### Amounts

A payment or write-off amount is never zero. Its sign must match the sign of the issued total. A refund on a credit note is a negative payment.

Hydda One accepts payments on a `sent`, `paid` or `written-off` invoice. A write-off cannot be undone, and there is no route for it.

### Limits

| What | Limit |
| --- | --- |
| `limit` on every queue | 1 to 200. Defaults to 50. |
| `reason` on reject and report-failed | 1 to 1000 characters. |
| Requests under `/v1/billing` | 300 a minute from each IP address. |

The queues have no cursor. You empty a queue by reporting on its items. A request over the rate limit gets `429`.

## Errors to handle

| Code | Status | When |
| --- | --- | --- |
| `BILLING_INTEGRATION_NOT_CONFIGURED` | 400 | The organization has no ready invoicing connector. |
| `BILLING_INTEGRATION_GATE_NOT_DECLARED` | 400 | Your connector did not declare the gate this route needs. |
| `BILLING_INVOICE_STATUS_INVALID` | 400 | The invoice is not in a status this report accepts. |
| `BILLING_PROPOSAL_STATUS_INVALID` | 400 | The proposal is no longer `proposed`. |
| `BILLING_PROPOSAL_MATERIAL_ALREADY_INVOICED` | 409 | Another invoice already bills this proposal's material. Do not retry. An operator cancels the proposal. |
| `BILLING_EXTERNAL_REFERENCE_ALREADY_USED` | 409 | The invoice already carries a different `externalReference`. |
| `BILLING_PAYMENT_REPLAY_MISMATCH` | 409 | The same `platformPaymentId` came with a different amount or date. |
| `BILLING_PAYMENT_REVERSED` | 409 | The payment was reversed, and cannot be reported again. |
| `BILLING_PAYMENT_NOT_FOUND` | 404 | The invoice has no payment with that `platformPaymentId`. |
| `BILLING_PAYMENT_AMOUNT_INVALID` | 400 | The amount is zero, or its sign does not match the issued total. |
| `BILLING_WRITE_OFF_REPLAY_MISMATCH` | 409 | The same `platformWriteOffId` came with a different amount or date. |
| `BILLING_WRITE_OFF_AMOUNT_INVALID` | 400 | The amount is zero, or its sign does not match the issued total. |
| `VALIDATION_FAILED` | 400 | A body, query or path parameter does not match its schema. |

A `404` without a `code` means the invoice or proposal does not exist, or belongs to another organization.

[Errors](/guides/errors) lists every billing code and its status.
