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

# Products

> Group and categorize your charges by product or service. Optional.

A **product** (`Product`) is used to **group and categorize** your charges (debts, subscriptions, and payment links). Its use is **completely optional**: you can charge without associating any product.

You use it when you want to report your charges by product or service line, for example "Plan Premium", "Gym monthly fee", or "Internet 100MB".

## Natural key: `name`

The `name` is the product's natural key and is **unique per organization**. Creating a product with a `name` that already exists does **not** generate a duplicate or an error: it returns the existing product.

<Note>
  That is why `POST /products` is idempotent by design: repeating the same `name` always returns the same product. You do not need to check beforehand whether it already exists.
</Note>

## Create a product

```bash theme={null}
curl --request POST \
  --url 'https://tapipay-facade.dev.tapila.cloud/products' \
  --header 'x-api-key: YOUR_API_KEY' \
  --header 'x-authorization-token: YOUR_TAPI_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{ "name": "Plan Premium" }'
```

```json theme={null}
{
  "data": {
    "productId": "42",
    "name": "Plan Premium",
    "active": true,
    "createdAt": "2026-06-03T18:00:00Z",
    "updatedAt": "2026-06-03T18:00:00Z"
  },
  "requestId": "req_a1b2c3d4"
}
```

<Note>
  The `productId` is the identifier in the system, exposed as a string **without a prefix**.
</Note>

## Inline creation (`productName`)

You do not need to create the product in advance. You can send it **inline** when creating a debt, subscription, or payment link with the `productName` field. If the name does not exist, it is created; if it already exists, it is reused without duplicating it.

```bash theme={null}
curl --request POST \
  --url 'https://tapipay-facade.dev.tapila.cloud/debts' \
  --header 'x-api-key: YOUR_API_KEY' \
  --header 'x-authorization-token: YOUR_TAPI_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "externalClientId": "CLI-00042",
    "amount": 2500.00,
    "dueDate": "2026-07-10",
    "productName": "Gym Plan"
  }'
```

In the charge response, the product comes embedded with four fields:

```json theme={null}
{
  "product": {
    "productId": "42",
    "name": "Gym Plan",
    "active": true,
    "createdInline": true
  }
}
```

The `createdInline` field is `true` if the product was created in that same request, and `false` if it already existed and was reused.

## Activate and deactivate

<ResponseField name="active" type="boolean" default="true">
  Status of the product. An inactive product cannot be associated with new charges.
</ResponseField>

`DELETE /products/{id}` performs a **soft delete**: it sets `active: false` without deleting the product, so the history is preserved. Associating an inactive product with a new charge returns `422 BUSINESS_RULE_VIOLATION`.

## Endpoints

| Method   | Path             | Description                                                     |
| -------- | ---------------- | --------------------------------------------------------------- |
| `POST`   | `/products`      | Create a product (or return the existing one with that `name`). |
| `GET`    | `/products/{id}` | Retrieve a product.                                             |
| `GET`    | `/products`      | List products (filters `name`, `active`).                       |
| `PATCH`  | `/products/{id}` | Update `name` or `active`.                                      |
| `DELETE` | `/products/{id}` | Soft delete (sets `active: false`).                             |

<Card title="Try it in the API Reference" icon="play" href="/api-reference">
  Explore each endpoint with its interactive playground.
</Card>
