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

# Develop with the Tabs API

The Tabs API is a RESTful, JSON-based interface for managing your billing lifecycle programmatically.
Use it to push Customers, Contracts, Billing Terms, and usage data into Tabs, and to pull Invoices, Payments, and reports back out.
This page covers the mechanics of working with the API—authentication, request format, error handling, pagination, and rate limits.

## Base URL

All production API requests use the following base URL:

```
https://integrators.prod.api.tabsplatform.com
```

The base URL above points to the Tabs production environment. To request access to a sandbox environment for development and testing, contact your Tabs Implementation Manager.

## Authentication

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

```
Authorization: YOUR_API_KEY
```

API keys are scoped to a single Tabs entity. The key identifies which account the request operates on, and Tabs enforces access controls accordingly.

Keys can be revoked at any time. A revoked key returns 401 Unauthorized.

## Request format

The API accepts and returns JSON. Include the Content-Type header on all requests with a body:

```
Content-Type: application/json
```

Here is a minimal example that creates a customer:

```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",
    "primaryBillingContactName": "Jordan Lee",
    "primaryBillingContactEmail": "jordan.lee@acme.com"
  }'
```

## Response format

Every response is wrapped in a standard envelope with three fields:

```json
{
  "payload": { ... },
  "success": true,
  "message": "Operation successful"
}
```

| Field   | Type            | Description                                                                                 |
| :------ | :-------------- | :------------------------------------------------------------------------------------------ |
| payload | Object or Array | The requested data. For list endpoints, this contains pagination metadata and a data array. |
| success | Boolean         | Whether the request succeeded.                                                              |
| message | String          | A human-readable summary of the result.                                                     |

## Errors

When a request fails, the response includes an error object with a status code, message, and optional details:

```json
{
  "payload": null,
  "success": false,
  "message": "Validation failed",
  "error": {
    "code": 400,
    "message": "Validation failed",
    "details": {
      "name": ["name must be a string"]
    }
  }
}
```

## Common status codes

| Status                  | Meaning                                                                  |
| :---------------------- | :----------------------------------------------------------------------- |
| 200 OK                  | Request succeeded                                                        |
| 201 Created             | Resource created successfully                                            |
| 400 Bad Request         | Invalid JSON, missing required fields, or a validation rule was violated |
| 401 Unauthorized        | Missing, invalid, or revoked API key                                     |
| 404 Not Found           | The requested resource does not exist                                    |
| 429 Too Many Requests   | Rate limit exceeded. Back off and retry                                  |
| 503 Service Unavailable | Transient failure                                                        |

## Pagination

List endpoints support offset-based pagination with page and limit query parameters:

```
GET /v3/customers?page=2&limit=25
```

The response wraps the results with pagination metadata:

```json
{
  "payload": {
    "currentPage": 2,
    "limit": 25,
    "totalItems": 142,
    "data": [ ... ]
  },
  "success": true,
  "message": "Operation successful"
}
```

| Parameter | Default | Description                           |
| :-------- | :------ | :------------------------------------ |
| page      | 1       | The page number (1-indexed).          |
| limit     | 50      | The maximum number of items per page. |

## Filtering

List endpoints support filtering with the filter query parameter. Each filter uses the format `property:rule:value`:

```
GET /v3/customers?filter=name:like:acme
```

To apply more than one filter, separate them with commas. Filters combine with AND—a customer must match every condition to be returned:

```
GET /v3/customers?filter=name:like:acme,lastUpdatedAt:gte:2026-01-01
```

The response is a standard paginated list containing only the customers that match your filters:

```json
{
  "payload": {
    "currentPage": 1,
    "limit": 50,
    "totalItems": 1,
    "data": [
      {
        "id": "123e4567-e89b-12d3-a456-426614174000",
        "name": "Acme Inc.",
        "primaryBillingContactEmail": "john.doe@example.com",
        "defaultCurrency": "USD",
        "externalIds": [{ "type": "NETSUITE", "id": "CUST-1042" }],
        "lastUpdatedAt": "2026-02-14T09:30:00.000Z"
      }
    ]
  },
  "success": true,
  "message": "Operation successful"
}
```

Supported rules:

| Rule      | Meaning                                 |
| :-------- | :-------------------------------------- |
| eq        | Equals                                  |
| neq       | Does not equal                          |
| gt        | Greater than                            |
| gte       | Greater than or equal to                |
| lt        | Less than                               |
| lte       | Less than or equal to                   |
| like      | Contains                                |
| nlike     | Does not contain (case-insensitive)     |
| in        | Matches any                             |
| nin       | None of a pipe-separated list           |
| isnull    | Value is null (no value: `name:isnull`) |
| isnotnull | Value is not null                       |

Filterable fields on `GET /v3/customers`:

| Field                  | Description                                                     |
| :--------------------- | :-------------------------------------------------------------- |
| name                   | The customer's name.                                            |
| externalIds.externalId | An external system identifier mapped to the customer.           |
| lastUpdatedAt          | When the customer was last updated. Use date format YYYY-MM-DD. |

Wrap values containing commas or spaces in double quotes (`filter=name:eq:"Acme, Inc."`). For `in` and `nin`, separate values with a pipe (`filter=externalIds.externalId:in:cust_1|cust_2`).

## Rate limiting

The API generally enforces a rate limit of 10 requests per second per merchant. When the limit is exceeded, the API returns 429 Too Many Requests. Pause and retry.

The usage events endpoint (`POST /v1/events`) has a separate, higher limit of 10,000 requests per minute to support high-throughput ingestion workloads.

## Next steps

* [Tabs data model](/data-model)
* [Invoice a customer](/invoice-customer-quickstart)
* [Usage-based billing quickstart](usage-based-quickstart)
* [API reference](/api-reference)