PPanel Edge

catalog

由 openapi.json 自动生成的 20 个端点及其请求、响应和权限元数据。

此参考由已发布 API 契约生成。字段、请求头和权限要求如下。

下载完整 OpenAPI 文档 · SHA-256: ccaa20cec11fad96e369b88073ce3608a5b071ce55d6dc03fa31df7b0f08c525

标识符和数据结构与公开契约一致,说明直接给出调用规则。共享模型见 数据模型.

GET /api/v1/admin/apple/product-mappings

GET /api/v1/admin/apple/product-mappings

Apple product to price mappings

{
  "parameters": [
    {
      "in": "query",
      "name": "cursor",
      "required": false,
      "schema": {
        "format": "uuid",
        "type": "string"
      }
    },
    {
      "in": "query",
      "name": "page_size",
      "required": false,
      "schema": {
        "default": 20,
        "maximum": 100,
        "minimum": 1,
        "type": "integer"
      }
    }
  ],
  "responses": {
    "200": {
      "content": {
        "application/json": {
          "schema": {
            "properties": {
              "has_more": {
                "type": "boolean"
              },
              "items": {
                "items": {
                  "properties": {
                    "apple_product_id": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "plan_id": {
                      "format": "uuid",
                      "type": "string"
                    },
                    "price_id": {
                      "format": "uuid",
                      "type": "string"
                    }
                  },
                  "required": [
                    "price_id",
                    "apple_product_id",
                    "plan_id"
                  ],
                  "type": "object"
                },
                "type": "array"
              },
              "next_cursor": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "items",
              "has_more",
              "next_cursor"
            ],
            "type": "object"
          }
        }
      },
      "description": "Result"
    }
  },
  "summary": "Apple product to price mappings",
  "tags": [
    "catalog"
  ],
  "x-body": "none",
  "x-body-limit": 0,
  "x-idempotency": "none",
  "x-permission": "catalog.read",
  "x-reauth": false,
  "effectiveSecurity": []
}

PUT /api/v1/admin/apple/product-mappings/{priceId}

PUT /api/v1/admin/apple/product-mappings/{priceId}

Assign an Apple product to a price

{
  "parameters": [
    {
      "in": "path",
      "name": "priceId",
      "required": true,
      "schema": {
        "format": "uuid",
        "type": "string"
      }
    }
  ],
  "requestBody": {
    "content": {
      "application/json": {
        "schema": {
          "properties": {
            "apple_product_id": {
              "maxLength": 255,
              "minLength": 1,
              "type": [
                "string",
                "null"
              ]
            },
            "expected_apple_product_id": {
              "maxLength": 255,
              "type": [
                "string",
                "null"
              ]
            }
          },
          "required": [
            "apple_product_id",
            "expected_apple_product_id"
          ],
          "type": "object"
        }
      }
    },
    "required": true
  },
  "responses": {
    "200": {
      "content": {
        "application/json": {
          "schema": {
            "properties": {
              "apple_product_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "price_id": {
                "format": "uuid",
                "type": "string"
              }
            },
            "required": [
              "price_id",
              "apple_product_id"
            ],
            "type": "object"
          }
        }
      },
      "description": "Result"
    },
    "409": {
      "description": "Mapping used, stale or duplicated"
    }
  },
  "summary": "Assign an Apple product to a price",
  "tags": [
    "catalog"
  ],
  "x-audit": "catalog.apple_mapping.update",
  "x-body": "json",
  "x-body-limit": 65536,
  "x-idempotency": "required",
  "x-permission": "catalog.price.manage",
  "x-reauth": false,
  "effectiveSecurity": []
}

PUT /api/v1/admin/catalog/change-paths

PUT /api/v1/admin/catalog/change-paths

Configure a price-option change path

{
  "requestBody": {
    "content": {
      "application/json": {
        "schema": {
          "additionalProperties": false,
          "properties": {
            "direction": {
              "enum": [
                "upgrade",
                "downgrade"
              ],
              "type": "string"
            },
            "enabled": {
              "type": "boolean"
            },
            "source_price_id": {
              "format": "uuid",
              "type": "string"
            },
            "target_price_id": {
              "format": "uuid",
              "type": "string"
            }
          },
          "required": [
            "source_price_id",
            "target_price_id",
            "direction",
            "enabled"
          ],
          "type": "object"
        }
      }
    },
    "required": true
  },
  "responses": {
    "200": {
      "content": {
        "application/json": {
          "schema": {
            "properties": {
              "ok": {
                "enum": [
                  true
                ],
                "type": "boolean"
              }
            },
            "required": [
              "ok"
            ],
            "type": "object"
          }
        }
      },
      "description": "Result"
    },
    "404": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "Subscription not found"
    },
    "409": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "Quote, path or version changed"
    }
  },
  "summary": "Configure a price-option change path",
  "tags": [
    "catalog"
  ],
  "x-audit": "catalog.change_path.configure",
  "x-body": "json",
  "x-body-limit": 65536,
  "x-idempotency": "required",
  "x-permission": "catalog.price.manage",
  "x-rate-limit": "commerce.checkout",
  "x-reauth": false,
  "effectiveSecurity": []
}

