Skip to content

Pay an invoice through the API

3 steps · Checked against the API code 3 November 2026

A guide does one task from start to finish. Every endpoint, with all its fields and cases, is in the API reference.

There are three ways to pay an invoice through the API. Charge the card saved on the account. Use account credit. Create a payment link for someone who pays in a browser. Each call returns the result at once. There is no job to follow.

  • An API key with the scope write:billing. It also covers read:billing, which steps 1 and 3 need. Keys and scopes
  • The requests read the key from $HOSTUP_API_KEY, as set in the Quickstart.
  • To pay by card: a card saved on the account. List payment methods shows it.

Call GET /api/v2/billing/invoices with status=unpaid. That includes the overdue ones. Use status=overdue for only those past their due date. To find one invoice by its number, add searchQuery=202600001.

GET /api/v2/billing/invoices
curl "https://cloud.hostup.se/api/v2/billing/invoices?status=unpaid&limit=20" \
-H "Authorization: Bearer $HOSTUP_API_KEY"
Response 200
{
"data": [
{
"id": "inv_01hxa3b4c5d6e7f8g9h0j1k2m3",
"number": "202600001",
"totals": {
"currencyCode": "SEK",
"outstanding": 249,
"…": "…"
},
"actions": {
"canPay": { "allowed": true, "reason": null },
"canGeneratePaymentLink": { "allowed": true, "reason": null },
"…": "…"
},
"paymentMethods": {
"card": { "available": true, "…": "…" },
"accountCredit": {
"available": true,
"requirements": {
"maximumAmount": {
"amount": 249,
"currencyCode": "SEK",
"comparison": "less_than_or_equal"
},
"…": "…"
},
"…": "…"
},
"…": "…"
},
"isBulkPaymentChild": false,
"bulkPaymentParentId": null,
"…": "…"
}
],
"hasMore": false,
"nextCursor": null
}

Keep the id of the invoice you want to pay:

export INVOICE_ID="inv_…" # data[].id of that invoice

When hasMore is true, send nextCursor as cursor for the next page. Pagination

actions.canPay says whether the invoice can be paid at all. Decide from it, not from status. When allowed is false, reason says why. Every way of paying is then blocked.

When canPay.allowed is true, three fields tell you which way of paying works for this invoice:

  • paymentMethods.card.available: the invoice can be paid by card in step 2. It is false when the balance is at or below the card minimum. reason then says so. Use account credit for such an invoice. This field does not say whether a card is saved. List payment methods does.
  • paymentMethods.accountCredit.available: the account has credit in the invoice’s currency to use in step 2. requirements.maximumAmount is the most you can apply.
  • actions.canGeneratePaymentLink.allowed: a payment link can be created in step 2. The payer then chooses among the methods the invoice allows.

The order response has the invoice ID in invoice.id. Keep it:

export INVOICE_ID="inv_…" # invoice.id from the order response

GET /api/v2/billing/invoices/{id} returns that one invoice with the same actions and paymentMethods. Read it as in step 3.

When isBulkPaymentChild is true, the invoice is already included in another invoice. bulkPaymentParentId is that invoice’s ID. Pay that invoice instead: put its ID in INVOICE_ID. canPay still says allowed for the included invoice. The pay calls accept it too. Paying it does not lower the other invoice. The same amount would then be asked for twice.

Pick one of the ways below. Step 1 shows which ones this invoice allows.

WayUse it when
Let the automatic card charge pay itA card is saved and payment on the due date is early enough.
With the saved cardYou want it paid now and paymentMethods.card.available is true.
With account creditpaymentMethods.accountCredit.available is true.
With a payment linkSomeone else pays in a browser. Also when the bank asks for 3-D Secure.

Call GET /api/v2/billing/unpaid-summary. It adds up what is owed. It also says whether automatic card payment will settle the invoices on their due date.

