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

# Payment links and products

> Read your hosted checkouts and the catalogue behind them

## Payment links

<Note>
  These examples call the production host. While you build, replace `https://api-harmony.compago.com` with `https://demo-api-harmony.compago.com` and use a key from the Demo dashboard. See [Environments](/get-started/environments).
</Note>

A payment link is a hosted checkout: a URL you send anywhere, which Compago renders and which collects the card. `GET /developer/v1/payment-link` returns your links with their current definition.

```bash theme={null}
curl "https://api-harmony.compago.com/api/developer/v1/payment-link?status=ACTIVE&limit=50" \
  -H "x-api-key: $COMPAGO_API_KEY"
```

```json theme={null}
{
  "id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
  "url": "https://app.compago.com/checkout/pl/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
  "type": "PRODUCT_BASED",
  "status": "ACTIVE",
  "archived": false,
  "version": 3,
  "title": null,
  "amount": null,
  "currency": null,
  "currentUsage": 12,
  "maxUsage": null,
  "allowedTerms": null,
  "fields": [
    { "id": "...", "title": "Order reference", "type": "TEXT", "isOptional": false, "sequence": 1 }
  ],
  "products": [
    {
      "id": "...",
      "quantity": 2,
      "product": { "id": "...", "name": "Camiseta azul", "price": 350, "currency": "MXN", "status": "ACTIVE" }
    }
  ]
}
```

### Types

| Type | What the customer pays |
| - | - |
| `FIXED_AMOUNT` | An amount set on the link. `amount` and `currency` are populated |
| `PRODUCT_BASED` | The total of the products on the link. `amount` is null; the total comes from `products` |
| `SUBSCRIPTION` | A recurring amount on a cadence. The `billingPeriod` fields are populated |
| `FLEXIBLE_AMOUNT` | The customer chose the amount. Legacy; readable but no longer created |

### Fields worth explaining

<AccordionGroup>
  <Accordion title="fields and products are the CURRENT definition">
    Custom inputs and products removed by an edit are soft-deleted rather than dropped, because past payments reference them and that is how an old receipt still renders what the customer actually saw. Both arrays return only what the link sells today.
  </Accordion>

  <Accordion title="version is a change token">
    It increments on every edit made in the dashboard. If you cache link definitions, comparing `version` tells you whether anything changed without diffing the whole object.
  </Accordion>

  <Accordion title="currentUsage counts only while a cap exists">
    `maxUsage` caps how many times a link can be paid, and `currentUsage` counts toward it. With no cap set, `currentUsage` is null: nothing is being counted.
  </Accordion>

  <Accordion title="allowedTerms restricts the installment plans">
    The installment plans the link's checkout offers, as numbers of monthly installments: `1` is a single payment and any larger number is that many months without interest. With one entry the customer pays with that plan and cannot choose another. `null` offers every plan active on your account. A listed plan that stops being available is no longer offered, and nothing is offered in its place. Always `null` on `SUBSCRIPTION` links.
  </Accordion>

  <Accordion title="archived is a soft delete">
    Archived links are hidden from the list unless you pass `archived=true`, and they never accept payments. The record is kept because payments reference the link they were taken through.
  </Accordion>
</AccordionGroup>

### Filters

| Parameter | Notes |
| - | - |
| `status` | `ACTIVE` or `INACTIVE` |
| `type` | One of the four types above |
| `archived` | `true` lists archived links instead of live ones |

## Products

Products are the catalogue that `PRODUCT_BASED` links sell from. A link references products rather than copying them, so a price change in the dashboard changes what every link containing that product charges.

```bash theme={null}
curl "https://api-harmony.compago.com/api/developer/v1/product?status=ACTIVE&limit=100" \
  -H "x-api-key: $COMPAGO_API_KEY"
```

```json theme={null}
{
  "id": "11111111-2222-3333-4444-555555555555",
  "name": "Camiseta azul",
  "description": "Talla M",
  "status": "ACTIVE",
  "price": 350,
  "currency": "MXN",
  "createdAt": "2026-03-04T05:06:07.089Z",
  "updatedAt": "2026-03-04T05:06:07.089Z"
}
```

| Parameter | Notes |
| - | - |
| `name` | Case-insensitive partial match |
| `status` | `ACTIVE` or `ARCHIVED`. Repeat for both |
| `createdAtFrom`, `createdAtTo` | ISO-8601 bounds |

<Note>
  Unlike the dashboard, which defaults to showing active products only, this endpoint returns **everything** when you omit `status`. A catalogue reconciliation needs to see archived rows to know they are archived; a UI list just needs to look tidy. Pass `status=ACTIVE` for the dashboard's behaviour.
</Note>

## Keeping your own catalogue in sync

Compago has no concept of your SKU, so keep your own map from SKU to Compago product id. Since this API is read-only, use it to detect drift and reconcile, and make the corrections in the dashboard.

```javascript theme={null}
async function findDrift(apiKey, bySku) {
  const url = new URL('https://api-harmony.compago.com/api/developer/v1/product');
  url.searchParams.set('limit', '100');

  const res = await fetch(url, { headers: { 'x-api-key': apiKey } });
  if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);

  const { data } = await res.json();

  // Anything whose Compago price no longer matches the price your system believes.
  return data
    .map(product => ({ product, expected: bySku.get(product.id)?.price }))
    .filter(({ product, expected }) => expected !== undefined && expected !== product.price);
}
```