PUT /api/v1/admin/catalog/paid-reset-pricing

PUT /api/v1/admin/catalog/paid-reset-pricing

Configure reset base price, pro-rata pricing and minimum

{
  "requestBody": {
    "content": {
      "application/json": {
        "schema": {
          "additionalProperties": false,
          "properties": {
            "minimum_minor": {
              "pattern": "^\\d{1,18}$",
              "type": "string"
            },
            "plan_id": {
              "format": "uuid",
              "type": "string"
            },
            "price_minor": {
              "pattern": "^\\d{1,18}$",
              "type": [
                "string",
                "null"
              ]
            },
            "prorata": {
              "type": "boolean"
            }
          },
          "required": [
            "plan_id",
            "price_minor",
            "prorata",
            "minimum_minor"
          ],
          "type": "object"
        }
      }
    },
    "required": true
  },
  "responses": {
    "200": {
      "content": {
        "application/json": {
          "schema": {
            "properties": {
              "ok": {
                "enum": [
                  true
                ],
                "type": "boolean"
              }
            },
            "required": [
              "ok"
            ],
            "type": "object"
          }
        }
      },
      "description": "Result"
    },
    "404": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "Subscription not found"
    },
    "409": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "Quote, path or version changed"
    }
  },
  "summary": "Configure reset base price, pro-rata pricing and minimum",
  "tags": [
    "catalog"
  ],
  "x-audit": "catalog.paid_reset.configure",
  "x-body": "json",
  "x-body-limit": 65536,
  "x-idempotency": "required",
  "x-permission": "catalog.price.manage",
  "x-rate-limit": "commerce.checkout",
  "x-reauth": false,
  "effectiveSecurity": []
}

GET /api/v1/admin/catalog/plans

GET /api/v1/admin/catalog/plans

List plans

{
  "parameters": [
    {
      "in": "query",
      "name": "product_id",
      "required": false,
      "schema": {
        "format": "uuid",
        "type": "string"
      }
    },
    {
      "description": "Status; repeat the parameter for several values",
      "in": "query",
      "name": "status",
      "required": false,
      "schema": {
        "anyOf": [
          {
            "enum": [
              "draft",
              "on_sale",
              "off_sale",
              "retired"
            ],
            "type": "string"
          },
          {
            "items": {
              "enum": [
                "draft",
                "on_sale",
                "off_sale",
                "retired"
              ],
              "type": "string"
            },
            "type": "array"
          }
        ],
        "description": "Status; repeat the parameter for several values"
      }
    },
    {
      "description": "Default 20, at most 100",
      "in": "query",
      "name": "page_size",
      "required": false,
      "schema": {
        "description": "Default 20, at most 100",
        "maximum": 100,
        "minimum": 1,
        "type": "integer"
      }
    },
    {
      "description": "`next_cursor` of the previous page",
      "in": "query",
      "name": "cursor",
      "required": false,
      "schema": {
        "description": "`next_cursor` of the previous page",
        "maxLength": 512,
        "minLength": 1,
        "type": "string"
      }
    },
    {
      "description": "Return `total` (first page only)",
      "in": "query",
      "name": "include_total",
      "required": false,
      "schema": {
        "description": "Return `total` (first page only)",
        "enum": [
          "true",
          "false"
        ],
        "type": "string"
      }
    }
  ],
  "responses": {
    "200": {
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/CatalogPlanPage"
          }
        }
      },
      "description": "A page of plans (sort, id)"
    },
    "422": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "Invalid filter or cursor"
    }
  },
  "summary": "List plans",
  "tags": [
    "catalog"
  ],
  "x-body": "none",
  "x-body-limit": 0,
  "x-permission": "catalog.read",
  "effectiveSecurity": []
}

POST /api/v1/admin/catalog/plans

POST /api/v1/admin/catalog/plans

