# Invoices API

> Create, list and send invoices with the Anytime Capital Payments API: line items, discount, tax and deposits, emailed in your branding and paid by card.

Source: https://anytime-capital.com/docs/invoices · Markdown: https://anytime-capital.com/docs/invoices.md · All docs: https://anytime-capital.com/docs/llms-full.txt

Invoices are emailed from Anytime Capital in your branding and paid at their `url`. Totals are calculated from the line items, an optional discount and tax. A customer can pay a deposit first, and automatic reminders go out around the due date unless you turn them off. Recurring invoices, bank transfer instructions and recording an offline payment are done in the dashboard.

| Topic | Detail |
| --- | --- |
| Endpoints | [`POST /api/v1/invoices`](#create-invoice), [`GET /api/v1/invoices`](#list-invoices), [`GET /api/v1/invoices/{id}`](#get-invoice), [`POST /api/v1/invoices/{id}/send`](#send-invoice) |
| Key permissions | `invoices:write`, `invoices:read` |
| Samples | curl; Node.js 18+ with the built-in `fetch` (in an ES module); the [Node SDK](https://anytime-capital.com/docs/node-sdk.md) (preview: not on npm yet); Python with `requests`. |
| Every request can also answer | 401 `UNAUTHORIZED`, 401 `API_KEY_EXPIRED`, 403 `NOT_ENROLLED`, 403 `PERMISSION_DENIED`, 403 `IP_NOT_ALLOWED`, 429 `RATE_LIMITED`, 500 `INTERNAL_ERROR`. See [Error codes](https://anytime-capital.com/docs/error-codes.md). |

## The invoice object

```json
{
  "object": "invoice",
  "livemode": true,
  "id": "inv_9QmR2vX7pLk3TzW8cN4b",
  "number": "INV-0042",
  "url": "https://anytime-capital.com/pay/invoice/inv_9QmR2vX7pLk3TzW8cN4b",
  "status": "open",
  "overdue": false,
  "customer": {
    "name": "Northwind LLC",
    "email": "ap@northwind.example"
  },
  "lineItems": [
    {
      "description": "Website redesign",
      "quantity": 1,
      "unitCents": 450000
    }
  ],
  "subtotalCents": 450000,
  "discountCents": 0,
  "taxBps": 825,
  "taxCents": 37125,
  "totalCents": 487125,
  "amountPaidCents": 0,
  "balanceCents": 487125,
  "depositCents": null,
  "currency": "usd",
  "dueDate": "2026-11-15",
  "notes": "Thank you for your business.",
  "paidVia": null,
  "paidAt": null,
  "paymentId": null,
  "sentAt": "2026-10-16T14:00:00.000Z",
  "voidedAt": null,
  "createdAt": "2026-10-16T14:00:00.000Z"
}
```

| Field | Type | Description |
| --- | --- | --- |
| `object` | string | The type of object. Always `invoice`. |
| `livemode` | boolean | False for test-mode data (an `ak_test_` key); true for live data. |
| `id` | string | `inv_` followed by 20 letters and digits. |
| `number` | string | INV-0001. |
| `url` | string (uri) | Where the customer views and pays it. |
| `status` | string | `open`, `partially_paid`, `paid`, `refunded`, `void`: `partially_paid` after a deposit or part payment, `refunded` when paid and then refunded in full. |
| `overdue` | boolean | Still owed and past its due date. |
| `customer` | object |  |
| `customer.name` | string | Name. |
| `customer.email` | string | Email. |
| `lineItems` | object[] |  |
| `lineItems[].description` | string | Line. |
| `lineItems[].quantity` | integer | Quantity. |
| `lineItems[].unitCents` | integer | Unit price. Integer cents. |
| `subtotalCents` | integer | Before discount and tax. Integer cents. |
| `discountCents` | integer | Discount. Integer cents. |
| `taxBps` | integer | Tax in basis points (825 is 8.25%). |
| `taxCents` | integer | Tax, applied after the discount. |
| `totalCents` | integer | Total. Integer cents. |
| `amountPaidCents` | integer | Paid so far. Integer cents. |
| `balanceCents` | integer | Still owed. Integer cents. |
| `depositCents` | integer or null | Deposit the customer may pay first. Integer cents. |
| `currency` | string | usd. |
| `dueDate` | string (date) | YYYY-MM-DD. |
| `notes` | string or null | Printed on the invoice. |
| `paidVia` | string or null | `card`, or how it was recorded as paid in the dashboard. |
| `paidAt` | string (date-time) or null | Paid in full. |
| `paymentId` | string or null | The card payment that paid it. |
| `sentAt` | string (date-time) or null | First emailed. |
| `voidedAt` | string (date-time) or null | Voided. |
| `createdAt` | string (date-time) | Created. |

## Endpoints

### Create an invoice

`POST /api/v1/invoices`

Permission: `invoices:write`. Supports `Idempotency-Key`.

Totals are calculated from the lines; emailed to the customer unless `send: false`. Totals are calculated from the lines (discount first, then tax) and run from $1.00 to $100,000.00. Recurring invoices are set up in the dashboard: sending `recurrence` answers `400 INVALID_INPUT`. If the email fails, the invoice is still created with `sentAt: null`; send it later.

**Headers**

| Header | Type | Required | Description |
| --- | --- | --- | --- |
| `Idempotency-Key` | string | no | Recommended. A unique key such as a UUID: a retry with the same key within 24 hours gets the first response back instead of acting twice. See [Idempotency](https://anytime-capital.com/docs/idempotency.md). |

**Body**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `customerName` | string | yes | Who the invoice is for. Up to 200 characters. |
| `customerEmail` | string (email) | yes | Where the invoice is emailed. Up to 255 characters. |
| `lineItems` | object[] | yes | 1–50 items. |
| `lineItems[].description` | string | yes | Up to 200 characters. |
| `lineItems[].quantity` | integer | yes | From 1 to 10,000. |
| `lineItems[].unitCents` | integer | yes | From 1 ($0.01) to 10,000,000 ($100,000.00). |
| `dueDate` | string (date) | yes | YYYY-MM-DD. |
| `discountCents` | integer | no | Taken off the subtotal before tax. |
| `taxBps` | integer | no | Tax in basis points: 825 is 8.25%. From 0 to 3,000. |
| `notes` | string | no | Printed on the invoice. Up to 2000 characters. |
| `deposit` | object | no | What the customer may pay first: `{"type": "amount", "amountCents": 50000}` or `{"type": "percent", "percent": 25}`. At least $1.00, and less than the total with at least $1.00 left to pay. |
| `remindersEnabled` | boolean | no | Automatic reminders around the due date. Default true. |
| `send` | boolean | no | Email the invoice now. Default true. |
| `saveCustomer` | boolean | no | Also add the customer to your customers (skipped when the email is already there). Default `false`. |

**Request (curl)**

```bash
curl -X POST https://anytime-capital.com/api/v1/invoices \
  -H "Authorization: Bearer $ANYTIME_API_KEY" \
  -H "Idempotency-Key: invoice-northwind-oct" \
  -H "Content-Type: application/json" \
  -d '{
    "customerName": "Northwind LLC",
    "customerEmail": "ap@northwind.example",
    "lineItems": [
      {
        "description": "Website redesign",
        "quantity": 1,
        "unitCents": 450000
      }
    ],
    "taxBps": 825,
    "dueDate": "2026-11-15"
  }'
```

**Request (Node.js)**

```javascript
const res = await fetch("https://anytime-capital.com/api/v1/invoices", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.ANYTIME_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": "invoice-northwind-oct",
  },
  body: JSON.stringify({
    customerName: "Northwind LLC",
    customerEmail: "ap@northwind.example",
    lineItems: [{ description: "Website redesign", quantity: 1, unitCents: 450000 }],
    taxBps: 825,
    dueDate: "2026-11-15",
  }),
});
const invoice = await res.json();
if (!res.ok) throw new Error(`${invoice.error.code}: ${invoice.error.message}`);
```

**Request (Node SDK)**

```typescript
import { AnytimeCapital } from "@anytimecapital/node";

const client = new AnytimeCapital({ apiKey: process.env.ANYTIME_API_KEY! });

const invoice = await client.invoices.create(
  {
    customerName: "Northwind LLC",
    customerEmail: "ap@northwind.example",
    lineItems: [{ description: "Website redesign", quantity: 1, unitCents: 450000 }],
    taxBps: 825,
    dueDate: "2026-11-15",
  },
  { idempotencyKey: "invoice-northwind-oct" },
);
```

**Request (Python)**

```python
import os

import requests

res = requests.post(
    "https://anytime-capital.com/api/v1/invoices",
    headers={
        "Authorization": f"Bearer {os.environ['ANYTIME_API_KEY']}",
        "Idempotency-Key": "invoice-northwind-oct",
    },
    json={
        "customerName": "Northwind LLC",
        "customerEmail": "ap@northwind.example",
        "lineItems": [
            {"description": "Website redesign", "quantity": 1, "unitCents": 450000},
        ],
        "taxBps": 825,
        "dueDate": "2026-11-15",
    },
    timeout=30,
)
invoice = res.json()
if not res.ok:
    raise RuntimeError(f"{invoice['error']['code']}: {invoice['error']['message']}")
```

**Response** (201)

```json
{
  "object": "invoice",
  "livemode": true,
  "id": "inv_9QmR2vX7pLk3TzW8cN4b",
  "number": "INV-0042",
  "url": "https://anytime-capital.com/pay/invoice/inv_9QmR2vX7pLk3TzW8cN4b",
  "status": "open",
  "overdue": false,
  "customer": {
    "name": "Northwind LLC",
    "email": "ap@northwind.example"
  },
  "lineItems": [
    {
      "description": "Website redesign",
      "quantity": 1,
      "unitCents": 450000
    }
  ],
  "subtotalCents": 450000,
  "discountCents": 0,
  "taxBps": 825,
  "taxCents": 37125,
  "totalCents": 487125,
  "amountPaidCents": 0,
  "balanceCents": 487125,
  "depositCents": null,
  "currency": "usd",
  "dueDate": "2026-11-15",
  "notes": "Thank you for your business.",
  "paidVia": null,
  "paidAt": null,
  "paymentId": null,
  "sentAt": "2026-10-16T14:00:00.000Z",
  "voidedAt": null,
  "createdAt": "2026-10-16T14:00:00.000Z"
}
```

**Errors**

| Status | Code | When |
| --- | --- | --- |
| 400 | `INVALID_JSON` | The body isn't valid JSON, or couldn't be read. An empty body reads as `{}`. Send `Content-Type: application/json` and a JSON object. |
| 400 | `INVALID_INPUT` | A field, filter, `limit` or `cursor` is missing or invalid. The message starts with the field's name, as in `amountCents: …`, or reads `Invalid cursor.` Fix the named field. Don't retry the same request unchanged. |
| 400 | `INVALID_IDEMPOTENCY_KEY` | The `Idempotency-Key` header is empty, longer than 255 characters, or has characters other than printable ASCII. Use a UUID. See [Idempotency](https://anytime-capital.com/docs/idempotency.md). |
| 422 | `IDEMPOTENCY_KEY_REUSED` | The `Idempotency-Key` was used in the last 24 hours with a different body or endpoint. Use a new key for a new request; reuse a key only to retry the same request. |

### List invoices

`GET /api/v1/invoices`

Permission: `invoices:read`.

Invoices with totals, what is paid and owed, and the pay page url.

**Query**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `status` | string | no | Only invoices with this status. One of `open`, `partially_paid`, `paid`, `refunded`, `void`. |
| `limit` | integer | no | How many objects per page, 1–100. Default 25. See [Pagination](https://anytime-capital.com/docs/pagination.md). |
| `cursor` | string | no | The previous page's `nextCursor`, for the page after it. |

**Request (curl)**

```bash
curl "https://anytime-capital.com/api/v1/invoices?status=open" \
  -H "Authorization: Bearer $ANYTIME_API_KEY"
```

**Request (Node.js)**

```javascript
const res = await fetch("https://anytime-capital.com/api/v1/invoices?status=open", {
  headers: { Authorization: `Bearer ${process.env.ANYTIME_API_KEY}` },
});
const page = await res.json();
if (!res.ok) throw new Error(`${page.error.code}: ${page.error.message}`);
```

**Request (Node SDK)**

```typescript
import { AnytimeCapital } from "@anytimecapital/node";

const client = new AnytimeCapital({ apiKey: process.env.ANYTIME_API_KEY! });

const page = await client.invoices.list({ status: "open" });
```

**Request (Python)**

```python
import os

import requests

res = requests.get(
    "https://anytime-capital.com/api/v1/invoices",
    headers={"Authorization": f"Bearer {os.environ['ANYTIME_API_KEY']}"},
    params={"status": "open"},
    timeout=30,
)
page = res.json()
if not res.ok:
    raise RuntimeError(f"{page['error']['code']}: {page['error']['message']}")
```

**Response** (200)

```json
{
  "object": "list",
  "data": [
    {
      "object": "invoice",
      "livemode": true,
      "id": "inv_9QmR2vX7pLk3TzW8cN4b",
      "number": "INV-0042",
      "url": "https://anytime-capital.com/pay/invoice/inv_9QmR2vX7pLk3TzW8cN4b",
      "status": "open",
      "overdue": false,
      "customer": {
        "name": "Northwind LLC",
        "email": "ap@northwind.example"
      },
      "lineItems": [
        {
          "description": "Website redesign",
          "quantity": 1,
          "unitCents": 450000
        }
      ],
      "subtotalCents": 450000,
      "discountCents": 0,
      "taxBps": 825,
      "taxCents": 37125,
      "totalCents": 487125,
      "amountPaidCents": 0,
      "balanceCents": 487125,
      "depositCents": null,
      "currency": "usd",
      "dueDate": "2026-11-15",
      "notes": "Thank you for your business.",
      "paidVia": null,
      "paidAt": null,
      "paymentId": null,
      "sentAt": "2026-10-16T14:00:00.000Z",
      "voidedAt": null,
      "createdAt": "2026-10-16T14:00:00.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}
```

**Errors**

| Status | Code | When |
| --- | --- | --- |
| 400 | `INVALID_INPUT` | A field, filter, `limit` or `cursor` is missing or invalid. The message starts with the field's name, as in `amountCents: …`, or reads `Invalid cursor.` Fix the named field. Don't retry the same request unchanged. |

### Get an invoice

`GET /api/v1/invoices/{id}`

Permission: `invoices:read`.

One invoice by id.

**Path**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | `inv_…` |

**Request (curl)**

```bash
curl https://anytime-capital.com/api/v1/invoices/inv_9QmR2vX7pLk3TzW8cN4b \
  -H "Authorization: Bearer $ANYTIME_API_KEY"
```

**Request (Node.js)**

```javascript
const res = await fetch("https://anytime-capital.com/api/v1/invoices/inv_9QmR2vX7pLk3TzW8cN4b", {
  headers: { Authorization: `Bearer ${process.env.ANYTIME_API_KEY}` },
});
const invoice = await res.json();
if (!res.ok) throw new Error(`${invoice.error.code}: ${invoice.error.message}`);
```

**Request (Node SDK)**

```typescript
import { AnytimeCapital } from "@anytimecapital/node";

const client = new AnytimeCapital({ apiKey: process.env.ANYTIME_API_KEY! });

const invoice = await client.invoices.retrieve("inv_9QmR2vX7pLk3TzW8cN4b");
```

**Request (Python)**

```python
import os

import requests

res = requests.get(
    "https://anytime-capital.com/api/v1/invoices/inv_9QmR2vX7pLk3TzW8cN4b",
    headers={"Authorization": f"Bearer {os.environ['ANYTIME_API_KEY']}"},
    timeout=30,
)
invoice = res.json()
if not res.ok:
    raise RuntimeError(f"{invoice['error']['code']}: {invoice['error']['message']}")
```

**Response** (200)

```json
{
  "object": "invoice",
  "livemode": true,
  "id": "inv_9QmR2vX7pLk3TzW8cN4b",
  "number": "INV-0042",
  "url": "https://anytime-capital.com/pay/invoice/inv_9QmR2vX7pLk3TzW8cN4b",
  "status": "open",
  "overdue": false,
  "customer": {
    "name": "Northwind LLC",
    "email": "ap@northwind.example"
  },
  "lineItems": [
    {
      "description": "Website redesign",
      "quantity": 1,
      "unitCents": 450000
    }
  ],
  "subtotalCents": 450000,
  "discountCents": 0,
  "taxBps": 825,
  "taxCents": 37125,
  "totalCents": 487125,
  "amountPaidCents": 0,
  "balanceCents": 487125,
  "depositCents": null,
  "currency": "usd",
  "dueDate": "2026-11-15",
  "notes": "Thank you for your business.",
  "paidVia": null,
  "paidAt": null,
  "paymentId": null,
  "sentAt": "2026-10-16T14:00:00.000Z",
  "voidedAt": null,
  "createdAt": "2026-10-16T14:00:00.000Z"
}
```

**Errors**

| Status | Code | When |
| --- | --- | --- |
| 404 | `NOT_FOUND` | No such object on your business (an id from another business reads the same), or no such endpoint under `/api/v1`, which still answers in JSON. Check the id and the path. Ids are case-sensitive. |

### Send an invoice

`POST /api/v1/invoices/{id}/send`

Permission: `invoices:write`. Supports `Idempotency-Key`.

Email it (a reminder after the first time), at most once a day, while something is owed. The invoice itself the first time (sends `invoice.sent`), a reminder after that.

**Path**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | `inv_…` |

**Headers**

| Header | Type | Required | Description |
| --- | --- | --- | --- |
| `Idempotency-Key` | string | no | Recommended. A unique key such as a UUID: a retry with the same key within 24 hours gets the first response back instead of acting twice. See [Idempotency](https://anytime-capital.com/docs/idempotency.md). |

**Request (curl)**

```bash
curl -X POST https://anytime-capital.com/api/v1/invoices/inv_9QmR2vX7pLk3TzW8cN4b/send \
  -H "Authorization: Bearer $ANYTIME_API_KEY" \
  -H "Idempotency-Key: send-inv-0042"
```

**Request (Node.js)**

```javascript
const res = await fetch("https://anytime-capital.com/api/v1/invoices/inv_9QmR2vX7pLk3TzW8cN4b/send", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.ANYTIME_API_KEY}`,
    "Idempotency-Key": "send-inv-0042",
  },
});
const invoice = await res.json();
if (!res.ok) throw new Error(`${invoice.error.code}: ${invoice.error.message}`);
```

**Request (Node SDK)**

```typescript
import { AnytimeCapital } from "@anytimecapital/node";

const client = new AnytimeCapital({ apiKey: process.env.ANYTIME_API_KEY! });

const invoice = await client.invoices.send(
  "inv_9QmR2vX7pLk3TzW8cN4b",
  { idempotencyKey: "send-inv-0042" },
);
```

**Request (Python)**

```python
import os

import requests

res = requests.post(
    "https://anytime-capital.com/api/v1/invoices/inv_9QmR2vX7pLk3TzW8cN4b/send",
    headers={
        "Authorization": f"Bearer {os.environ['ANYTIME_API_KEY']}",
        "Idempotency-Key": "send-inv-0042",
    },
    timeout=30,
)
invoice = res.json()
if not res.ok:
    raise RuntimeError(f"{invoice['error']['code']}: {invoice['error']['message']}")
```

**Response** (200)

```json
{
  "object": "invoice",
  "livemode": true,
  "id": "inv_9QmR2vX7pLk3TzW8cN4b",
  "number": "INV-0042",
  "url": "https://anytime-capital.com/pay/invoice/inv_9QmR2vX7pLk3TzW8cN4b",
  "status": "open",
  "overdue": false,
  "customer": {
    "name": "Northwind LLC",
    "email": "ap@northwind.example"
  },
  "lineItems": [
    {
      "description": "Website redesign",
      "quantity": 1,
      "unitCents": 450000
    }
  ],
  "subtotalCents": 450000,
  "discountCents": 0,
  "taxBps": 825,
  "taxCents": 37125,
  "totalCents": 487125,
  "amountPaidCents": 0,
  "balanceCents": 487125,
  "depositCents": null,
  "currency": "usd",
  "dueDate": "2026-11-15",
  "notes": "Thank you for your business.",
  "paidVia": null,
  "paidAt": null,
  "paymentId": null,
  "sentAt": "2026-10-16T14:00:00.000Z",
  "voidedAt": null,
  "createdAt": "2026-10-16T14:00:00.000Z"
}
```

**Errors**

| Status | Code | When |
| --- | --- | --- |
| 400 | `INVALID_IDEMPOTENCY_KEY` | The `Idempotency-Key` header is empty, longer than 255 characters, or has characters other than printable ASCII. Use a UUID. See [Idempotency](https://anytime-capital.com/docs/idempotency.md). |
| 404 | `NOT_FOUND` | No such object on your business (an id from another business reads the same), or no such endpoint under `/api/v1`, which still answers in JSON. Check the id and the path. Ids are case-sensitive. |
| 409 | `INVOICE_CLOSED` | The invoice is already paid, refunded or void, so there's nothing to send. Read the invoice's `status`; nothing to do. |
| 422 | `IDEMPOTENCY_KEY_REUSED` | The `Idempotency-Key` was used in the last 24 hours with a different body or endpoint. Use a new key for a new request; reuse a key only to retry the same request. |
| 429 | `TOO_SOON` | The invoice was already emailed in the last 24 hours. Wait the seconds in `Retry-After`. |
| 502 | `EMAIL_FAILED` | We couldn't send the email. Retry. Nothing was marked as sent. |

## Knowing when an invoice is paid

Subscribe to the `invoice.paid` webhook, which carries the invoice object once it is paid in full. Each card payment on an invoice, deposits included, also sends `payment.succeeded` with `source.type` `invoice` and `source.id` the invoice id. `invoice.created`, `invoice.sent`, `invoice.overdue` and `invoice.voided` follow the rest of its life. See [Webhooks](https://anytime-capital.com/docs/webhooks.md).

- [Bank transfers on invoices](https://anytime-capital.com/docs/invoice-bank-transfers.md): Show your ACH and wire details on invoices.
- [Customers](https://anytime-capital.com/docs/customers.md): Save the people you invoice.
- [Prompt library](https://anytime-capital.com/docs/ai-prompt-library.md): Have an AI assistant draft and send invoices for you.

---

Anytime Capital is a fintech company, not a bank. Payment processing, banking and payout services are provided by Anytime Capital's licensed partners. Rates shown are for US card payments; international cards, currency conversion and disputes may carry additional fees shown in your dashboard before you accept a payment.
