# Retrieving Invoices and Recording Payments 

** List, sort, and retrieve invoices using the Karbon API, then record manual payments against them by specifying a payment method, date, and amount—with support for reversals, deletions, and webhook subscriptions for real-time invoice change notifications. **
---

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](/guides/authentication/) if you don't have these set up yet.

## Listing Invoices

```http
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`:

```http
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

```http
# 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:

```http
GET /v3/Invoices?$top=50
```

Response:

```json
{
  "@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](./06-pagination.md) for details.

## Retrieving a Specific Invoice

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

```http
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.

It also includes `InvoiceMagicLink`, a link you can send to the client to pay the invoice through Karbon Payments, and `InvoicePdfUrl`, a link to the rendered invoice PDF you can preview, download, or forward to the client. Both are only returned once the invoice has been posted or sent — otherwise they're `null` — and both are only available on the single-invoice response, not on the `/v3/Invoices` list.

The token in `InvoicePdfUrl` is valid for 15 minutes, so fetch the invoice again for a fresh link instead of storing it.

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 URL       | What comes back                                                                                                                                                                            |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `?$expand=LineItems` | The invoice line items — the rows that appear on the invoice itself.                                                                                                                       |
| `?$expand=Payments`  | Any payments already recorded against the invoice.                                                                                                                                         |
| `?$expand=Data`      | The 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:

```http
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`](./05-rate-limits.md#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`:

```json
"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`:

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

## Listing Existing Payments

To see payments already recorded:

```http
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:

```http
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:**

| Field           | Description                                                                           |
| --------------- | ------------------------------------------------------------------------------------- |
| `InvoiceKey`    | The key of the invoice being paid                                                     |
| `PaymentMethod` | One of: `Check`, `Cash`, `EFT`, `Credit Card`, `BankTransfer`, `DirectDebit`, `Other` |
| `PaymentDate`   | ISO 8601 date (e.g., `2026-03-31`)                                                    |
| `TotalAmount`   | The 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:

```http
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:

```http
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:

```http
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:

```json
{
  "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](./07-webhooks.md) 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:

```http
GET /v3/WebhookSubscriptions/Invoice
```

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