Create a plan (draft)

{
  "parameters": [
    {
      "description": "Idempotency-Key: 16–128 visible ASCII characters",
      "in": "header",
      "name": "idempotency-key",
      "required": true,
      "schema": {
        "description": "Idempotency-Key: 16–128 visible ASCII characters",
        "pattern": "^[\\x21-\\x7e]{16,128}$",
        "type": "string"
      }
    }
  ],
  "requestBody": {
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/CatalogPlanCreate"
        }
      }
    },
    "required": true
  },
  "responses": {
    "201": {
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/CatalogPlanSaved"
          }
        }
      },
      "description": "The plan with tag warnings, with ETag",
      "headers": {
        "ETag": {
          "description": "Current resource version for If-Match.",
          "schema": {
            "type": "string"
          }
        }
      }
    },
    "409": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "catalog.plan_code_taken or an Idempotency-Key conflict"
    },
    "422": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "Validation failed (tags, product, source-locale name)"
    }
  },
  "summary": "Create a plan (draft)",
  "tags": [
    "catalog"
  ],
  "x-audit": "catalog.plan.create",
  "x-body": "json",
  "x-body-limit": 524288,
  "x-concurrency": {
    "etag": true
  },
  "x-idempotency": "required",
  "x-permission": "catalog.plan.manage",
  "effectiveSecurity": []
}

POST /api/v1/admin/catalog/plans/reorder

POST /api/v1/admin/catalog/plans/reorder

Set the display order of the plans of a product

{
  "requestBody": {
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/CatalogPlanReorder"
        }
      }
    },
    "required": true
  },
  "responses": {
    "200": {
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/CatalogPlanList"
          }
        }
      },
      "description": "The plans of the product in their new order"
    },
    "404": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "The product has no plans"
    },
    "422": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "catalog.reorder_mismatch: not exactly the plans of the product"
    }
  },
  "summary": "Set the display order of the plans of a product",
  "tags": [
    "catalog"
  ],
  "x-audit": "catalog.plan.reorder",
  "x-body": "json",
  "x-body-limit": 65536,
  "x-idempotency": "optional",
  "x-permission": "catalog.plan.manage",
  "effectiveSecurity": []
}

GET /api/v1/admin/catalog/plans/{id}

GET /api/v1/admin/catalog/plans/{id}

One plan with its price options and tag warnings

{
  "parameters": [
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "format": "uuid",
        "type": "string"
      }
    }
  ],
  "responses": {
    "200": {
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/CatalogPlanDetail"
          }
        }
      },
      "description": "The plan, with ETag",
      "headers": {
        "ETag": {
          "description": "Current resource version for If-Match.",
          "schema": {
            "type": "string"
          }
        }
      }
    },
    "404": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "No such plan"
    }
  },
  "summary": "One plan with its price options and tag warnings",
  "tags": [
    "catalog"
  ],
  "x-body": "none",
  "x-body-limit": 0,
  "x-concurrency": {
    "etag": true
  },
  "x-permission": "catalog.read",
  "effectiveSecurity": []
}

PATCH /api/v1/admin/catalog/plans/{id}

PATCH /api/v1/admin/catalog/plans/{id}

Edit a plan (If-Match); sold entitlements keep their snapshots

{
  "parameters": [
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "format": "uuid",
        "type": "string"
      }
    },
    {
      "description": "Resource ETag; missing 428, malformed 412, stale 409.",
      "in": "header",
      "name": "If-Match",
      "required": true,
      "schema": {
        "type": "string"
      }
    }
  ],
  "requestBody": {
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/CatalogPlanPatch"
        }
      }
    },
    "required": true
  },
  "responses": {
    "200": {
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/CatalogPlanSaved"
          }
        }
      },
      "description": "The plan with tag warnings, with ETag",
      "headers": {
        "ETag": {
          "description": "Current resource version for If-Match.",
          "schema": {
            "type": "string"
          }
        }
      }
    },
    "404": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "No such plan"
    },
    "409": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "common.version_conflict (with current) or catalog.plan_code_taken"
    },
    "412": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "Malformed If-Match"
    },
    "422": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "Validation failed (tags, product, reset policy vs a permanent price)"
    },
    "428": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "If-Match missing"
    }
  },
  "summary": "Edit a plan (If-Match); sold entitlements keep their snapshots",
  "tags": [
    "catalog"
  ],
  "x-audit": "catalog.plan.update",
  "x-body": "json",
  "x-body-limit": 524288,
  "x-concurrency": {
    "etag": true,
    "ifMatch": true
  },
  "x-idempotency": "optional",
  "x-permission": "catalog.plan.manage",
  "effectiveSecurity": []
}

