> ## Documentation Index
> Fetch the complete documentation index at: https://developer.meow.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create and track vendor bills via the API

> Draft bills payable to your vendors, attach invoices, track them through payment, and schedule recurring bills — all as drafts for dashboard approval.

Bill Pay is Meow's accounts-payable surface. You record what you owe a vendor as
a **bill** — its line items, due date, and how it should be paid — and the API
saves it as a draft. Someone on your team approves the bill in the Meow
dashboard, and only then does it get paid.

<Note>
  Every bill you touch through the API stays a **draft pending approval**. Creating
  a bill, editing it, or attaching documents never moves money on its own — payment
  happens only after a human approves the bill in the dashboard.
</Note>

## You'll need

* An API key with `billpay:read` to list and retrieve bills, and `billpay:write`
  to create, update, delete, and attach documents to them.
* A **contact** for each vendor, created with banking (`ACH`, wire, or check)
  `counterparty` details. A bill is payable to a contact that must already exist;
  create one first with [`POST /contacts`](/api-reference/core-api/create-contact).
  (The [Contacts guide](/guides/contacts) covers USDC crypto contacts, which Bill
  Pay doesn't use.)
* If your key can access more than one entity, pass the `x-entity-id` header to
  choose which entity the bill belongs to.

## 1. Create a bill

A bill is payable to a `contact_id` and is made of one or more line items. Each
line item is either an `EXPENSE` (a flat `amount`) or an `ITEM` (a unit `amount`
times a `quantity`); the bill total is calculated from the lines. Send a unique
`idempotency_key` so a retried request never creates a duplicate.

```bash theme={null}
curl -X POST https://api.meow.com/v1/bills \
  -H "x-api-key: $MEOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "idempotency_key": "bill-2026-06-30-catnip-001",
    "contact_id": "b3f1c2d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
    "invoice_number": "CATNIP-4471",
    "bill_date": "2026-06-30",
    "bill_due_date": "2026-07-30",
    "description": "June cloud hosting",
    "memo": "Approved by ops",
    "payment_type": "ACH",
    "currency": "USD",
    "line_items": [
      { "type": "EXPENSE", "amount": 1200.00, "description": "Cloud hosting - June" },
      { "type": "ITEM", "amount": 15.00, "quantity": 20, "description": "Extra storage (GB)" }
    ]
  }'
```

```json Response theme={null}
{
  "id": "9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d",
  "status": "DRAFT",
  "vendor_name": "Catnip Cloud",
  "invoice_number": "CATNIP-4471",
  "description": "June cloud hosting",
  "bill_date": "2026-06-30",
  "bill_due_date": "2026-07-30",
  "bill_total": "1500.00",
  "currency": "USD",
  "payment_type": "ACH",
  "contact_id": "b3f1c2d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
  "created_at": "2026-06-30T10:30:00.000Z",
  "line_items": [
    { "id": "1a2b3c4d-...", "type": "EXPENSE", "amount": "1200.00", "quantity": null, "description": "Cloud hosting - June", "amount_total": "1200.00" },
    { "id": "2b3c4d5e-...", "type": "ITEM", "amount": "15.00", "quantity": "20", "description": "Extra storage (GB)", "amount_total": "300.00" }
  ]
}
```

Save `id` — it's the `{bill_id}` for attaching documents, tracking, updating, or
canceling the bill below.

| Field                         | Description                                                                                                                       |
| ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `idempotency_key`             | **Required.** A key you generate so retrying never creates a duplicate bill; reusing a key is rejected.                           |
| `contact_id`                  | The vendor this bill is payable to. The vendor must already exist as a contact.                                                   |
| `payment_type`                | How the bill is paid once approved: `ACH` (default), `WIRE`, or `CHECK`.                                                          |
| `account_id`                  | The account to pay from, in the id format returned by `GET /accounts`. Defaults to your configured bill pay account.              |
| `currency`                    | The bill's currency (ISO 4217). Defaults to `USD`.                                                                                |
| `bill_date` / `bill_due_date` | The date on the bill and the date it's due (`YYYY-MM-DD`).                                                                        |
| `line_items`                  | The lines that make up the bill. Use `type: "ITEM"` with a `quantity` for itemized lines, or `type: "EXPENSE"` for a flat amount. |
| `rrule`                       | An iCalendar RRULE to make this a [recurring bill](#5-schedule-recurring-bills).                                                  |

<Note>
  Bill Pay accepts the `ACH`, `WIRE`, and `CHECK` payment types. Crypto and
  international payments are arranged in the Meow dashboard, not through this API.
</Note>

## 2. Attach supporting documents

Attaching the vendor's invoice or a receipt is a three-step flow: ask for upload
URLs, upload each file, then confirm the attachment. You can attach up to five
documents per confirm call.

<Steps>
  <Step title="Request upload URLs">
    Describe each file — its `filename`, `content_type`, and `size` in bytes — and
    get a short-lived upload target for each.

    ```bash theme={null}
    curl -X POST https://api.meow.com/v1/bills/$BILL_ID/documents/presigned-urls \
      -H "x-api-key: $MEOW_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "files": [ { "filename": "catnip-cloud-4471.pdf", "content_type": "application/pdf", "size": 48213 } ] }'
    ```

    ```json Response theme={null}
    {
      "uploads": [
        {
          "url": "https://uploads.meow.com/billpay",
          "fields": { "key": "billpay/9a8b.../catnip-cloud-4471.pdf", "policy": "eyJ...", "x-amz-signature": "abc123" },
          "object_key": "billpay/9a8b.../catnip-cloud-4471.pdf",
          "filename": "catnip-cloud-4471.pdf",
          "max_bytes": 10485760
        }
      ]
    }
    ```
  </Step>

  <Step title="Upload each file">
    POST the file to its `url` as a multipart form, including every entry from
    `fields` and putting the file last. Keep it under `max_bytes` and upload before
    the URL expires.

    ```bash theme={null}
    curl -X POST "https://uploads.meow.com/billpay" \
      -F "key=billpay/9a8b.../catnip-cloud-4471.pdf" \
      -F "policy=eyJ..." \
      -F "x-amz-signature=abc123" \
      -F "file=@catnip-cloud-4471.pdf"
    ```
  </Step>

  <Step title="Confirm the attachment">
    Confirm each upload with the `object_key` and `filename` you received. The
    response is the updated bill, now carrying the document under
    `additional_documents` with a short-lived download URL.

    ```bash theme={null}
    curl -X POST https://api.meow.com/v1/bills/$BILL_ID/documents \
      -H "x-api-key: $MEOW_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "documents": [ { "object_key": "billpay/9a8b.../catnip-cloud-4471.pdf", "filename": "catnip-cloud-4471.pdf" } ] }'
    ```

    ```json Response theme={null}
    {
      "id": "9a8b7c6d-...",
      "status": "DRAFT",
      "vendor_name": "Catnip Cloud",
      "additional_documents": [
        { "id": "d1e2f3a4-...", "name": "catnip-cloud-4471.pdf", "url": "https://download.meow.com/...", "created_at": "2026-06-30T10:32:00.000Z" }
      ]
    }
    ```
  </Step>
</Steps>

## 3. Track bills

`GET /bills` returns a paginated list and filters by status, vendor, amount, and
date ranges, plus full-text `search`.

```bash theme={null}
curl "https://api.meow.com/v1/bills?statuses=DRAFT&statuses=PENDING&due_date_to=2026-07-31&limit=25" \
  -H "x-api-key: $MEOW_API_KEY"
```

| Parameter                         | Description                                                             |
| --------------------------------- | ----------------------------------------------------------------------- |
| `statuses`                        | Filter by one or more bill statuses; repeat the parameter for several.  |
| `contact_id`                      | Filter to bills payable to specific vendors.                            |
| `amount_min` / `amount_max`       | Filter by bill total.                                                   |
| `bill_date_from` / `bill_date_to` | Filter by the date on the bill.                                         |
| `due_date_from` / `due_date_to`   | Filter by due date.                                                     |
| `search`                          | Full-text search across bill fields.                                    |
| `sort_by` / `sort_descending`     | Order the results.                                                      |
| `limit` / `offset`                | Page through results using the `nextOffset` from the previous response. |

Retrieve one bill with `GET /bills/{bill_id}`:

```bash theme={null}
curl https://api.meow.com/v1/bills/$BILL_ID -H "x-api-key: $MEOW_API_KEY"
```

A bill moves through these statuses as it's approved and paid:

| `status`            | Meaning                                                |
| ------------------- | ------------------------------------------------------ |
| `DRAFT`             | Created and waiting for approval in the dashboard.     |
| `PENDING`           | Submitted and moving through approval.                 |
| `APPROVED`          | Approved and ready to be scheduled.                    |
| `SCHEDULED`         | Approved with a payment scheduled; see `scheduled_at`. |
| `PAYMENT_INITIATED` | Payment is on its way.                                 |
| `PAYMENT_DELIVERED` | Payment delivered; see `paid_at`.                      |
| `PAYMENT_FAILED`    | The payment attempt failed.                            |
| `REJECTED`          | An approver declined the bill.                         |
| `CANCELED`          | The bill was canceled before payment.                  |

## 4. Update or cancel a draft

While a bill is still a `DRAFT`, you can edit it. `PATCH` uses partial semantics —
only the fields you send change, and sending `line_items` replaces the existing
lines (send an empty list to clear them).

```bash theme={null}
curl -X PATCH https://api.meow.com/v1/bills/$BILL_ID \
  -H "x-api-key: $MEOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "memo": "Revised per updated invoice", "payment_type": "WIRE" }'
```

Cancel a bill that hasn't been approved yet by deleting it:

```bash theme={null}
curl -X DELETE https://api.meow.com/v1/bills/$BILL_ID -H "x-api-key: $MEOW_API_KEY"
```

<Note>
  Once a bill is approved (or paid), it can no longer be edited or deleted through
  the API.
</Note>

## 5. Schedule recurring bills

To generate a bill on a repeating schedule, include an iCalendar `rrule` (with a
`DTSTART`) when you create it. Each occurrence is generated as its own draft for
approval — recurring bills are never paid automatically.

```bash theme={null}
curl -X POST https://api.meow.com/v1/bills \
  -H "x-api-key: $MEOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "idempotency_key": "bill-rent-recurring-001",
    "contact_id": "c4d5e6f7-8a9b-0c1d-2e3f-4a5b6c7d8e9f",
    "description": "Monthly office rent",
    "payment_type": "ACH",
    "rrule": "DTSTART:20260701T000000Z\nRRULE:FREQ=MONTHLY;BYMONTHDAY=1",
    "line_items": [ { "type": "EXPENSE", "amount": 4500.00, "description": "Office rent — Whisker Works" } ]
  }'
```

<Tip>
  Building the schedule string by hand is fiddly. A tool like the
  [RRULE generator](https://icalendar.org/rrule-tool.html) will assemble the
  `DTSTART`/`RRULE` for you.
</Tip>

## End to end

```bash theme={null}
# 1. Create the bill and capture its id.
BILL_ID=$(curl -s -X POST https://api.meow.com/v1/bills \
  -H "x-api-key: $MEOW_API_KEY" -H "Content-Type: application/json" \
  -d '{"idempotency_key":"bill-'"$(date +%Y%m%d)"'-catnip-001","contact_id":"b3f1c2d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d","invoice_number":"CATNIP-4471","payment_type":"ACH","line_items":[{"type":"EXPENSE","amount":1200.00,"description":"Cloud hosting - June"}]}' \
  | jq -r .id)

# 2. Request an upload URL for the vendor's invoice PDF.
UPLOAD=$(curl -s -X POST https://api.meow.com/v1/bills/$BILL_ID/documents/presigned-urls \
  -H "x-api-key: $MEOW_API_KEY" -H "Content-Type: application/json" \
  -d '{"files":[{"filename":"catnip-cloud-4471.pdf","content_type":"application/pdf","size":48213}]}')
OBJECT_KEY=$(echo "$UPLOAD" | jq -r '.uploads[0].object_key')

# 3. Upload the file to .uploads[0].url with .uploads[0].fields, then confirm.
curl -s -X POST https://api.meow.com/v1/bills/$BILL_ID/documents \
  -H "x-api-key: $MEOW_API_KEY" -H "Content-Type: application/json" \
  -d "{\"documents\":[{\"object_key\":\"$OBJECT_KEY\",\"filename\":\"catnip-cloud-4471.pdf\"}]}" | jq '.status'
```

The bill now sits as a `DRAFT` with its invoice attached, waiting for an approver
in the dashboard.

## See also

<CardGroup cols={2}>
  <Card title="Create a contact" icon="address-card" href="/api-reference/core-api/create-contact">
    Register a vendor with banking `counterparty` details to pay bills to.
  </Card>

  <Card title="Create a bill" icon="file-invoice-dollar" href="/api-reference/billpay/create-bill">
    Full request contract for `POST /bills`.
  </Card>

  <Card title="List bills" icon="list" href="/api-reference/billpay/list-bills">
    Every filter, sort, and pagination option.
  </Card>

  <Card title="Update a bill" icon="pen" href="/api-reference/billpay/update-bill">
    Edit a draft with `PATCH` partial semantics.
  </Card>

  <Card title="Confirm bill documents" icon="paperclip" href="/api-reference/billpay/confirm-bill-documents">
    Attach uploaded invoices and receipts.
  </Card>
</CardGroup>
