Home

Retrieving Invoices and Recording Payments

This guide covers listing and retrieving invoices, then recording a manual payment against one.

Authentication uses the same Authorization and AccessKey headers as every other endpoint — see the Authentication guide if you don’t have these set up yet.

Listing Invoices

GET https://api.karbonhq.com/v3/Invoices
Authorization: Bearer {token}
AccessKey: {key}

Filtering Invoices

Use $filter to narrow results down to invoices in a particular state. The /v3/Invoices endpoint supports filtering by InvoiceStatus:

GET /v3/Invoices?$filter=InvoiceStatus eq 'Approved'

Supported InvoiceStatus values: Approved, AwaitingPayment, Paid, Exported, Voided. InvoiceStatus is the only field you can filter by.

Sorting Invoices

# Most recent invoices first
GET /v3/Invoices?$orderby=InvoiceDate desc

# By invoice number
GET /v3/Invoices?$orderby=InvoiceNumber

# Recently created or updated
GET /v3/Invoices?$orderby=CreatedAt desc
GET /v3/Invoices?$orderby=UpdatedAt desc

Paginating Through Results

Use $top and $skip or follow the @odata.nextLink in the response:

GET /v3/Invoices?$top=50

Response:

{
  "@odata.count": 214,
  "@odata.nextLink": "https://api.karbonhq.com/v3/Invoices?$skip=50",
  "value": [ ... ]
}

@odata.count is returned automatically. Always use the @odata.nextLink URL directly rather than manually incrementing $skip. See the Pagination guide for details.

Retrieving a Specific Invoice

Each invoice in the list response includes an InvoiceKey. Use it to fetch a single invoice:

GET https://api.karbonhq.com/v3/Invoices/{InvoiceKey}
Authorization: Bearer {token}
AccessKey: {key}

The response includes the invoice total, subtotal, tax total, status, currency, payment due date, client details, and a TaxLineItems array breaking down individual tax components.

A 404 response means no invoice exists with that key — either it was deleted, or the key is wrong.

Asking for more than the invoice summary

By default the invoice response is a summary. To pull line items, payments, or the source breakdown back in the same request, add $expand= and list the sections you want:

Add to the URLWhat comes back
?$expand=LineItemsThe invoice line items — the rows that appear on the invoice itself.
?$expand=PaymentsAny payments already recorded against the invoice.
?$expand=DataThe underlying items the invoice is billing for. Ad hoc invoices return the Work Items and Expenses behind each charge; recurring fixed-fee invoices return the Services being billed for.

Combine sections by comma-separating them:

GET /v3/Invoices/{InvoiceKey}?$expand=LineItems,Payments,Data

$expand only works when retrieving a single invoice — it is not supported on the /v3/Invoices list endpoint. $filter and $orderby on the list endpoint can be combined freely with each other and with pagination.

Only ask for what you need — additional data will result in a larger payload and take longer to process. See Avoid unnecessary $expand for more.

Where to read the tax breakdown

Tax can be recorded against either the line items or the billed items, depending on the invoice. Check TaxLocation to know which array holds the tax fields:

  • TaxLocation=Presentation — tax is on each LineItems entry; tax fields in Data are null. All current ad hoc invoices.
  • TaxLocation=Data — tax is on each Data entry; tax fields in LineItems are null. All recurring billed invoices, and future ad hoc invoices.

If you need tax regardless of where it lives, request both LineItems and Data and read whichever side TaxLocation points to.

What the Data array looks like

For an ad hoc invoice, each entry is a Work Item or Client (for client time/expenses), with Work and/or Expenses populated and Service always null:

"Data": [
  {
    "EntityKey": "M4QTnt1srdD",
    "EntityType": "Work Item",
    "Billed": 800,
    "Service": null,
    "Work": { ... },
    "Expenses": { ... }
  }
]

For a recurring fixed-fee invoice, each entry is a Service, with Work and Expenses both null:

"Data": [
  {
    "EntityKey": "4lQzzVNBdHwq",
    "EntityType": "Service",
    "Billed": 150,
    "Service": { ... },
    "Work": null,
    "Expenses": null
  }
]

Listing Existing Payments

To see payments already recorded:

GET https://api.karbonhq.com/v3/Payments
Authorization: Bearer {token}
AccessKey: {key}

Recording a Manual Payment

Once you have the InvoiceKey, record a payment against it:

POST https://api.karbonhq.com/v3/ManualPayments
Authorization: Bearer {token}
AccessKey: {key}
Content-Type: application/json

{
  "InvoiceKey": "8fRnKqT3mXvP",
  "PaymentMethod": "EFT",
  "PaymentDate": "2026-03-31",
  "TotalAmount": 1500.00
}

Required fields:

FieldDescription
InvoiceKeyThe key of the invoice being paid
PaymentMethodOne of: Check, Cash, EFT, Credit Card, BankTransfer, DirectDebit, Other
PaymentDateISO 8601 date (e.g., 2026-03-31)
TotalAmountThe amount being paid (decimal)

TotalAmount is recorded in the Karbon account’s configured currency, and PaymentDate is interpreted in the account’s configured date/timezone — both are inherited from Karbon settings rather than passed in the request.

A successful 201 Created response returns the new payment record including its PaymentKey.

Reversing a Manual Payment

Reversing a payment leaves a record behind — it appears in the payments list as a Manual Payment Reversed entry, preserving the audit trail. Deleting a payment removes it entirely, with no record that it ever existed. Reach for reversal when something was recorded incorrectly after the fact; reach for delete only when the payment should never have been recorded at all.

To reverse, call the ReverseManualPayment endpoint with the PaymentKey from the original payment and the date the reversal should be recorded:

POST https://api.karbonhq.com/v3/ReverseManualPayment
Authorization: Bearer {token}
AccessKey: {key}
Content-Type: application/json

{
  "PaymentKey": "5hWjNbL2cYqR",
  "ReversalDate": "2026-04-01T00:00:00Z"
}

Deleting a Manual Payment

Alternatively, delete it directly:

DELETE https://api.karbonhq.com/v3/ManualPayments/{PaymentKey}
Authorization: Bearer {token}
AccessKey: {key}

Subscribing to Invoice Webhooks

To be notified when invoices are created or updated without polling, subscribe to invoice webhooks:

POST https://api.karbonhq.com/v3/WebhookSubscriptions
Authorization: Bearer {token}
AccessKey: {key}
Content-Type: application/json

{
  "WebhookType": "Invoice",
  "TargetUrl": "https://your-app.example.com/webhooks/karbon",
  "SigningKey": "your-secret-signing-key-min-16-chars"
}

Your endpoint will receive payloads in this format:

{
  "ResourcePermaKey": "{InvoiceKey}",
  "ResourceType": "Invoice",
  "ActionType": "Updated",
  "TimeStamp": "2026-03-31T14:22:00Z"
}

ActionType is always Updated for invoice webhooks — Karbon does not emit separate Created, Paid, or Voided events. Fetch the full invoice using the ResourcePermaKey when you receive a notification to see what changed.

The SigningKey you provided when subscribing is used to verify each payload came from Karbon — see the Webhooks guide for how to validate signatures on your endpoint.

Important: You can only have one webhook subscription per type. If your endpoint fails to respond with a 2xx status code (e.g. 200 OK) ten times in a row, the subscription is automatically cancelled. Check for a 404 on your subscription to detect this:

GET /v3/WebhookSubscriptions/Invoice

A 404 means the subscription was auto-cancelled and needs to be re-created.