GET /api/v1/admin/catalog/plans/{id}/prices

GET /api/v1/admin/catalog/plans/{id}/prices

Price options of a plan

{
  "parameters": [
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "format": "uuid",
        "type": "string"
      }
    }
  ],
  "responses": {
    "200": {
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/CatalogPriceList"
          }
        }
      },
      "description": "Active options first"
    },
    "404": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "No such plan"
    }
  },
  "summary": "Price options of a plan",
  "tags": [
    "catalog"
  ],
  "x-body": "none",
  "x-body-limit": 0,
  "x-permission": "catalog.read",
  "effectiveSecurity": []
}

POST /api/v1/admin/catalog/plans/{id}/prices

POST /api/v1/admin/catalog/plans/{id}/prices

Add a price option

{
  "parameters": [
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "format": "uuid",
        "type": "string"
      }
    },
    {
      "description": "Idempotency-Key: 16–128 visible ASCII characters",
      "in": "header",
      "name": "idempotency-key",
      "required": true,
      "schema": {
        "description": "Idempotency-Key: 16–128 visible ASCII characters",
        "pattern": "^[\\x21-\\x7e]{16,128}$",
        "type": "string"
      }
    }
  ],
  "requestBody": {
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/CatalogPriceCreate"
        }
      }
    },
    "required": true
  },
  "responses": {
    "201": {
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/CatalogPrice"
          }
        }
      },
      "description": "The new option"
    },
    "404": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "No such plan"
    },
    "409": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "catalog.price_period_taken or an Idempotency-Key conflict"
    },
    "422": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "Validation failed (site currency, permanent price vs the reset policy)"
    }
  },
  "summary": "Add a price option",
  "tags": [
    "catalog"
  ],
  "x-audit": "catalog.price.create",
  "x-body": "json",
  "x-body-limit": 65536,
  "x-idempotency": "required",
  "x-permission": "catalog.price.manage",
  "effectiveSecurity": []
}

DELETE /api/v1/admin/catalog/plans/{id}/prices/{priceId}

DELETE /api/v1/admin/catalog/plans/{id}/prices/{priceId}

Delete a price option nothing refers to

{
  "parameters": [
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "format": "uuid",
        "type": "string"
      }
    },
    {
      "in": "path",
      "name": "priceId",
      "required": true,
      "schema": {
        "format": "uuid",
        "type": "string"
      }
    }
  ],
  "responses": {
    "204": {
      "description": "Deleted"
    },
    "404": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "No such price option"
    },
    "409": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "catalog.price_referenced: an order or an auto-renew setting refers to it; deactivate instead"
    }
  },
  "summary": "Delete a price option nothing refers to",
  "tags": [
    "catalog"
  ],
  "x-audit": "catalog.price.delete",
  "x-body": "none",
  "x-body-limit": 0,
  "x-idempotency": "optional",
  "x-permission": "catalog.price.manage",
  "effectiveSecurity": []
}

POST /api/v1/admin/catalog/plans/{id}/prices/{priceId}/deactivate

POST /api/v1/admin/catalog/plans/{id}/prices/{priceId}/deactivate

Deactivate a price option

{
  "parameters": [
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "format": "uuid",
        "type": "string"
      }
    },
    {
      "in": "path",
      "name": "priceId",
      "required": true,
      "schema": {
        "format": "uuid",
        "type": "string"
      }
    }
  ],
  "responses": {
    "200": {
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/CatalogPrice"
          }
        }
      },
      "description": "The option (inactive)"
    },
    "404": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "No such price option"
    }
  },
  "summary": "Deactivate a price option",
  "tags": [
    "catalog"
  ],
  "x-audit": "catalog.price.deactivate",
  "x-body": "none",
  "x-body-limit": 0,
  "x-idempotency": "optional",
  "x-permission": "catalog.price.manage",
  "effectiveSecurity": []
}

POST /api/v1/admin/catalog/plans/{id}/status

POST /api/v1/admin/catalog/plans/{id}/status

Change the sale status (If-Match)

