Billing
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.
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.
The queues stay closed until the connector reports that configuration ready. Until then, the queues and reports answer BILLING_INTEGRATION_NOT_CONFIGURED. See 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
Each call is described in the billing reference.
- Verify the proposals. Only with the
basisVerificationgate. PollGET /v1/billing/verification-queue. For each proposal, callpasswith yourexternalReference, orrejectwith areason. 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. - 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 callreport-sent, orreport-failedwith areason. - Report payments. Call
report-paymentonce for each payment your platform receives. Callreport-payment-reversedwhen a payment is taken back. Both routes also exist under/v1/billing/invoices/by-external-reference/{ref}/, addressed by your own reference. - Report write-offs. Call
report-write-offwhen your platform gives up collecting an amount. It is addressed by the invoice id only. - 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 callreport-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.
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 lists every billing code and its status.