---
title: "Invoices"
description: "Invoice generation, payment tracking, and billing history via Stripe integration"
canonical: "https://docs.ainative.studio/docs/billing/invoices"
last-updated: "2026-10-22T17:48:46.689Z"
---

# Invoices

Source: https://docs.ainative.studio/docs/billing/invoices

> Invoice generation, payment tracking, and billing history via Stripe integration

## Invoices

AINative generates invoices for every billing period using its own invoicing system (not Stripe Invoicing). Invoices include line items, PDF download URLs, and payment history.

**Base URL:** `https://api.ainative.studio`

---

## Invoice statuses

| Status | Description |
|--------|-------------|
| `draft` | Created but not yet sent. Can still be edited. |
| `sent` | Finalized and delivered to the customer. Immutable. |
| `paid` | Payment received. |
| `overdue` | Due date passed without payment. |
| `void` | Cancelled. Cannot be paid or un-voided. |

---

## List Invoices (Billing Portal)

<span className="badge badge--get">GET</span> `/api/v1/billing/invoices`

Paginated list of invoices for the authenticated user from the billing portal. Amounts are returned in dollars (not cents).

**Auth required.**

**Query parameters**

| Param | Type | Default | Description |
|-------|------|---------|-------------|
| `page` | integer | 1 | Page number |
| `page_size` | integer | 20 | Invoices per page |
| `status` | string | — | Filter by status (omit for all) |

```bash
curl "https://api.ainative.studio/api/v1/billing/invoices?page=1&page_size=10" \
  -H "Authorization: Bearer <your_api_key>"
```

```python
import httpx

resp = httpx.get(
    "https://api.ainative.studio/api/v1/billing/invoices",
    headers={"Authorization": "Bearer <your_api_key>"},
    params={"page": 1, "page_size": 10},
)
data = resp.json()["data"]
for invoice in data["items"]:
    print(invoice["number"], invoice["status"], invoice["amount"])
```

**Response**

```json
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "invoice-uuid",
        "number": "INV-2026-0042",
        "amount": 49.00,
        "amount_paid": 49.00,
        "currency": "USD",
        "status": "paid",
        "period_start": "2026-04-01T00:00:00Z",
        "period_end": "2026-04-30T23:59:59Z",
        "due_date": "2026-05-05T00:00:00Z",
        "paid_at": "2026-04-25T10:30:00Z",
        "invoice_pdf": "https://storage.example.com/invoices/INV-2026-0042.pdf",
        "line_items": [
          {
            "description": "Pro Plan - Monthly Subscription",
            "amount": 4900,
            "quantity": 1
          }
        ],
        "created_at": "2026-04-01T00:00:00Z"
      }
    ],
    "total": 3,
    "page": 1,
    "page_size": 10,
    "has_more": false
  }
}
```

---

## Get Invoice

<span className="badge badge--get">GET</span> `/api/v1/billing/invoices/{invoice_id}`

Retrieve a single invoice by its Stripe invoice ID.

**Auth required.**

```bash
curl https://api.ainative.studio/api/v1/billing/invoices/in_xxx \
  -H "Authorization: Bearer <your_api_key>"
```

**Response**

```json
{
  "success": true,
  "data": {
    "id": "in_xxx",
    "amount": 49.00,
    "currency": "USD",
    "status": "paid",
    "created_at": "2026-04-01T00:00:00Z",
    "period_start": "2026-04-01T00:00:00Z",
    "period_end": "2026-04-30T23:59:59Z",
    "items": [...]
  }
}
```

---

## Download Invoice PDF

<span className="badge badge--get">GET</span> `/api/v1/billing/invoices/{invoice_id}/download`

Returns the PDF download URL for an invoice. **No authentication required** — the invoice UUID acts as an unguessable access token, allowing PDF links in emails to work without login.

Void and cancelled invoices return `404`.

```bash
curl https://api.ainative.studio/api/v1/billing/invoices/invoice-uuid/download
```

**Response**

```json
{
  "success": true,
  "data": {
    "invoice_id": "invoice-uuid",
    "invoice_number": "INV-2026-0042",
    "pdf_url": "https://storage.example.com/invoices/INV-2026-0042.pdf",
    "amount": 49.00,
    "currency": "USD",
    "status": "paid"
  }
}
```