{
  "parameters": [
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "format": "uuid",
        "type": "string"
      }
    },
    {
      "description": "Resource ETag; missing 428, malformed 412, stale 409.",
      "in": "header",
      "name": "If-Match",
      "required": true,
      "schema": {
        "type": "string"
      }
    }
  ],
  "requestBody": {
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/CatalogPlanStatus"
        }
      }
    },
    "required": true
  },
  "responses": {
    "200": {
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/CatalogPlanSaved"
          }
        }
      },
      "description": "The plan, with ETag",
      "headers": {
        "ETag": {
          "description": "Current resource version for If-Match.",
          "schema": {
            "type": "string"
          }
        }
      }
    },
    "404": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "No such plan"
    },
    "409": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "common.version_conflict or catalog.status_transition_invalid"
    },
    "412": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "Malformed If-Match"
    },
    "422": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "Validation failed"
    },
    "428": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "If-Match missing"
    }
  },
  "summary": "Change the sale status (If-Match)",
  "tags": [
    "catalog"
  ],
  "x-audit": "catalog.plan.status",
  "x-body": "json",
  "x-body-limit": 65536,
  "x-concurrency": {
    "etag": true,
    "ifMatch": true
  },
  "x-idempotency": "optional",
  "x-permission": "catalog.plan.manage",
  "effectiveSecurity": []
}

PUT /api/v1/admin/catalog/plans/{id}/stock

PUT /api/v1/admin/catalog/plans/{id}/stock

Set the stock total (If-Match); null = unlimited

{
  "parameters": [
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "format": "uuid",
        "type": "string"
      }
    },
    {
      "description": "Resource ETag; missing 428, malformed 412, stale 409.",
      "in": "header",
      "name": "If-Match",
      "required": true,
      "schema": {
        "type": "string"
      }
    }
  ],
  "requestBody": {
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/CatalogPlanStock"
        }
      }
    },
    "required": true
  },
  "responses": {
    "200": {
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/CatalogPlanSaved"
          }
        }
      },
      "description": "The plan, with ETag",
      "headers": {
        "ETag": {
          "description": "Current resource version for If-Match.",
          "schema": {
            "type": "string"
          }
        }
      }
    },
    "404": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "No such plan"
    },
    "409": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "common.version_conflict or catalog.stock_below_committed"
    },
    "412": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "Malformed If-Match"
    },
    "422": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "Validation failed"
    },
    "428": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "If-Match missing"
    }
  },
  "summary": "Set the stock total (If-Match); null = unlimited",
  "tags": [
    "catalog"
  ],
  "x-audit": "catalog.plan.stock",
  "x-body": "json",
  "x-body-limit": 65536,
  "x-concurrency": {
    "etag": true,
    "ifMatch": true
  },
  "x-idempotency": "optional",
  "x-permission": "catalog.plan.manage",
  "effectiveSecurity": []
}

GET /api/v1/admin/catalog/products

GET /api/v1/admin/catalog/products

List products

{
  "parameters": [
    {
      "description": "Status; repeat the parameter for several values",
      "in": "query",
      "name": "status",
      "required": false,
      "schema": {
        "anyOf": [
          {
            "enum": [
              "draft",
              "active",
              "archived"
            ],
            "type": "string"
          },
          {
            "items": {
              "enum": [
                "draft",
                "active",
                "archived"
              ],
              "type": "string"
            },
            "type": "array"
          }
        ],
        "description": "Status; repeat the parameter for several values"
      }
    },
    {
      "description": "Default 20, at most 100",
      "in": "query",
      "name": "page_size",
      "required": false,
      "schema": {
        "description": "Default 20, at most 100",
        "maximum": 100,
        "minimum": 1,
        "type": "integer"
      }
    },
    {
      "description": "`next_cursor` of the previous page",
      "in": "query",
      "name": "cursor",
      "required": false,
      "schema": {
        "description": "`next_cursor` of the previous page",
        "maxLength": 512,
        "minLength": 1,
        "type": "string"
      }
    },
    {
      "description": "Return `total` (first page only)",
      "in": "query",
      "name": "include_total",
      "required": false,
      "schema": {
        "description": "Return `total` (first page only)",
        "enum": [
          "true",
          "false"
        ],
        "type": "string"
      }
    }
  ],
  "responses": {
    "200": {
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/CatalogProductPage"
          }
        }
      },
      "description": "A page of products (sort, id)"
    },
    "422": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "Invalid filter or cursor"
    }
  },
  "summary": "List products",
  "tags": [
    "catalog"
  ],
  "x-body": "none",
  "x-body-limit": 0,
  "x-permission": "catalog.read",
  "effectiveSecurity": []
}

POST /api/v1/admin/catalog/products

POST /api/v1/admin/catalog/products

Create a product