GET /api/v2/billing/unpaid-summary
curl "https://cloud.hostup.se/api/v2/billing/unpaid-summary" \
-H "Authorization: Bearer $HOSTUP_API_KEY"
Response 200
{
"totalAmount": 249,
"currencyCode": "SEK",
"count": 1,
"invoices": [
{
"id": "inv_01hxa3b4c5d6e7f8g9h0j1k2m3",
"number": "202600001",
"amount": 249,
"dueStatus": "upcoming",
"daysUntilDue": 9,
"…": "…"
}
],
"autoCharge": {
"enabled": true,
"status": "scheduled",
"scheduledAt": "2026-11-11T19:00:00.000Z",
"failureKind": null,
"…": "…"
},
"…": "…"
}
  • autoCharge.status scheduled: the saved card is charged automatically at scheduledAt, 20:00 Swedish time on the earliest due date. Pay now only if you want the invoice settled earlier.
  • autoCharge.status failed: an invoice is past due and the automatic charge did not go through. failureKind says why. Pay it another way below.
  • autoCharge.status off: automatic card payment is turned off or no card is saved. Nothing is charged until you pay.

Call POST /api/v2/billing/invoices/{id}/actions/pay. Send {} to charge the whole outstanding amount to the saved card. To pay part of the balance, send amount in the invoice’s currency. It cannot be more than what is outstanding. It must also be above the card minimum in paymentMethods.card.requirements.minimumAmount.

POST /api/v2/billing/invoices/{id}/actions/pay
curl -X POST "https://cloud.hostup.se/api/v2/billing/invoices/$INVOICE_ID/actions/pay" \
-H "Authorization: Bearer $HOSTUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
Response 200
{
"id": "inv_01hxa3b4c5d6e7f8g9h0j1k2m3",
"number": "202600001",
"status": "paid",
"dates": {
"paidAt": "2026-11-02T10:15:41.000Z",
"…": "…"
},
"totals": {
"currencyCode": "SEK",
"amountPaid": 249,
"outstanding": 0,
"…": "…"
},
"…": "…"
}

After a part payment, totals.outstanding shows what is left.

A 502 upstream_invoice_shape_invalid means the card was charged but the updated invoice could not be read. Read it as in step 3. Do not pay again.

A charge the bank refuses returns 400 payment_failed. Errors you may hit says what to do for each reason.

The response is 400 payment_failed with paymentFailure.kind authentication_required. The bank wants the card holder to confirm the payment. A charge sent from your server cannot do that. Send a payment link instead. The card holder then pays in the browser, where that confirmation can take place.

Call POST /api/v2/billing/invoices/{id}/actions/apply-credit. Send the amount of credit to apply. It must be more than zero. paymentMethods.accountCredit.requirements.maximumAmount from step 1 is the highest amount that works.

POST /api/v2/billing/invoices/{id}/actions/apply-credit
curl -X POST "https://cloud.hostup.se/api/v2/billing/invoices/$INVOICE_ID/actions/apply-credit" \
-H "Authorization: Bearer $HOSTUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"amount": 249}'
Response 200
{
"id": "inv_01hxa3b4c5d6e7f8g9h0j1k2m3",
"number": "202600001",
"status": "paid",
"totals": {
"currencyCode": "SEK",
"creditApplied": 249,
"outstanding": 0,
"…": "…"
},
"…": "…"
}

When the credit covers only part of the invoice, the rest stays unpaid. Pay it with the card or a payment link.

Call POST /api/v2/billing/invoices/{id}/actions/generate-payment-link. A payment link lets someone pay without an API key or a login, for example a colleague with the company card. Send the request with no body.

POST /api/v2/billing/invoices/{id}/actions/generate-payment-link
curl -X POST "https://cloud.hostup.se/api/v2/billing/invoices/$INVOICE_ID/actions/generate-payment-link" \
-H "Authorization: Bearer $HOSTUP_API_KEY" \
-H "Content-Type: application/json"
Response 200
{
"paymentUrl": "https://cloud.hostup.se/billing/pay/550e8400-e29b-41d4-a716-446655440000",
"expiresAt": "2026-12-02T10:15:41.000Z",
"invoice": {
"id": "inv_01hxa3b4c5d6e7f8g9h0j1k2m3",
"number": "202600001",
"amount": 249,
"currencyCode": "SEK",
"dueAt": "2026-11-11T00:00:00.000Z",
"status": "unpaid",
"paymentUrl": "https://cloud.hostup.se/billing/pay/550e8400-e29b-41d4-a716-446655440000"
}
}