---

## Email Invoice

<span className="badge badge--post">POST</span> `/api/v1/billing/invoices/{invoice_id}/email`

Send an invoice email to the authenticated user. Duplicate sends are prevented by checking `invoice_email_log`. Use `resend-email` with `force_send=true` to override.

**Auth required.**

```bash
curl -X POST https://api.ainative.studio/api/v1/billing/invoices/in_xxx/email \
  -H "Authorization: Bearer <your_api_key>"
```

---

## Resend Invoice Email

<span className="badge badge--post">POST</span> `/api/v1/billing/invoices/{invoice_id}/resend-email`

Resend an invoice email with duplicate detection. The email type (`due_invoice` or `paid_invoice`) is determined automatically from the invoice status.

**Auth required.**

**Query parameters**

| Param | Type | Default | Description |
|-------|------|---------|-------------|
| `force_send` | boolean | false | Send even if already sent previously |

```bash
## Force resend even if email was already sent
curl -X POST \
  "https://api.ainative.studio/api/v1/billing/invoices/in_xxx/resend-email?force_send=true" \
  -H "Authorization: Bearer <your_api_key>"
```

**Response (success)**

```json
{
  "success": true,
  "message": "Invoice email sent to user@example.com",
  "invoice_id": "in_xxx",
  "invoice_number": "INV-2026-0042",
  "email_type": "paid_invoice",
  "recipient": "user@example.com",
  "log_id": "log-uuid",
  "forced": false
}
```

**Response (already sent, force_send not set)**

```json
{
  "success": false,
  "message": "Invoice email already sent. Use force_send=true to resend.",
  "already_sent": true,
  "previous_sends": [
    {
      "type": "paid_invoice",
      "status": "sent",
      "sent_at": "2026-04-25T11:00:00Z"
    }
  ]
}
```

---

## Bulk Download Invoices

<span className="badge badge--post">POST</span> `/api/v1/billing/invoices/download-bulk`

Get PDF download URLs for up to 50 invoices in a single request.

**Auth required.**

**Request body**

| Field | Type | Description |
|-------|------|-------------|
| `invoice_ids` | string[] | Stripe invoice IDs to download (max 50) |

```bash
curl -X POST https://api.ainative.studio/api/v1/billing/invoices/download-bulk \
  -H "Authorization: Bearer <your_api_key>" \
  -H "Content-Type: application/json" \
  -d '{"invoice_ids": ["in_aaa", "in_bbb", "in_ccc"]}'
```

**Response**

```json
{
  "success": true,
  "data": {
    "invoices": [
      {
        "invoice_id": "in_aaa",
        "invoice_number": "INV-2026-0040",
        "pdf_url": "https://storage.example.com/invoices/INV-2026-0040.pdf",
        "amount": 49.00,
        "currency": "USD",
        "created_at": "2026-03-01T00:00:00Z"
      }
    ],
    "total_requested": 3,
    "total_available": 1,
    "failed": [
      {"invoice_id": "in_bbb", "reason": "PDF not available"},
      {"invoice_id": "in_ccc", "reason": "Invoice not found"}
    ]
  }
}
```

---

## Billing Information

<span className="badge badge--get">GET</span> `/api/v1/billing`

Returns a summary of the user's billing info: primary payment method, next billing date, and amount due. Card numbers are masked — only the last 4 digits are shown.

**Auth required.**

```bash
curl https://api.ainative.studio/api/v1/billing \
  -H "Authorization: Bearer <your_api_key>"
```

**Response (with active subscription)**

```json
{
  "success": true,
  "data": {
    "payment_method": {
      "type": "card",
      "last4": "4242",
      "exp_month": 12,
      "exp_year": 2027,
      "brand": "visa"
    },
    "next_billing_date": "2026-05-01T00:00:00Z",
    "amount_due": 49.00,
    "currency": "USD",
    "billing_cycle": "monthly"
  }
}
```

**Response (no active subscription)**

```json
{
  "success": true,
  "data": {
    "payment_method": null,
    "next_billing_date": null,
    "amount_due": 0.00,
    "currency": "USD",
    "billing_cycle": null
  }
}
```

