> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.tabs.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.tabs.com/_mcp/server.

# Generate invoices

This guide walks through generating flat-rate invoices using the Tabs API.
Flat-rate billing charges a fixed amount per invoice—either as a one-off charge or on a recurring schedule.

## Step 1: Create the customer

A Customer in Tabs represents the business you're billing. At minimum, you need a business name. If the customer already exists in Tabs (synced from your CRM or ERP), skip to Step 3 and use their existing `id`.

```bash
curl -X POST https://integrators.prod.api.tabsplatform.com/v3/customers \
  -H "Authorization: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Corp",
    "primaryBillingContactEmail": "billing@acme.com",
    "billingAddress": {
      "line1": "123 Main Street",
      "city": "San Francisco",
      "state": "CA",
      "postalCode": "94105",
      "country": "US"
    }
  }'
```

Tabs returns the created Customer object. Save the `id`—you'll use it throughout this guide as `{customerId}`.

By default, Tabs also creates this customer in your connected ERP. To disable that behavior, coordinate with your Implementation Manager before making this API call.

## Step 2: Create products

A Product represents something you sell: a SKU, a service line, or any billable offering you want to reuse across contracts. Once created, a product is available to attach to billing terms and appears in your Tabs product catalog.

If the product already exists in Tabs (created in the app or via an earlier API call), skip ahead and use its existing `id` when you build the billing term in Step 5.

```bash
curl -X POST https://integrators.prod.api.tabsplatform.com/v3/products \
  -H "Authorization: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Professional Services",
    "status": "ACTIVE",
    "description": "Hourly consulting and implementation work",
    "integrationItemId": "{itemId}",
    "erpClassId": "{erpClassId}"
  }'
```

Tabs returns the created Product object. Save the `id` as `{productId}` — you'll reference it when creating billing terms.

`integrationItemId` maps the product to an account in your connected ERP — see Step 4 for how to look one up. `erpClassId` is optional and only applies if your ERP uses Class.

## Step 3: Create the contract

A Contract defines the billing arrangement with the customer and serves as the container for the billing terms you attach in Step 5. Every billing relationship in Tabs starts with a contract.

```bash
curl -X POST https://integrators.prod.api.tabsplatform.com/v3/contracts \
  -H "Authorization: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Corp — Services Agreement 2025",
    "customerId": "{customerId}"
  }'
```

Tabs returns the created Contract object. Save the `id` as `{contractId}`.

## Step 4: Look up an item

An Item maps a billing term to an account in your ERP. Items are created in the Tabs app or synced from your ERP—they can't be created via the API.

List your available items to find the one to use for this billing term:

```bash
curl https://integrators.prod.api.tabsplatform.com/v3/items \
  -H "Authorization: YOUR_API_KEY"
```

Tabs returns a paginated list of items:

```json
{
  "payload": {
    "data": [
      {
        "id": "item_abc123",
        "name": "Professional Services",
        "externalIds": [
          {
            "externalId": "PS-001",
            "sourceType": "QUICKBOOKS"
          }
        ]
      }
    ],
    "limit": 50,
    "totalItems": 1,
    "currentPage": 1
  },
  "success": true,
  "message": "string",
  "error": null
}
```

Save the `id` of the item you want to use as `{itemId}`.

## Step 5: Add a flat-rate billing term

A billing term defines what you're charging, how much it costs, and when. For flat-rate billing, set `billingType` to `FLAT` and set the invoice amount in the `pricing` array.

Pass the `itemId` from Step 4 to link this billing term to your ERP and reporting.

```bash
curl -X POST https://integrators.prod.api.tabsplatform.com/v3/contracts/{contractId}/billing-terms \
  -H "Authorization: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Professional Services — February 2025",
    "description": "One-time engagement fee for February deliverables",
    "billingStartDate": "2025-02-01",
    "isRecurring": false,
    "interval": "NONE",
    "intervalFrequency": 1,
    "invoiceDateStrategy": "FIRST_OF_PERIOD",
    "netPaymentTerms": 30,
    "quantity": 0,
    "billingType": "FLAT",
    "pricingType": "SIMPLE",
    "itemId": "{itemId}",
    "pricing": [
      {
        "tier": 1,
        "amount": 5000.00,
        "amountType": "TOTAL_INVOICE",
        "tierMinimum": 0
      }
    ]
  }'
```

Tabs returns the created billing term. Save the `id` as `{billingTermId}`.

To add multiple line items to the same invoice, create multiple Billing Terms on the same Contract before processing it in Step 6.

## Step 6: Mark the contract as processed

Marking a contract as processed tells Tabs to generate invoices for all of its billing terms and surface the billing and revenue data in the app.

```bash
curl -X POST https://integrators.prod.api.tabsplatform.com/v3/contracts/{contractId}/actions \
  -H "Authorization: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "MARK_AS_PROCESSED"
  }'
```

Tabs generates a draft invoice for each billing term on the contract.

Once a Contract is marked as processed, its Billing Terms can no longer be edited. Review all Billing Terms before calling this endpoint.

## Step 7: Send the invoice

Fetch the generated invoice to review it, then send it to the customer.

Fetch the invoice:

```bash
curl "https://integrators.prod.api.tabsplatform.com/v3/invoices?filter=customerId:eq:{customerId}&page=1&limit=50" \
  -H "Authorization: YOUR_API_KEY"
```

Send the invoice:

```bash
curl -X POST "https://integrators.prod.api.tabsplatform.com/v3/customers/{customerId}/invoices/{invoiceId}/actions" \
  -H "Authorization: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "SEND",
    "sendToCustomer": true,
    "sendToErp": true
  }'
```

Tabs emails the invoice to the customer's billing contact and syncs it to your connected ERP.

Set `sendToCustomer` to `false` to record the invoice in Tabs without emailing it, or `sendToErp` to `false` to skip the ERP sync. At least one of the two must be `true`.

## Set up recurring flat-rate billing

To charge the same flat amount on a repeating schedule, set three fields on the billing term:

* Set `isRecurring` to `true`
* Set `interval` to your billing frequency (`MONTH`, `QUARTER`, `YEAR`, and so on)
* Set `intervalFrequency` to the number of intervals between each invoice (for example, `1` for monthly, `3` for quarterly)

Everything else stays the same. Tabs automatically generates a new invoice at each billing interval after the contract is processed.

Example: monthly billing at \$5,000

```bash
curl -X POST https://integrators.prod.api.tabsplatform.com/v3/contracts/{contractId}/billing-terms \
  -H "Authorization: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Monthly retainer",
    "billingStartDate": "2025-01-01",
    "isRecurring": true,
    "interval": "MONTH",
    "intervalFrequency": 1,
    "duration": 12,
    "invoiceDateStrategy": "FIRST_OF_PERIOD",
    "netPaymentTerms": 30,
    "quantity": 0,
    "billingType": "FLAT",
    "pricingType": "SIMPLE",
    "itemId": "{itemId}",
    "pricing": [
      {
        "tier": 1,
        "amount": 5000.00,
        "amountType": "TOTAL_INVOICE",
        "tierMinimum": 0
      }
    ]
  }'
```

## Check async job status

Some Tabs operations complete asynchronously. When an operation returns a `jobId`, poll it to check completion:

```bash
curl "https://integrators.prod.api.tabsplatform.com/v3/jobs/{jobId}" \
  -H "Authorization: YOUR_API_KEY"
```

Poll until `status` is `SUCCESS` or `FAILURE`.

## Next steps

* [Set up Stripe](/stripe)
* [Configure Custom Billing](/bill-customers/custom)
* [Match payments to invoices](/collect-payments/match-payments)