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

# Invoice a customer quickstart

Learn how to send an invoice to a customer using the Tabs API. In this guide you'll make a couple API calls and build a working example that you can adapt for your integration.

## Get your API key

You can create an API key from the [app](https://app.tabsplatform.com/merchant/developers). All requests require your key in the `Authorization` header.

## Step 1: Create the customer

A customer in Tabs represents the business you're billing. At minimum, provide a business name, but additional details are used in other workflows.

Create a customer using the `POST /v3/customers` endpoint, swapping in your API key:

```curl
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": "jordan.lee@acme.com",
    "billingAddress": {
      "line1": "123 Main Street",
      "city": "San Francisco",
      "state": "CA",
      "postalCode": "94105",
      "country": "US"
    }
  }'
```

Customer creation is processed asynchronously. The response doesn't include the new customer — it includes a `jobId` you can poll for the result:

```json
{
  "payload": {
    "message": "Customer creation job created, jobId: 123e4567-e89b-12d3-a456-426614174000",
    "jobId": "123e4567-e89b-12d3-a456-426614174000",
    "statusEndpoint": "/v3/jobs/123e4567-e89b-12d3-a456-426614174000"
  },
  "success": true,
  "message": "Create customer"
}
```

Poll the job status endpoint until it completes, then read the new customer's `id` from the result:

```curl
curl "https://integrators.prod.api.tabsplatform.com/v3/jobs/123e4567-e89b-12d3-a456-426614174000"
  -H "Authorization: YOUR_API_KEY"
```

```json
{
  "payload": {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "status": "SUCCESS",
    "type": "CREATE_CUSTOMER",
    "results": {
      "data": { "customerId": "123e4567-e89b-12d3-a456-426614174000" },
      "status": "SUCCESS"
    }
  },
  "success": true,
  "message": "Returns Job Status by Job id"
}
```

Alternatively, look up the customer with a filtered `GET /v3/customers` call — useful if you don't have the `jobId` handy, but note this can return more than one result if customer names aren't unique:

```curl
curl "https://integrators.prod.api.tabsplatform.com/v3/customers?filter=name:eq:Acme Corp&page=1&limit=50"
  -H "Authorization: YOUR_API_KEY"
```

```json
{
  "payload": {
    "data": [
      {
        "id": "123e4567-e89b-12d3-a456-426614174000",
        "name": "Acme Corp",
        "defaultCurrency": "USD",
        "primaryBillingContactEmail": "jordan.lee@acme.com",
        "billingAddress": {
          "line1": "123 Main Street",
          "city": "San Francisco",
          "state": "CA",
          "postalCode": "94105",
          "country": "US"
        },
        "lastUpdatedAt": "2025-01-15T12:00:00Z"
      }
    ],
    "limit": 50,
    "totalItems": 1,
    "currentPage": 1
  },
  "success": true,
  "message": "string",
  "error": null
}
```

Save the returned `id` — you'll use it in subsequent steps. IDs in Tabs are UUIDs (e.g. `123e4567-e89b-12d3-a456-426614174000`).

Tabs can automatically create customers by syncing with your CRM or ERP. If the customers exist already, try the GET /v3/customers endpoint to retrieve a customer id.

## Step 2: Create the contract

Every billing relationship in Tabs starts with a contract. A contract defines the billing arrangement with the customer. It also links the customer to a named agreement and serves as the container for any billing terms and schedules you attach later.

To create a contract (in Tabs) for a customer, call the POST /v3/contracts endpoint. Use your API key and the customer ID returned in Step 1.

```curl
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": "123e4567-e89b-12d3-a456-426614174000"
  }'
```

Unlike customer creation, contract creation is synchronous — Tabs returns the created contract directly:

```json
{
  "payload": {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "name": "Acme Corp — Services Agreement 2025",
    "status": "NEW",
    "customerId": "123e4567-e89b-12d3-a456-426614174000",
    "customerName": "Acme Corp",
    "source": "API",
    "createdAt": "2025-01-15T12:05:00Z",
    "lastUpdatedAt": "2025-01-15T12:05:00Z"
  },
  "success": true,
  "message": "Create contract"
}
```

Again, save the returned `id`, which represents the contract. Note  the contract starts in status `NEW` and won't generate invoices until you mark it as processed in Step 4.

## Step 3: Add a billing term

A billing term defines what you're charging, how much it costs, and how often. For a one-off invoice, create a billing term with isRecurring set to false. Tabs generates a single invoice for the full amount when you mark the contract as processed in Step 4.

Before Tabs can send the resulting invoice in Step 5, the billing term's line item must resolve to a catalog item — either through a **product** (recommended, see below) or by passing `itemId` directly. If your merchant account is connected to an ERP, a billing term created without either will generate an invoice that fails to send with a 400 error.

### Create a product

The recommended way to link a billing term to a catalog item is through the [Product Catalog](/product-catalog): create a product once, mapped to an item in your ERP, and reuse it across billing terms. Look up the ERP item you want to map to with `GET /v3/items`, then create the product:

```curl
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",
    "displayName": "Professional Services",
    "status": "ACTIVE",
    "integrationItemId": "00112233-4455-6677-8899-aabbccddeeff"
  }'
```

Tabs returns the created product:

```json
{
  "payload": {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "manufacturerId": "9f8e7d6c-5b4a-3928-1706-f5e4d3c2b1a0",
    "name": "Professional Services",
    "displayName": "Professional Services",
    "status": "ACTIVE",
    "integrationItemId": "00112233-4455-6677-8899-aabbccddeeff",
    "integrationItemName": "Consulting Services",
    "erpClassId": null,
    "erpClassName": null,
    "createdAt": "2025-01-15T12:08:00Z",
    "updatedAt": "2025-01-15T12:08:00Z"
  },
  "success": true,
  "message": "Create a new product"
}
```

Save the returned `id` to connect it to the billing term next.

### Create the billing term

Create the billing term on the contract by calling the POST /v3/contracts/\{contractId}/billing-terms endpoint, passing the product's `id` as `productId`. Tabs copies the product's name, description, integration item, and integration class onto the billing term automatically — you can still override any of them here:

```curl
curl -X POST https://integrators.prod.api.tabsplatform.com/v3/contracts/a1b2c3d4-e5f6-7890-abcd-ef1234567890/billing-terms
  -H "Authorization: YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
    "productId": "123e4567-e89b-12d3-a456-426614174000",
    "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": 1,
    "billingType": "FLAT",
    "pricingType": "SIMPLE",
    "pricing": [
      {
        "tier": 1,
        "amount": 5000.00,
        "amountType": "TOTAL_INVOICE",
        "tierMinimum": 0
      }
    ]
  }'
```

Tabs returns a response like:

```json
{
  "payload": {
    "id": "11223344-5566-7788-99aa-bbccddeeff00",
    "contractId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "billingTermGroupId": "aabbccdd-eeff-0011-2233-445566778899",
    "name": "Professional Services",
    "description": "One-time engagement fee for February deliverables",
    "billingStartDate": "2025-02-01",
    "billingEndDate": "2025-02-01",
    "isRecurring": false,
    "billingType": "FLAT",
    "pricingType": "SIMPLE",
    "netPaymentTerms": 30,
    "itemId": "00112233-4455-6677-8899-aabbccddeeff",
    "productId": "123e4567-e89b-12d3-a456-426614174000",
    "createdAt": "2025-01-15T12:10:00Z"
  },
  "success": true,
  "message": "Create a billing term for a contract"
}
```

If you want to link the billing term directly, pass `itemId` (from `GET /v3/items`) instead of `productId`, along with `name` — but note that skipping both `itemId` and `productId` entirely will produce an invoice that can't be sent.

## Step 4: Mark the contract as processed

Creating a billing term defines what to bill, but Tabs doesn't generate the invoice until the contract is processed. Mark the contract as processed with the POST /v3/contracts/\{contractId}/actions endpoint:

```curl
curl -X POST https://integrators.prod.api.tabsplatform.com/v3/contracts/cont_xyz789/actions

  -H "Authorization: YOUR_API_KEY"

  -H "Content-Type: application/json"

  -d '{"action": "MARK_AS_PROCESSED"}'
```

Tabs sets the contract status to PROCESSED and generates a draft invoice for the billing term. The invoice has an issueDate of 2025-02-01 and a dueDate 30 days later (2025-03-03), based on the netPaymentTerms you specified.

You can change an invoice while it's in draft state.

## Step 5: Send the invoice

The final step is to send the invoice to the customer. First, fetch the invoice to review its details. Use the customer ID to scope the lookup with a filtered `GET /v3/invoices` call:

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

Tabs returns a response like:

```json
{
  "payload": {
    "data": [
      {
        "id": "inv_ghi012",
        "customerId": "cust_abc123",
        "status": "DRAFT",
        "issueDate": "2025-02-01",
        "dueDate": "2025-03-03",
        "total": 5000.00,
        "balanceRemaining": 5000.00,
        "lineItems": [
          {
            "id": "li_001",
            "name": "Professional Services — February 2025",
            "description": "One-time engagement fee for February deliverables",
            "quantity": 1,
            "unitPrice": 5000.00,
            "total": 5000.00
          }
        ]
      }
    ],
    "limit": 50,
    "totalItems": 1,
    "currentPage": 1
  },
  "success": true,
  "message": "string",
  "error": null
}
```

When you're ready to send the invoice, use the invoice actions endpoint to transition it from `DRAFT` to `SENT`. Sending is asynchronous, and at least one of `sendToErp` or `sendToCustomer` must be `true`:

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

Tabs queues a background job and returns a job reference:

```json
{
  "message": "Invoice send job created, jobId: 9f8b7c6d-1234-4a56-b789-0a1b2c3d4e5f, please check the job status via /jobs/9f8b7c6d-1234-4a56-b789-0a1b2c3d4e5f"
}
```

This response intentionally doesn't follow the standard `{payload, success, message}` envelope described in [Develop with the Tabs API](/develop-with-api#response-format). Parse the `jobId` out of `message`, or poll `/v3/jobs/{jobId}` using the id you already have.

Once the job completes, the invoice transitions from `DRAFT` to `SENT`. In a complete integration, you can set up payment options through Stripe and Plaid.

## Next steps

From here, you can:

* Review the [Tabs Data Model](/data-model)
* [Mark an Invoice as paid](/collect-payments/mark-invoices)