---

## Payment Methods

### List Payment Methods

<span className="badge badge--get">GET</span> `/api/v1/billing/payment-methods`

Returns all Stripe payment methods attached to the user's customer account.

**Auth required.**

```bash
curl https://api.ainative.studio/api/v1/billing/payment-methods \
  -H "Authorization: Bearer <your_api_key>"
```

**Response**

```json
{
  "success": true,
  "data": {
    "payment_methods": [
      {
        "id": "pm_xxx",
        "type": "card",
        "card": {
          "brand": "visa",
          "last4": "4242",
          "exp_month": 12,
          "exp_year": 2027
        }
      }
    ]
  }
}
```

---

### Add Payment Method

<span className="badge badge--post">POST</span> `/api/v1/billing/payment-methods`

Attach a Stripe payment method to the user's customer account. The `payment_method_id` must be created by Stripe Elements on the frontend before calling this endpoint.

**Auth required.**

**Request body**

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `payment_method_id` | string | Yes | Stripe payment method ID (e.g. `"pm_xxx"`) |

```bash
curl -X POST https://api.ainative.studio/api/v1/billing/payment-methods \
  -H "Authorization: Bearer <your_api_key>" \
  -H "Content-Type: application/json" \
  -d '{"payment_method_id": "pm_xxx"}'
```

**Response** `201 Created`

```json
{
  "success": true,
  "data": {
    "id": "pm_xxx",
    "type": "card",
    "card": {
      "brand": "visa",
      "last4": "4242",
      "exp_month": 12,
      "exp_year": 2027
    }
  },
  "message": "Payment method added successfully"
}
```

Returns `404` if the user has no Stripe customer (must subscribe to a plan first).

---

## Admin: Invoice Management

The following endpoints are restricted to users with `ADMIN` or `SUPERUSER` roles.

### Create Invoice

<span className="badge badge--post">POST</span> `/api/v1/invoices`

Create a new draft invoice with line items. Invoice number is auto-generated in `INV-YYYY-NNNN` format.

**Admin only.**

### List All Invoices

<span className="badge badge--get">GET</span> `/api/v1/invoices`

Paginated list of all invoices with optional filters.

**Admin only.**

| Param | Description |
|-------|-------------|
| `status` | Filter by status |
| `user_id` | Filter by customer user ID |
| `skip` / `limit` | Pagination |

### Get Invoice by ID

<span className="badge badge--get">GET</span> `/api/v1/invoices/{invoice_id}`

Users can view their own invoices. Admins can view any invoice.

### Update Draft Invoice

<span className="badge badge--patch">PATCH</span> `/api/v1/invoices/{invoice_id}`

Update line items, due date, billing period, or metadata on a draft invoice. Finalized invoices are immutable.

**Admin only.**

### Finalize Invoice

<span className="badge badge--post">POST</span> `/api/v1/invoices/{invoice_id}/finalize`

Transitions invoice from `draft` to `sent`, generates a PDF asynchronously, and sends an email notification.

**Admin only.**

### Void Invoice

<span className="badge badge--post">POST</span> `/api/v1/invoices/{invoice_id}/void`

Cancel an invoice. Void is irreversible.

**Admin only.**

### Mark Invoice Paid

<span className="badge badge--post">POST</span> `/api/v1/invoices/{invoice_id}/mark-paid`

Manually mark an invoice as paid for non-Stripe payments (wire transfer, ACH, etc.).

**Admin only.**

**Request body**

| Field | Type | Description |
|-------|------|-------------|
| `payment_method` | string | e.g. `"wire_transfer"`, `"ach"`, `"check"` |
| `payment_reference` | string | Reference number or note |
| `paid_at` | string | ISO-8601 timestamp (optional, defaults to now) |

### Get Invoice PDF

<span className="badge badge--get">GET</span> `/api/v1/invoices/{invoice_id}/pdf`

Returns the PDF URL or generates the PDF on-demand if not yet created.

### Create Payment Intent for Invoice

<span className="badge badge--post">POST</span> `/api/v1/invoices/{invoice_id}/payment-intent`

Creates a Stripe Payment Intent for the invoice amount. Returns a `client_secret` for use with Stripe.js.