Send paymentUrl to the payer yourself. Hostup does not e-mail it. The page shows the invoice with its lines and the ways it can be paid, such as card or Swish. A card used on the page is not saved on the account. Anyone who has the link can open it. Share it only with the payer.

This paymentUrl is a full URL. The invoice.paymentUrl in an order response is not: it is a path on https://cloud.hostup.se.

invoice.amount is the invoice total, not what is still owed. The page asks only for the outstanding amount. To tell the payer how much to pay, use totals.outstanding from step 1.

The link works for 30 days, until expiresAt. Asking again while a link is active returns the same link. The link stops working when the invoice is paid.

Read the invoice with GET /api/v2/billing/invoices/{id} and skipCache=1. skipCache=1 makes sure you get its current payment state. After a payment link, poll it every few minutes until the payer has paid.

GET /api/v2/billing/invoices/{id}
curl "https://cloud.hostup.se/api/v2/billing/invoices/$INVOICE_ID?skipCache=1" \
-H "Authorization: Bearer $HOSTUP_API_KEY"
Response 200
{
"id": "inv_01hxa3b4c5d6e7f8g9h0j1k2m3",
"number": "202600001",
"status": "paid",
"dates": {
"paidAt": "2026-11-02T10:15:41.000Z",
"…": "…"
},
"totals": {
"outstanding": 0,
"…": "…"
},
"payments": [
{
"kind": "payment",
"method": "card",
"methodLabel": "Card",
"amount": 249,
"currencyCode": "SEK",
"at": "2026-11-02T10:15:41.000Z"
}
],
"…": "…"
}

status paid and totals.outstanding 0 mean the invoice is settled. payments lists each payment with its method.

Once the invoice is paid, Hostup carries out what it was for. A new order is set up: Order a VPS and Register a domain show how to follow it. A renewal moves the service’s next due date forward. A paid upgrade, IP change or extra bandwidth is applied. A Cloud VPS that was paused over unpaid invoices comes back when every invoice behind the pause is paid.

  • 400 payment_failed: the card was not charged. detail gives the reason. Don’t retry the same card in a loop. paymentFailure.kind says what to do:

    • card_declined, card_expired or card_missing: save a new card in the client area and pay again.
    • insufficient_funds: try again later. A payment link also works.
    • authentication_required: send a payment link (3-D Secure).
    • unknown: read detail. It may be the amount, not the card. An amount above the balance or at or below the card minimum gives this kind.

    The card payment also uses this code with status 409 when the invoice changed during the call. With status 503, the invoice could not be read. Read the invoice again before you pay. A decline from this call is reported only in the response. Hostup normally sends no card-decline e-mail for it.

  • 502 upstream_invoice_shape_invalid: the payment or credit went through. Reading the updated invoice afterwards failed. Do not pay again. Read the invoice as in step 3.

  • 409 payment_method_invalid: the account has no saved card. The same code comes back when the card in savedCardId no longer exists. The response has paymentLinkGeneratorUrl, the path of the payment-link call for this invoice. POST to it as shown under With a payment link. You can also save a card in the client area first.

  • 409 invoice_payment_in_progress: a payment of this invoice is already running. Wait a moment. Read the invoice before you try again. It may be paid already. A paid invoice returns 409 invoice_already_paid.

  • 409 invoice_not_payable: from account credit, the invoice is in another currency than the account. The same code comes for an invoice that adds funds to the account credit. detail says which.

  • 400 invalid_request: the body is wrong. The usual causes are a field the call does not take, an amount that is not a positive number and a savedCardId that is not a card on this account. Account credit also needs amount. For the card payment, detail lists every rule at once. It does not say which one failed. For account credit, errors[0].pointer names the field. There, errors[0].code amount_exceeds_available_credit or amount_exceeds_invoice_balance means amount is too high. Lower it to paymentMethods.accountCredit.requirements.maximumAmount.

Next: Order a VPS