{
  "parameters": [
    {
      "description": "Idempotency-Key: 16–128 visible ASCII characters",
      "in": "header",
      "name": "idempotency-key",
      "required": true,
      "schema": {
        "description": "Idempotency-Key: 16–128 visible ASCII characters",
        "pattern": "^[\\x21-\\x7e]{16,128}$",
        "type": "string"
      }
    }
  ],
  "requestBody": {
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/CatalogProductCreate"
        }
      }
    },
    "required": true
  },
  "responses": {
    "201": {
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/CatalogProduct"
          }
        }
      },
      "description": "The product, with ETag",
      "headers": {
        "ETag": {
          "description": "Current resource version for If-Match.",
          "schema": {
            "type": "string"
          }
        }
      }
    },
    "409": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "catalog.slug_taken or an Idempotency-Key conflict"
    },
    "422": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "Validation failed"
    }
  },
  "summary": "Create a product",
  "tags": [
    "catalog"
  ],
  "x-audit": "catalog.product.create",
  "x-body": "json",
  "x-body-limit": 524288,
  "x-concurrency": {
    "etag": true
  },
  "x-idempotency": "required",
  "x-permission": "catalog.product.manage",
  "effectiveSecurity": []
}

GET /api/v1/admin/catalog/products/{id}

GET /api/v1/admin/catalog/products/{id}

One product

{
  "parameters": [
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "format": "uuid",
        "type": "string"
      }
    }
  ],
  "responses": {
    "200": {
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/CatalogProduct"
          }
        }
      },
      "description": "The product, with ETag",
      "headers": {
        "ETag": {
          "description": "Current resource version for If-Match.",
          "schema": {
            "type": "string"
          }
        }
      }
    },
    "404": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "No such product"
    }
  },
  "summary": "One product",
  "tags": [
    "catalog"
  ],
  "x-body": "none",
  "x-body-limit": 0,
  "x-concurrency": {
    "etag": true
  },
  "x-permission": "catalog.read",
  "effectiveSecurity": []
}

PATCH /api/v1/admin/catalog/products/{id}

PATCH /api/v1/admin/catalog/products/{id}

Update a product (If-Match); status archived archives it

{
  "parameters": [
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "format": "uuid",
        "type": "string"
      }
    },
    {
      "description": "Resource ETag; missing 428, malformed 412, stale 409.",
      "in": "header",
      "name": "If-Match",
      "required": true,
      "schema": {
        "type": "string"
      }
    }
  ],
  "requestBody": {
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/CatalogProductPatch"
        }
      }
    },
    "required": true
  },
  "responses": {
    "200": {
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/CatalogProduct"
          }
        }
      },
      "description": "The product, with ETag",
      "headers": {
        "ETag": {
          "description": "Current resource version for If-Match.",
          "schema": {
            "type": "string"
          }
        }
      }
    },
    "404": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "No such product"
    },
    "409": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "common.version_conflict (with current) or catalog.slug_taken"
    },
    "412": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "Malformed If-Match"
    },
    "422": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "Validation failed"
    },
    "428": {
      "content": {
        "application/problem+json": {
          "schema": {
            "$ref": "#/components/schemas/Problem"
          }
        }
      },
      "description": "If-Match missing"
    }
  },
  "summary": "Update a product (If-Match); status `archived` archives it",
  "tags": [
    "catalog"
  ],
  "x-audit": "catalog.product.update",
  "x-body": "json",
  "x-body-limit": 524288,
  "x-concurrency": {
    "etag": true,
    "ifMatch": true
  },
  "x-idempotency": "optional",
  "x-permission": "catalog.product.manage",
  "effectiveSecurity": []
}

GET /api/v1/public/catalog

GET /api/v1/public/catalog

Products, plans and price options on sale (localized)

{
  "parameters": [
    {
      "description": "Preferred locale; before Accept-Language",
      "in": "query",
      "name": "locale",
      "required": false,
      "schema": {
        "description": "Preferred locale; before Accept-Language",
        "pattern": "^[a-z]{2,3}(-[A-Z][a-z]{3})?(-([A-Z]{2}|\\d{3}))?$",
        "type": "string"
      }
    }
  ],
  "responses": {
    "200": {
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/PublicCatalog"
          }
        }
      },
      "description": "The catalog, with Content-Language"
    }
  },
  "summary": "Products, plans and price options on sale (localized)",
  "tags": [
    "catalog"
  ],
  "x-body": "none",
  "x-body-limit": 0,
  "effectiveSecurity": []
}