TracePass
Products

Delete a product permanently

**Permanent and irreversible.** Removes the product row + any uploaded documents whose only reference was this product (documents linked to other products/passports stay, just with `productId` unset). R2 storage under `<companyId>/products/<productId>/` is swept.

DELETE/api/v1/products/{id}
Download OpenAPI 3.1
DELETE/api/v1/products/{id}

Delete a product permanently

**Permanent and irreversible.** Removes the product row + any uploaded documents whose only reference was this product (documents linked to other products/passports stay, just with `productId` unset). R2 storage under `<companyId>/products/<productId>/` is swept.

**Only products with ZERO passports of any status are eligible** — including archived. Returns `409 Conflict` with the live passport count in `reason` when ineligible. The rule preserves audit history: an archived passport carries the regulatory story ("we published this, then withdrew it") that would be orphaned if the parent product disappeared.

**Paid-plan feature.** Returns `403 Forbidden` on Free plans; use `POST /api/v1/products/{id}/archive` instead — archive keeps the row for audit and works on every plan.

Counts as one v1 write. Honours `Idempotency-Key`. No webhook (the `billingEvents` audit entry is the trail).

Path parameters

  • idrequired

    ObjectId

    Product ID.

Headers

  • Authorizationrequired

    string

    `Bearer <api-key>`.

  • Idempotency-Key

    string

    UUID v4.

Request

curl -sS -X DELETE \
  https://app.tracepass.eu/api/v1/products/6650b2c3d4e5f6a7b8c9d0e1 \
  -H "Authorization: Bearer tp_REDACTED_xxxxxxxxxxxx"

Response

{
  "message": "Product permanently deleted",
  "summary": {
    "productId": "6650b2c3d4e5f6a7b8c9d0e1",
    "name": "Demo product",
    "deletedCounts": {
      "documentRefsUnlinked": 2,
      "documentsDeleted": 1,
      "r2ObjectsDeleted": 3,
      "documentR2ObjectsDeleted": 1
    }
  }
}

Related endpoints