diff --git a/docs/en/latest/admin-api.json b/docs/en/latest/admin-api.json new file mode 100644 index 000000000000..7148ccbef5e7 --- /dev/null +++ b/docs/en/latest/admin-api.json @@ -0,0 +1,16892 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "Apache APISIX Admin API", + "description": "The **Apache APISIX Admin API** is a RESTful interface for managing all APISIX gateway resources — routes, upstreams, services, consumers, SSL certificates, plugins, and more.\n\nAPISIX Admin API allows you to dynamically configure the API gateway without restarts. Accepted changes propagate to workers asynchronously.\n\nUnless API key authentication is explicitly disabled with `deployment.admin.admin_key_required: false`, endpoints require an **API key**. Pass a key configured under `deployment.admin.admin_key` in APISIX's `config.yaml` using the `X-API-KEY` request header.\n\nBy default, the Admin API binds to `0.0.0.0:9180` and allows clients from `127.0.0.0/24`. The examples use `http://127.0.0.1:9180`; configure the listener and client allowlist in `config.yaml` under `deployment.admin.admin_listen` and `deployment.admin.allow_admin`.\n\nTo learn how to configure and operate APISIX, see [Get Started](https://apisix.apache.org/docs/apisix/3.18/getting-started/README/).\n\nThis contract describes release/3.18 at commit 947d4b47d824657bba6508b1c0cb204a2ec01f82. Resource schemas describe structural validation; plugin-specific Lua checks, reference existence, certificate parsing, and deployment configuration impose additional constraints. The default v3 response representation is shown; legacy v2 response bodies differ.", + "version": "3.18.0", + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "tags": [ + { + "name": "Routes", + "description": "A **Route** defines how client requests are matched and forwarded to upstream services. Routes are the most fundamental resource in APISIX — every incoming request is evaluated against configured routes.\n\nA route must include at least one URI pattern through `uri` or `uris`, and one of: an inline `upstream`, an `upstream_id`, a `service_id`, `plugins`, or `script`.\n\n**Key Features**\n- Match by URI, host, HTTP method, headers, query parameters, and custom expressions\n- Attach plugins for authentication, rate limiting, transformation, and more\n- Support WebSocket proxying and gRPC transcoding\n- Priority-based routing when multiple routes match\n\nTo learn how to configure routes, see [Routes](https://apisix.apache.org/docs/apisix/3.18/terminology/route/).", + "x-displayName": "Routes" + }, + { + "name": "Upstreams", + "description": "An **Upstream** is a virtual host abstraction over a pool of backend service nodes. It defines how APISIX distributes traffic to your backend servers.\n\n**Load Balancing Algorithms**\n- `roundrobin` — Weighted round-robin (default)\n- `chash` — Consistent hashing for session affinity\n- `ewma` — Latency-based selection (exponentially weighted moving average)\n- `least_conn` — Fewest active connections\n\n**Health Checks**\nUpstreams support **active** and **passive** health checks to automatically remove unhealthy nodes from the load balancing pool.\n\nTo learn how to configure upstreams, see [Upstreams](https://apisix.apache.org/docs/apisix/3.18/terminology/upstream/).", + "x-displayName": "Upstreams" + }, + { + "name": "Services", + "description": "A **Service** is a reusable abstraction that groups an upstream and a set of plugins. Multiple routes can reference the same service, reducing configuration duplication.\n\nWhen a route references a service via `service_id`, the service's upstream and plugins are merged with the route's own configuration. Route-level settings take precedence.\n\nTo learn how to configure services, see [Services](https://apisix.apache.org/docs/apisix/3.18/terminology/service/).", + "x-displayName": "Services" + }, + { + "name": "Consumers", + "description": "A **Consumer** represents an API user, application, or host that consumes your APIs. Consumers are typically used with authentication plugins (e.g., `key-auth`, `jwt-auth`) to identify callers and apply per-consumer rate limits or access controls.\n\nEach consumer is uniquely identified by a `username` and can belong to a consumer group.\n\nTo learn how to configure consumers, see [Consumers](https://apisix.apache.org/docs/apisix/3.18/terminology/consumer/).", + "x-displayName": "Consumers" + }, + { + "name": "Consumer Groups", + "description": "A **Consumer Group** allows you to define shared plugin configurations that apply to multiple consumers. Instead of duplicating plugin settings across consumers, assign them to a group.\n\nConsumers reference a group via the `group_id` field. Group-level plugins are merged with consumer-level plugins, with consumer-level taking precedence.\n\nTo learn how to configure consumer groups, see [Consumer Groups](https://apisix.apache.org/docs/apisix/3.18/terminology/consumer-group/).", + "x-displayName": "Consumer Groups" + }, + { + "name": "Credentials", + "description": "A **Credential** stores authentication configuration associated with a consumer. Each credential can hold at most one authentication plugin (for example, `key-auth`, `basic-auth`, or `jwt-auth`).\n\nCredentials are sub-resources of consumers. A consumer can have multiple credentials for the same authentication plugin when their identifying values differ, or for different authentication plugins.\n\nTo learn how to configure consumer credentials, see [Credentials](https://apisix.apache.org/docs/apisix/3.18/terminology/credential/).", + "x-displayName": "Credentials" + }, + { + "name": "Plugins", + "description": "**Plugins** extend APISIX's core functionality with middleware capabilities such as authentication, traffic control, observability, and request/response transformation.\n\nUse these endpoints to query available plugins and their JSON schemas, or to hot-reload plugins without restarting APISIX.\n\nPlugin configurations can be attached to routes, services, consumers, consumer groups, plugin configs, global rules, and stream routes through the `plugins` object. Credentials can hold at most one authentication plugin configuration.\n\nSee [Plugin Hub](https://apisix.apache.org/plugins/) for the full list of available plugins.", + "x-displayName": "Plugins" + }, + { + "name": "Plugin Configs", + "description": "A **Plugin Config** is a reusable set of plugin configurations that can be shared across multiple routes. Instead of duplicating plugin settings in each route, create a plugin config and reference it via `plugin_config_id`.\n\nRoute-level plugins are merged with plugin config plugins, with route-level taking precedence.\n\nTo learn how to configure reusable plugin configurations, see [Plugin Configs](https://apisix.apache.org/docs/apisix/3.18/terminology/plugin-config/).", + "x-displayName": "Plugin Configs" + }, + { + "name": "Plugin Metadata", + "description": "**Plugin Metadata** configures shared settings for instances of a specific plugin across the APISIX cluster. A plugin can allow an individual instance to override a metadata setting. This is useful for plugins that need global configuration, such as log format templates.\n\nFor example, metadata for `http-logger` supplies a shared `log_format`; an instance-level `log_format` takes precedence.\n\nTo learn how to configure plugin metadata, see [Plugin Metadata](https://apisix.apache.org/docs/apisix/3.18/terminology/plugin-metadata/).", + "x-displayName": "Plugin Metadata" + }, + { + "name": "Global Rules", + "description": "A Global Rule applies plugins to incoming requests independently of route and service configuration. For HTTP requests, global and local plugins are interleaved by phase: global `rewrite`, local `rewrite`, global `access`, then local `access`. Plugin priority applies within each phase and scope.\n\nTo learn how to configure global rules, see [Plugin Global Rules](https://apisix.apache.org/docs/apisix/3.18/terminology/global-rule/).", + "x-displayName": "Global Rules" + }, + { + "name": "Stream Routes", + "description": "A **Stream Route** defines TCP/UDP layer (L4) proxying rules. Unlike HTTP routes, stream routes operate at the transport layer and can proxy raw TCP and UDP connections.\n\nStream routes match traffic by `remote_addr`, `server_addr`, `server_port`, or `sni` (for TLS connections).\n\n**Note:** Stream mode must be enabled in `config.yaml` (`apisix.proxy_mode: stream` or `http&stream`) before creating stream routes.\n\nTo learn how to configure stream routes, see [Stream Routes](https://apisix.apache.org/docs/apisix/3.18/stream-proxy/).", + "x-displayName": "Stream Routes" + }, + { + "name": "SSL Certificates", + "description": "The **SSL** resource manages TLS certificates for HTTPS termination and mTLS authentication.\n\n**Certificate Types**\n- `server` — Server certificate for HTTPS termination (matched by SNI)\n- `client` — Client certificate for mTLS with upstream services\n\nAPISIX dynamically selects the correct certificate based on the SNI (Server Name Indication) in the TLS handshake. Certificates can be rotated without restart.\n\nTo learn how to configure TLS certificates, see [SSL Certificates](https://apisix.apache.org/docs/apisix/3.18/certificate/).", + "x-displayName": "SSL Certificates" + }, + { + "name": "Secrets", + "description": "The **Secret** resource integrates APISIX with external secret managers (e.g., HashiCorp Vault, AWS Secrets Manager) for dynamic secret retrieval at runtime.\n\nOnce configured, you can reference a provider value through an APISIX Secret resource. For example, `$secret://vault/1/my-secret/key` uses the Vault Secret resource `1` to retrieve `key` from `my-secret`.\n\nTo learn how to configure secret managers, see [Secrets](https://apisix.apache.org/docs/apisix/3.18/terminology/secret/).", + "x-displayName": "Secrets" + }, + { + "name": "Protos", + "description": "The **Proto** resource stores Protocol Buffer (protobuf) `.proto` file content used by the `grpc-transcode` plugin to convert between HTTP/JSON and gRPC.\n\nUpload your `.proto` definitions here, then reference them by ID in the `grpc-transcode` plugin configuration.\n\nTo learn how to configure gRPC transcoding, see [gRPC Transcode](https://apisix.apache.org/docs/apisix/3.18/plugins/grpc-transcode/).", + "x-displayName": "Protos" + }, + { + "name": "Schema Validation", + "description": "Endpoints for validating one resource or a complete declarative configuration without creating, persisting, or applying it.", + "x-displayName": "Schema Validation" + }, + { + "name": "Health", + "description": "Liveness probe." + }, + { + "name": "Standalone", + "description": "Endpoints exposed only when the deployment uses the YAML config provider (`deployment.role_data_plane.config_provider: yaml`)." + } + ], + "paths": { + "/apisix/admin/routes": { + "get": { + "summary": "List All Routes", + "description": "Retrieve all configured routes. Supports filtering by `name`, `label`, `uri`, or `filter` expression.\n\nResults include full route configuration and metadata (creation/update timestamps).", + "tags": [ + "Routes" + ], + "parameters": [ + { + "$ref": "#/components/parameters/NameFilter" + }, + { + "$ref": "#/components/parameters/LabelFilter" + }, + { + "$ref": "#/components/parameters/UriFilter" + }, + { + "$ref": "#/components/parameters/FilterExpr" + }, + { + "$ref": "#/components/parameters/Page" + }, + { + "$ref": "#/components/parameters/PageSize" + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RouteListEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "total": 1, + "list": [ + { + "createdIndex": 203, + "key": "/apisix/routes/1", + "value": { + "remote_addrs": [ + "127.0.0.1" + ], + "desc": "desc", + "name": "route-test", + "priority": 0, + "vars": [ + [ + "http_user", + "==", + "ios" + ] + ], + "update_time": 1684490006, + "create_time": 1684490006, + "id": "1", + "plugins": { + "limit-count": { + "show_limit_quota_header": true, + "policy": "local", + "count": 2, + "allow_degradation": false, + "rejected_code": 503, + "time_window": 60, + "key_type": "var", + "key": "remote_addr" + } + }, + "status": 1, + "timeout": { + "read": 3, + "connect": 3, + "send": 3 + }, + "uri": "/ip", + "upstream": { + "hash_on": "vars", + "pass_host": "pass", + "nodes": { + "httpbin.org:80": 1 + }, + "type": "roundrobin", + "scheme": "http" + }, + "methods": [ + "GET", + "POST" + ], + "hosts": [ + "a.com", + "b.com" + ] + }, + "modifiedIndex": 203 + } + ] + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "listRoutes", + "x-codeSamples": [ + { + "lang": "cURL", + "label": "cURL", + "source": "curl -i http://127.0.0.1:9180/apisix/admin/routes \\\n -H 'X-API-KEY: your-api-key'" + } + ] + }, + "post": { + "summary": "Create a Route", + "description": "Create a new route with an auto-generated ID.\n\nA route must include at least a `uri` and one of: `upstream` (inline), `upstream_id`, `service_id`, `plugins`, or a Lua `script`.", + "tags": [ + "Routes" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/RouteUpsertPostCollection" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RouteEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "value": { + "desc": "desc", + "name": "route-name", + "update_time": 1684490245, + "priority": 0, + "id": "00000000000000000206", + "status": 1, + "create_time": 1684490245, + "uri": "/ip", + "upstream": { + "hash_on": "vars", + "pass_host": "pass", + "nodes": { + "httpbin.org:80": 1 + }, + "type": "roundrobin", + "scheme": "http" + }, + "methods": [ + "GET", + "POST" + ], + "hosts": [ + "a.com", + "b.com" + ] + }, + "key": "/apisix/routes/00000000000000000206" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "createRoute", + "x-codeSamples": [ + { + "lang": "cURL", + "label": "cURL", + "source": "curl -i http://127.0.0.1:9180/apisix/admin/routes \\\n -H 'X-API-KEY: your-api-key' \\\n -H 'Content-Type: application/json' \\\n -X POST -d '{\n \"uri\": \"/api/v1/users/*\",\n \"upstream\": {\n \"type\": \"roundrobin\",\n \"nodes\": {\"10.0.0.1:8080\": 1}\n }\n }'" + } + ] + }, + "put": { + "summary": "Create or replace routes using a body ID", + "description": "Equivalent to PUT on the item URL, with id required in the request body. The resource handler injects and validates this ID before persistence.", + "tags": [ + "Routes" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "description": "`Route` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "allOf": [ + { + "$ref": "#/components/schemas/Route" + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + }, + { + "required": [ + "id" + ] + } + ] + }, + "example": { + "name": "payments-route", + "desc": "Route for payment processing API", + "uri": "/api/v1/payments", + "methods": [ + "POST" + ], + "plugins": { + "key-auth": {}, + "limit-count": { + "count": 10, + "time_window": 60, + "rejected_code": 429, + "key_type": "var", + "key": "consumer_name" + } + }, + "upstream": { + "type": "roundrobin", + "nodes": { + "payments-service:8080": 1 + } + }, + "timeout": { + "connect": 10, + "send": 10, + "read": 30 + }, + "id": "1" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RouteEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "value": { + "remote_addrs": [ + "127.0.0.1" + ], + "desc": "desc", + "name": "route-test", + "priority": 0, + "plugins": { + "limit-count": { + "show_limit_quota_header": true, + "policy": "local", + "count": 2, + "key": "remote_addr", + "rejected_code": 503, + "time_window": 60, + "key_type": "var", + "allow_degradation": false + } + }, + "vars": [ + [ + "http_user", + "==", + "ios" + ] + ], + "id": "1", + "status": 1, + "uri": "/ip", + "timeout": { + "connect": 3, + "read": 3, + "send": 3 + }, + "upstream": { + "hash_on": "vars", + "pass_host": "pass", + "nodes": { + "httpbin.org:80": 1 + }, + "type": "roundrobin", + "scheme": "http" + }, + "methods": [ + "GET", + "POST" + ], + "hosts": [ + "a.com", + "b.com" + ] + }, + "key": "/apisix/routes/1" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "putRouteWithBodyId" + } + }, + "/apisix/admin/routes/{id}": { + "put": { + "summary": "Create or Replace a Route", + "description": "Create a new route with a specified ID, or fully replace an existing route.\n\n**Note:** This is a full replacement — all fields must be provided. Use `PATCH` for partial updates.", + "tags": [ + "Routes" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/RouteUpsertPutId" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RouteEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "value": { + "remote_addrs": [ + "127.0.0.1" + ], + "desc": "desc", + "name": "route-test", + "priority": 0, + "plugins": { + "limit-count": { + "show_limit_quota_header": true, + "policy": "local", + "count": 2, + "key": "remote_addr", + "rejected_code": 503, + "time_window": 60, + "key_type": "var", + "allow_degradation": false + } + }, + "vars": [ + [ + "http_user", + "==", + "ios" + ] + ], + "id": "1", + "status": 1, + "uri": "/ip", + "timeout": { + "connect": 3, + "read": 3, + "send": 3 + }, + "upstream": { + "hash_on": "vars", + "pass_host": "pass", + "nodes": { + "httpbin.org:80": 1 + }, + "type": "roundrobin", + "scheme": "http" + }, + "methods": [ + "GET", + "POST" + ], + "hosts": [ + "a.com", + "b.com" + ] + }, + "key": "/apisix/routes/1" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "createRouteById", + "x-codeSamples": [ + { + "lang": "cURL", + "label": "cURL", + "source": "curl -i http://127.0.0.1:9180/apisix/admin/routes/1 \\\n -H 'X-API-KEY: your-api-key' \\\n -H 'Content-Type: application/json' \\\n -X PUT -d '{\n \"uri\": \"/api/v1/payments\",\n \"methods\": [\"POST\"],\n \"upstream\": {\n \"type\": \"roundrobin\",\n \"nodes\": {\"payments-service:8080\": 1}\n }\n }'" + } + ] + }, + "get": { + "summary": "Get a Route", + "description": "Retrieve a single route by its ID.", + "tags": [ + "Routes" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RouteEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "createdIndex": 203, + "key": "/apisix/routes/1", + "value": { + "remote_addrs": [ + "127.0.0.1" + ], + "desc": "desc", + "name": "route-test", + "priority": 0, + "vars": [ + [ + "http_user", + "==", + "ios" + ] + ], + "update_time": 1684490006, + "create_time": 1684490006, + "id": "1", + "plugins": { + "limit-count": { + "show_limit_quota_header": true, + "policy": "local", + "count": 2, + "allow_degradation": false, + "rejected_code": 503, + "time_window": 60, + "key_type": "var", + "key": "remote_addr" + } + }, + "status": 1, + "timeout": { + "read": 3, + "connect": 3, + "send": 3 + }, + "uri": "/ip", + "upstream": { + "hash_on": "vars", + "pass_host": "pass", + "nodes": { + "httpbin.org:80": 1 + }, + "type": "roundrobin", + "scheme": "http" + }, + "methods": [ + "GET", + "POST" + ], + "hosts": [ + "a.com", + "b.com" + ] + }, + "modifiedIndex": 203 + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "getRoute", + "x-codeSamples": [ + { + "lang": "cURL", + "label": "cURL", + "source": "curl -i http://127.0.0.1:9180/apisix/admin/routes/1 \\\n -H 'X-API-KEY: your-api-key'" + } + ] + }, + "patch": { + "summary": "Update a Route (Partial)", + "description": "Partially update a route's configuration. Only the fields included in the request body are modified; all other fields remain unchanged.\n\n**Tip:** Use this to update a single field (e.g., change the upstream) without resending the entire route configuration.", + "tags": [ + "Routes" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/RouteUpsertPatchId" + }, + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RouteEnvelope" + }, + "examples": { + "1": { + "summary": "Update Upstream Node To The Route", + "value": { + "value": { + "remote_addrs": [ + "127.0.0.1" + ], + "desc": "desc", + "name": "route-test", + "priority": 0, + "vars": [ + [ + "http_user", + "==", + "ios" + ] + ], + "update_time": 1684490363, + "create_time": 1684490006, + "id": "1", + "plugins": { + "limit-count": { + "show_limit_quota_header": true, + "policy": "local", + "count": 2, + "allow_degradation": false, + "rejected_code": 503, + "time_window": 60, + "key_type": "var", + "key": "remote_addr" + } + }, + "status": 1, + "timeout": { + "read": 3, + "connect": 3, + "send": 3 + }, + "uri": "/ip", + "upstream": { + "hash_on": "vars", + "pass_host": "pass", + "nodes": { + "httpbin.org:80": 1, + "127.0.0.1:1981": 1 + }, + "type": "roundrobin", + "scheme": "http" + }, + "methods": [ + "GET", + "POST" + ], + "hosts": [ + "a.com", + "b.com" + ] + }, + "key": "/apisix/routes/1" + } + }, + "2": { + "summary": "Delete An Upstream Node For The Route", + "value": { + "value": { + "remote_addrs": [ + "127.0.0.1" + ], + "desc": "desc", + "name": "route-test", + "vars": [ + [ + "http_user", + "==", + "ios" + ] + ], + "priority": 0, + "update_time": 1684490476, + "create_time": 1684490006, + "plugins": { + "limit-count": { + "show_limit_quota_header": true, + "policy": "local", + "count": 2, + "key": "remote_addr", + "rejected_code": 503, + "time_window": 60, + "key_type": "var", + "allow_degradation": false + } + }, + "id": "1", + "status": 1, + "uri": "/ip", + "timeout": { + "connect": 3, + "read": 3, + "send": 3 + }, + "upstream": { + "hash_on": "vars", + "pass_host": "pass", + "nodes": { + "httpbin.org:80": 1 + }, + "type": "roundrobin", + "scheme": "http" + }, + "methods": [ + "GET", + "POST" + ], + "hosts": [ + "a.com", + "b.com" + ] + }, + "key": "/apisix/routes/1" + } + }, + "3": { + "summary": "Disable Route", + "value": { + "value": { + "remote_addrs": [ + "127.0.0.1" + ], + "vars": [ + [ + "http_user", + "==", + "ios" + ] + ], + "name": "route-test", + "desc": "desc", + "priority": 0, + "update_time": 1684490503, + "create_time": 1684490006, + "id": "1", + "plugins": { + "limit-count": { + "show_limit_quota_header": true, + "policy": "local", + "count": 2, + "allow_degradation": false, + "rejected_code": 503, + "time_window": 60, + "key_type": "var", + "key": "remote_addr" + } + }, + "status": 0, + "timeout": { + "read": 3, + "connect": 3, + "send": 3 + }, + "uri": "/ip", + "upstream": { + "hash_on": "vars", + "pass_host": "pass", + "nodes": { + "httpbin.org:80": 1 + }, + "type": "roundrobin", + "scheme": "http" + }, + "methods": [ + "GET", + "POST" + ], + "hosts": [ + "a.com", + "b.com" + ] + }, + "key": "/apisix/routes/1" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "updateRoute" + }, + "delete": { + "summary": "Delete a Route", + "description": "Delete a route by its ID. This action is irreversible.", + "tags": [ + "Routes" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + }, + { + "name": "force", + "in": "query", + "description": "Delete resource by force even if the resource is in use.", + "required": false, + "example": "true", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "key": { + "description": "Unique key identifier for this route in the data store.", + "type": "string" + }, + "deleted": { + "description": "Number of deleted keys, serialized as a string for compatibility.", + "type": "string" + } + } + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "deleted": "1", + "key": "/apisix/routes/1" + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Missing identifier or resource is still referenced (where dependency checks apply)." + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "deleteRoute", + "x-codeSamples": [ + { + "lang": "cURL", + "label": "cURL", + "source": "curl -i http://127.0.0.1:9180/apisix/admin/routes/1 \\\n -H 'X-API-KEY: your-api-key' -X DELETE" + } + ] + } + }, + "/apisix/admin/services": { + "get": { + "summary": "List All Services", + "description": "Retrieve all configured services.", + "tags": [ + "Services" + ], + "parameters": [ + { + "$ref": "#/components/parameters/NameFilter" + }, + { + "$ref": "#/components/parameters/LabelFilter" + }, + { + "$ref": "#/components/parameters/Page" + }, + { + "$ref": "#/components/parameters/PageSize" + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServiceListEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "total": 1, + "list": [ + { + "createdIndex": 117, + "key": "/apisix/services/1", + "value": { + "enable_websocket": true, + "name": "service-test", + "update_time": 1684288908, + "create_time": 1684287870, + "id": "1", + "desc": "hello world", + "upstream": { + "hash_on": "vars", + "pass_host": "pass", + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin", + "scheme": "http" + }, + "plugins": { + "limit-count": { + "show_limit_quota_header": true, + "policy": "local", + "count": 2, + "key": "remote_addr", + "rejected_code": 503, + "time_window": 60, + "key_type": "var", + "allow_degradation": false + } + }, + "hosts": [ + "foo.com" + ] + }, + "modifiedIndex": 118 + } + ] + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "listServices" + }, + "post": { + "summary": "Create a Service", + "description": "Create a new service with an auto-generated ID.", + "tags": [ + "Services" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/ServiceUpsertPostCollection" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServiceEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "value": { + "enable_websocket": true, + "name": "service-test", + "update_time": 1684491069, + "create_time": 1684491069, + "plugins": {}, + "id": "00000000000000000218", + "upstream": { + "hash_on": "vars", + "pass_host": "pass", + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin", + "scheme": "http" + }, + "desc": "hello world", + "hosts": [ + "foo.com" + ] + }, + "key": "/apisix/services/00000000000000000218" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "createService" + }, + "put": { + "summary": "Create or replace services using a body ID", + "description": "Equivalent to PUT on the item URL, with id required in the request body. The resource handler injects and validates this ID before persistence.", + "tags": [ + "Services" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "description": "`Service` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "allOf": [ + { + "$ref": "#/components/schemas/Service" + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + }, + { + "required": [ + "id" + ] + } + ] + }, + "example": { + "name": "user-service", + "desc": "User management microservice", + "upstream": { + "type": "roundrobin", + "nodes": { + "user-svc:8080": 1 + } + }, + "plugins": { + "limit-count": { + "count": 200, + "time_window": 60, + "rejected_code": 429, + "key_type": "var", + "key": "remote_addr" + } + }, + "id": "1" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServiceEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "value": { + "desc": "hello world", + "name": "service-test", + "update_time": 1684491005, + "create_time": 1684287870, + "plugins": { + "limit-count": { + "show_limit_quota_header": true, + "policy": "local", + "count": 2, + "key": "remote_addr", + "rejected_code": 503, + "time_window": 60, + "key_type": "var", + "allow_degradation": false + } + }, + "id": "1", + "upstream": { + "hash_on": "vars", + "pass_host": "pass", + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin", + "scheme": "http" + }, + "enable_websocket": true, + "hosts": [ + "foo.com" + ] + }, + "key": "/apisix/services/1" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "putServiceWithBodyId" + } + }, + "/apisix/admin/services/{id}": { + "get": { + "summary": "Get a Service", + "description": "Retrieve a single service by its ID.", + "tags": [ + "Services" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServiceEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "createdIndex": 117, + "key": "/apisix/services/1", + "value": { + "enable_websocket": true, + "name": "service-test", + "update_time": 1684490934, + "create_time": 1684287870, + "plugins": { + "limit-count": { + "show_limit_quota_header": true, + "policy": "local", + "count": 2, + "allow_degradation": false, + "rejected_code": 503, + "time_window": 60, + "key_type": "var", + "key": "remote_addr" + } + }, + "desc": "hello world", + "upstream": { + "hash_on": "vars", + "pass_host": "pass", + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin", + "scheme": "http" + }, + "id": "1", + "hosts": [ + "foo.com" + ] + }, + "modifiedIndex": 216 + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "getService" + }, + "put": { + "summary": "Create or Replace a Service", + "description": "Create a new service with a specified ID, or fully replace an existing service.", + "tags": [ + "Services" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/ServiceUpsertPutId" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServiceEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "value": { + "desc": "hello world", + "name": "service-test", + "update_time": 1684491005, + "create_time": 1684287870, + "plugins": { + "limit-count": { + "show_limit_quota_header": true, + "policy": "local", + "count": 2, + "key": "remote_addr", + "rejected_code": 503, + "time_window": 60, + "key_type": "var", + "allow_degradation": false + } + }, + "id": "1", + "upstream": { + "hash_on": "vars", + "pass_host": "pass", + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin", + "scheme": "http" + }, + "enable_websocket": true, + "hosts": [ + "foo.com" + ] + }, + "key": "/apisix/services/1" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "createServiceById" + }, + "patch": { + "summary": "Update a Service (Partial)", + "description": "Partially update a service's configuration.", + "tags": [ + "Services" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/ServiceUpsertPatchId" + }, + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServiceEnvelope" + }, + "examples": { + "1": { + "summary": "Update Upstream Node To The Service", + "value": { + "value": { + "upstream": { + "scheme": "http", + "nodes": { + "127.0.0.1:1981": 1, + "127.0.0.1:1980": 1 + }, + "type": "roundrobin", + "hash_on": "vars", + "pass_host": "pass" + }, + "create_time": 1684142130, + "enable_websocket": true, + "id": "1", + "update_time": 1684142459, + "hosts": [ + "foo.com" + ], + "name": "service-test", + "plugins": {}, + "desc": "hello world" + }, + "key": "/apisix/services/1" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "updateService" + }, + "delete": { + "summary": "Delete a Service", + "description": "Delete a service by its ID.", + "tags": [ + "Services" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + }, + { + "name": "force", + "in": "query", + "description": "Delete resource by force even if the resource is in use.", + "required": false, + "example": "true", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "deleted": { + "description": "Number of deleted keys, serialized as a string for compatibility.", + "type": "string" + }, + "key": { + "description": "Unique key identifier for this service.", + "type": "string" + } + } + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "deleted": "1", + "key": "/apisix/services/1" + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Missing identifier or resource is still referenced (where dependency checks apply)." + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "deleteService" + } + }, + "/apisix/admin/consumers": { + "put": { + "summary": "Create or Update a Consumer", + "description": "Create a new consumer or update an existing one. The consumer is identified by `username` in the request body.", + "tags": [ + "Consumers" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/ConsumerUpsertPutCollection" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "key": { + "description": "Unique key identifier for this consumer.", + "type": "string" + }, + "value": { + "description": "Consumer values.", + "properties": { + "plugins": { + "description": "Key-value pairs of plugins and their configurations on the object.", + "type": "object", + "properties": {} + }, + "username": { + "description": "The unique identifier of the consumer.", + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[a-zA-Z0-9_-]+$" + }, + "labels": { + "type": "object", + "patternProperties": { + ".*": { + "minLength": 1, + "maxLength": 64, + "pattern": "^\\S+$", + "type": "string", + "description": "Value of label." + } + }, + "description": "Key-value pairs to specify attributes.", + "properties": {} + }, + "desc": { + "description": "Consumer description.", + "maxLength": 256, + "type": "string" + }, + "group_id": { + "description": "Consumer group ID.", + "anyOf": [ + { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + }, + { + "minimum": 1, + "type": "integer" + } + ] + }, + "create_time": { + "description": "Creation time of the consumer.", + "type": "integer" + } + }, + "type": "object" + } + } + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/consumers/jack", + "value": { + "plugins": { + "limit-count": { + "time_window": 60, + "rejected_code": 503, + "key": "remote_addr", + "key_type": "var", + "count": 2, + "show_limit_quota_header": true, + "allow_degradation": false, + "policy": "local" + } + }, + "username": "jack", + "create_time": 1683164776, + "update_time": 1684289832 + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "createConsumer", + "x-codeSamples": [ + { + "lang": "cURL", + "label": "cURL", + "source": "curl -i http://127.0.0.1:9180/apisix/admin/consumers \\\n -H 'X-API-KEY: your-api-key' \\\n -H 'Content-Type: application/json' \\\n -X PUT -d '{\n \"username\": \"my-app\",\n \"plugins\": {\n \"key-auth\": {\"key\": \"app-secret-key-123\"}\n }\n }'" + } + ] + }, + "get": { + "summary": "List All Consumers", + "description": "Retrieve all configured consumers.", + "tags": [ + "Consumers" + ], + "parameters": [ + { + "$ref": "#/components/parameters/LabelFilter" + }, + { + "$ref": "#/components/parameters/Page" + }, + { + "$ref": "#/components/parameters/PageSize" + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConsumerListEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "list": [ + { + "value": { + "plugins": { + "limit-count": { + "rejected_code": 503, + "time_window": 60, + "key": "remote_addr", + "key_type": "var", + "count": 2, + "allow_degradation": false, + "show_limit_quota_header": true, + "policy": "local" + } + }, + "username": "jack", + "create_time": 1683164776, + "update_time": 1684289832 + }, + "key": "/apisix/consumers/jack", + "modifiedIndex": 119, + "createdIndex": 17 + } + ], + "total": 1 + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "listConsumers" + } + }, + "/apisix/admin/consumers/{username}": { + "get": { + "summary": "Get a Consumer", + "description": "Retrieve a single consumer by username.", + "tags": [ + "Consumers" + ], + "parameters": [ + { + "$ref": "#/components/parameters/UsernameInPath" + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConsumerEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "value": { + "plugins": { + "limit-count": { + "rejected_code": 503, + "time_window": 60, + "key": "remote_addr", + "key_type": "var", + "count": 2, + "allow_degradation": false, + "show_limit_quota_header": true, + "policy": "local" + } + }, + "username": "jack", + "create_time": 1683164776, + "update_time": 1684289832 + }, + "key": "/apisix/consumers/jack", + "modifiedIndex": 119, + "createdIndex": 17 + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "getConsumer" + }, + "delete": { + "summary": "Delete a Consumer", + "description": "Delete a consumer by username. This also deletes all associated credentials.", + "tags": [ + "Consumers" + ], + "parameters": [ + { + "$ref": "#/components/parameters/UsernameInPath" + }, + { + "name": "force", + "in": "query", + "description": "Delete resource by force even if the resource is in use.", + "required": false, + "example": "true", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "key": { + "description": "Unique key identifier for this consumer in the data store.", + "type": "string" + }, + "deleted": { + "description": "Number of deleted keys, serialized as a string for compatibility.", + "type": "string" + } + } + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/consumers/jack", + "deleted": "1" + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Missing identifier or resource is still referenced (where dependency checks apply)." + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "deleteConsumer" + }, + "put": { + "summary": "Create or replace a consumer by username", + "description": "The request body username must match the URL username.", + "tags": [ + "Consumers" + ], + "parameters": [ + { + "$ref": "#/components/parameters/UsernameInPath" + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/ConsumerUpsertPutCollection" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "key": { + "description": "Unique key identifier for this consumer.", + "type": "string" + }, + "value": { + "description": "Consumer values.", + "properties": { + "plugins": { + "description": "Key-value pairs of plugins and their configurations on the object.", + "type": "object", + "properties": {} + }, + "username": { + "description": "The unique identifier of the consumer.", + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[a-zA-Z0-9_-]+$" + }, + "labels": { + "type": "object", + "patternProperties": { + ".*": { + "minLength": 1, + "maxLength": 64, + "pattern": "^\\S+$", + "type": "string", + "description": "Value of label." + } + }, + "description": "Key-value pairs to specify attributes.", + "properties": {} + }, + "desc": { + "description": "Consumer description.", + "maxLength": 256, + "type": "string" + }, + "group_id": { + "description": "Consumer group ID.", + "anyOf": [ + { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + }, + { + "minimum": 1, + "type": "integer" + } + ] + }, + "create_time": { + "description": "Creation time of the consumer.", + "type": "integer" + } + }, + "type": "object" + } + } + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/consumers/jack", + "value": { + "plugins": { + "limit-count": { + "time_window": 60, + "rejected_code": 503, + "key": "remote_addr", + "key_type": "var", + "count": 2, + "show_limit_quota_header": true, + "allow_degradation": false, + "policy": "local" + } + }, + "username": "jack", + "create_time": 1683164776, + "update_time": 1684289832 + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "putConsumerByUsername" + } + }, + "/apisix/admin/upstreams": { + "get": { + "summary": "List All Upstreams", + "description": "Retrieve all configured upstreams.", + "tags": [ + "Upstreams" + ], + "parameters": [ + { + "$ref": "#/components/parameters/NameFilter" + }, + { + "$ref": "#/components/parameters/LabelFilter" + }, + { + "$ref": "#/components/parameters/Page" + }, + { + "$ref": "#/components/parameters/PageSize" + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpstreamListEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "list": [ + { + "value": { + "timeout": { + "send": 15, + "read": 15, + "connect": 15 + }, + "desc": "desc", + "id": "1", + "create_time": 1684290697, + "retries": 1, + "pass_host": "pass", + "update_time": 1684291419, + "type": "roundrobin", + "name": "upstream-for-test", + "hash_on": "vars", + "nodes": { + "httpbin.org:443": 1 + }, + "scheme": "https" + }, + "key": "/apisix/upstreams/1", + "modifiedIndex": 126, + "createdIndex": 121 + }, + { + "value": { + "timeout": { + "send": 15, + "read": 15, + "connect": 15 + }, + "desc": "desc", + "id": "2", + "create_time": 1684291436, + "retries": 1, + "pass_host": "pass", + "update_time": 1684291436, + "type": "roundrobin", + "name": "upstream-for-test", + "hash_on": "vars", + "nodes": { + "httpbin.org:443": 1 + }, + "scheme": "https" + }, + "key": "/apisix/upstreams/2", + "modifiedIndex": 127, + "createdIndex": 127 + } + ], + "total": 2 + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "listUpstreams" + }, + "post": { + "summary": "Create an Upstream", + "description": "Create a new upstream with an auto-generated ID.", + "tags": [ + "Upstreams" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/UpstreamUpsertPostCollection" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpstreamEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/upstreams/00000000000000000128", + "value": { + "timeout": { + "send": 15, + "read": 15, + "connect": 15 + }, + "desc": "desc", + "id": "00000000000000000128", + "create_time": 1684291897, + "retries": 1, + "pass_host": "pass", + "update_time": 1684291897, + "type": "roundrobin", + "name": "upstream-for-test", + "hash_on": "vars", + "nodes": { + "127.0.0.1:1980": 1 + }, + "scheme": "http" + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "createUpstream" + }, + "put": { + "summary": "Create or replace upstreams using a body ID", + "description": "Equivalent to PUT on the item URL, with id required in the request body. The resource handler injects and validates this ID before persistence.", + "tags": [ + "Upstreams" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "description": "`Upstream` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "allOf": [ + { + "$ref": "#/components/schemas/Upstream" + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + }, + { + "required": [ + "id" + ] + } + ] + }, + "example": { + "name": "backend-cluster", + "desc": "Primary backend service cluster", + "type": "roundrobin", + "nodes": { + "10.0.0.1:8080": 3, + "10.0.0.2:8080": 2, + "10.0.0.3:8080": 1 + }, + "retries": 3, + "retry_timeout": 5, + "timeout": { + "connect": 6, + "send": 6, + "read": 6 + }, + "checks": { + "active": { + "type": "http", + "http_path": "/health", + "healthy": { + "interval": 5, + "successes": 2 + }, + "unhealthy": { + "interval": 3, + "http_failures": 3, + "tcp_failures": 2 + } + } + }, + "scheme": "http", + "pass_host": "pass", + "id": "1" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpstreamEnvelope" + }, + "examples": { + "1": { + "summary": "Create an Upstream by ID", + "value": { + "key": "/apisix/upstreams/1", + "value": { + "timeout": { + "send": 15, + "read": 15, + "connect": 15 + }, + "desc": "desc", + "id": "1", + "create_time": 1684290697, + "retries": 1, + "pass_host": "pass", + "update_time": 1684290697, + "type": "roundrobin", + "name": "upstream-for-test", + "hash_on": "vars", + "nodes": { + "127.0.0.1:1980": 1 + }, + "scheme": "http" + } + } + }, + "2": { + "summary": "Create HTTPS Upstream", + "value": { + "key": "/apisix/upstreams/1", + "value": { + "timeout": { + "send": 15, + "read": 15, + "connect": 15 + }, + "desc": "desc", + "id": "1", + "create_time": 1684290697, + "retries": 1, + "pass_host": "pass", + "update_time": 1684291419, + "type": "roundrobin", + "name": "upstream-for-test", + "hash_on": "vars", + "nodes": { + "httpbin.org:443": 1 + }, + "scheme": "https" + } + } + }, + "3": { + "summary": "Enable Health Check", + "value": { + "key": "/apisix/upstreams/1", + "value": { + "hash_on": "vars", + "desc": "desc", + "id": "1", + "create_time": 1684292372, + "checks": { + "passive": { + "type": "http", + "healthy": { + "http_statuses": [ + 200, + 201 + ], + "successes": 3 + }, + "unhealthy": { + "timeouts": 7, + "http_statuses": [ + 500 + ], + "http_failures": 3, + "tcp_failures": 3 + } + }, + "active": { + "host": "foo.com", + "concurrency": 10, + "http_path": "/status", + "https_verify_certificate": true, + "type": "http", + "req_headers": [ + "User-Agent: curl/7.29.0" + ], + "healthy": { + "successes": 1, + "http_statuses": [ + 200, + 302 + ], + "interval": 2 + }, + "timeout": 5, + "unhealthy": { + "timeouts": 3, + "http_statuses": [ + 429, + 404, + 500, + 501, + 502, + 503, + 504, + 505 + ], + "interval": 1, + "http_failures": 2, + "tcp_failures": 2 + } + } + }, + "pass_host": "pass", + "update_time": 1684292372, + "type": "roundrobin", + "name": "upstream-for-test", + "retries": 1, + "nodes": { + "127.0.0.1:1980": 1, + "127.0.0.1:1970": 1 + }, + "scheme": "http" + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "putUpstreamWithBodyId" + } + }, + "/apisix/admin/upstreams/{id}": { + "put": { + "summary": "Create or Replace an Upstream", + "description": "Create a new upstream with a specified ID, or fully replace an existing upstream.", + "tags": [ + "Upstreams" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/UpstreamUpsertPutId" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpstreamEnvelope" + }, + "examples": { + "1": { + "summary": "Create an Upstream by ID", + "value": { + "key": "/apisix/upstreams/1", + "value": { + "timeout": { + "send": 15, + "read": 15, + "connect": 15 + }, + "desc": "desc", + "id": "1", + "create_time": 1684290697, + "retries": 1, + "pass_host": "pass", + "update_time": 1684290697, + "type": "roundrobin", + "name": "upstream-for-test", + "hash_on": "vars", + "nodes": { + "127.0.0.1:1980": 1 + }, + "scheme": "http" + } + } + }, + "2": { + "summary": "Create HTTPS Upstream", + "value": { + "key": "/apisix/upstreams/1", + "value": { + "timeout": { + "send": 15, + "read": 15, + "connect": 15 + }, + "desc": "desc", + "id": "1", + "create_time": 1684290697, + "retries": 1, + "pass_host": "pass", + "update_time": 1684291419, + "type": "roundrobin", + "name": "upstream-for-test", + "hash_on": "vars", + "nodes": { + "httpbin.org:443": 1 + }, + "scheme": "https" + } + } + }, + "3": { + "summary": "Enable Health Check", + "value": { + "key": "/apisix/upstreams/1", + "value": { + "hash_on": "vars", + "desc": "desc", + "id": "1", + "create_time": 1684292372, + "checks": { + "passive": { + "type": "http", + "healthy": { + "http_statuses": [ + 200, + 201 + ], + "successes": 3 + }, + "unhealthy": { + "timeouts": 7, + "http_statuses": [ + 500 + ], + "http_failures": 3, + "tcp_failures": 3 + } + }, + "active": { + "host": "foo.com", + "concurrency": 10, + "http_path": "/status", + "https_verify_certificate": true, + "type": "http", + "req_headers": [ + "User-Agent: curl/7.29.0" + ], + "healthy": { + "successes": 1, + "http_statuses": [ + 200, + 302 + ], + "interval": 2 + }, + "timeout": 5, + "unhealthy": { + "timeouts": 3, + "http_statuses": [ + 429, + 404, + 500, + 501, + 502, + 503, + 504, + 505 + ], + "interval": 1, + "http_failures": 2, + "tcp_failures": 2 + } + } + }, + "pass_host": "pass", + "update_time": 1684292372, + "type": "roundrobin", + "name": "upstream-for-test", + "retries": 1, + "nodes": { + "127.0.0.1:1980": 1, + "127.0.0.1:1970": 1 + }, + "scheme": "http" + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "createUpstreamById" + }, + "patch": { + "summary": "Update an Upstream (Partial)", + "description": "Partially update an upstream's configuration.", + "tags": [ + "Upstreams" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/UpstreamUpsertPatchId" + }, + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpstreamEnvelope" + }, + "examples": { + "1": { + "summary": "Add Node for Upstream", + "value": { + "key": "/apisix/upstreams/1", + "value": { + "timeout": { + "send": 15, + "read": 15, + "connect": 15 + }, + "desc": "desc", + "id": "1", + "create_time": 1684290697, + "retries": 1, + "pass_host": "pass", + "update_time": 1684291001, + "type": "roundrobin", + "name": "upstream-for-test", + "hash_on": "vars", + "nodes": { + "127.0.0.1:1981": 1, + "127.0.0.1:1980": 1 + }, + "scheme": "http" + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "updateUpstream" + }, + "get": { + "summary": "Get an Upstream", + "description": "Retrieve a single upstream by its ID.", + "tags": [ + "Upstreams" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpstreamEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "value": { + "timeout": { + "send": 15, + "read": 15, + "connect": 15 + }, + "desc": "desc", + "id": "1", + "create_time": 1684290697, + "retries": 1, + "pass_host": "pass", + "update_time": 1684291419, + "type": "roundrobin", + "name": "upstream-for-test", + "hash_on": "vars", + "nodes": { + "httpbin.org:443": 1 + }, + "scheme": "https" + }, + "key": "/apisix/upstreams/1", + "modifiedIndex": 126, + "createdIndex": 121 + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "getUpstream" + }, + "delete": { + "summary": "Delete an Upstream", + "description": "Delete an upstream by its ID. Fails if the upstream is still referenced by a route or service.", + "tags": [ + "Upstreams" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + }, + { + "name": "force", + "in": "query", + "description": "Delete resource by force even if the resource is in use.", + "required": false, + "example": "true", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "key": { + "description": "Unique key identifier for this upstream in the data store.", + "type": "string" + }, + "deleted": { + "description": "Number of deleted keys, serialized as a string for compatibility.", + "type": "string" + } + } + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/upstreams/1", + "deleted": "1" + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Missing identifier or resource is still referenced (where dependency checks apply)." + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "deleteUpstream" + } + }, + "/apisix/admin/ssls": { + "get": { + "summary": "List All SSL Certificates", + "description": "Retrieve all configured SSL certificates. Private keys are **not** included in the response for security.", + "tags": [ + "SSL Certificates" + ], + "parameters": [ + { + "$ref": "#/components/parameters/LabelFilter" + }, + { + "$ref": "#/components/parameters/Page" + }, + { + "$ref": "#/components/parameters/PageSize" + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SSLListEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "list": [ + { + "value": { + "type": "server", + "snis": [ + "test.com" + ], + "id": "1", + "create_time": 1684373523, + "status": 1, + "cert": "-----BEGIN CERTIFICATE-----\nMIIDazCCAlOgAwIBAgIUJ9hQk3kXgZeZpY/m/0V6RmH1AbcwDQYJKoZIhvcNAQEL\nBQAwRTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM\nGEludGVybmFsIFJvb3QgQ0EgKGV4YW1wbGUpMB4XDTI1MDEwMTAwMDAwMFoXDTM1\nMDEwMTAwMDAwMFowRTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUx\nITAfBgNVBAoMGEludGVybmFsIFJvb3QgQ0EgKGV4YW1wbGUpMIIBIjANBgkqhkiG\n9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4eXAMPLEcaPUBLIcfakeforopeNAPIspecON\nLYexampleCAcontentsoExpandedFakeKeyMaterial==\n-----END CERTIFICATE-----\n", + "update_time": 1684373523 + }, + "key": "/apisix/ssls/1", + "modifiedIndex": 133, + "createdIndex": 133 + } + ], + "total": 1 + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "listSSLCertificates" + }, + "post": { + "summary": "Create an SSL Certificate", + "description": "Upload a new SSL certificate with an auto-generated ID.", + "tags": [ + "SSL Certificates" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/SslUpsertPostCollection" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SSLWriteEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/ssls/00000000000000000134", + "value": { + "type": "server", + "cert": "-----BEGIN CERTIFICATE-----\nMIIDazCCAlOgAwIBAgIUJ9hQk3kXgZeZpY/m/0V6RmH1AbcwDQYJKoZIhvcNAQEL\nBQAwRTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM\nGEludGVybmFsIFJvb3QgQ0EgKGV4YW1wbGUpMB4XDTI1MDEwMTAwMDAwMFoXDTM1\nMDEwMTAwMDAwMFowRTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUx\nITAfBgNVBAoMGEludGVybmFsIFJvb3QgQ0EgKGV4YW1wbGUpMIIBIjANBgkqhkiG\n9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4eXAMPLEcaPUBLIcfakeforopeNAPIspecON\nLYexampleCAcontentsoExpandedFakeKeyMaterial==\n-----END CERTIFICATE-----\n", + "key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDh5cAWYXAMPLEbe\nfakekeyForOpenAPISpecPlaceholderOnlyDoNotUseForRealKeysNopeNopeNo\npeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNo\npeNopeNopeNopeNopeNopeNope==\n-----END PRIVATE KEY-----\n", + "id": "00000000000000000134", + "create_time": 1684374944, + "status": 1, + "snis": [ + "test.com" + ], + "update_time": 1684374944 + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "createSSLCertificate" + }, + "put": { + "summary": "Create or replace ssls using a body ID", + "description": "Equivalent to PUT on the item URL, with id required in the request body. The resource handler injects and validates this ID before persistence.", + "tags": [ + "SSL Certificates" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "description": "`SSL` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "allOf": [ + { + "$ref": "#/components/schemas/SSL" + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + }, + { + "required": [ + "id" + ] + } + ] + }, + "example": { + "type": "server", + "sni": "api.example.com", + "cert": "-----BEGIN CERTIFICATE-----\nMIIDazCCAlOgAwIBAgIUJ9hQk3kXgZeZpY/m/0V6RmH1AbcwDQYJKoZIhvcNAQEL\nBQAwRTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM\nGEludGVybmFsIFJvb3QgQ0EgKGV4YW1wbGUpMB4XDTI1MDEwMTAwMDAwMFoXDTM1\nMDEwMTAwMDAwMFowRTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUx\nITAfBgNVBAoMGEludGVybmFsIFJvb3QgQ0EgKGV4YW1wbGUpMIIBIjANBgkqhkiG\n9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4eXAMPLEcaPUBLIcfakeforopeNAPIspecON\nLYexampleCAcontentsoExpandedFakeKeyMaterial==\n-----END CERTIFICATE-----\n", + "key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDh5cAWYXAMPLEbe\nfakekeyForOpenAPISpecPlaceholderOnlyDoNotUseForRealKeysNopeNopeNo\npeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNo\npeNopeNopeNopeNopeNopeNope==\n-----END PRIVATE KEY-----\n", + "id": "1" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SSLWriteEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/ssls/1", + "value": { + "type": "server", + "key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDh5cAWYXAMPLEbe\nfakekeyForOpenAPISpecPlaceholderOnlyDoNotUseForRealKeysNopeNopeNo\npeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNo\npeNopeNopeNopeNopeNopeNope==\n-----END PRIVATE KEY-----\n", + "snis": [ + "test.com" + ], + "id": "1", + "create_time": 1684373523, + "status": 1, + "cert": "-----BEGIN CERTIFICATE-----\nMIIDazCCAlOgAwIBAgIUJ9hQk3kXgZeZpY/m/0V6RmH1AbcwDQYJKoZIhvcNAQEL\nBQAwRTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM\nGEludGVybmFsIFJvb3QgQ0EgKGV4YW1wbGUpMB4XDTI1MDEwMTAwMDAwMFoXDTM1\nMDEwMTAwMDAwMFowRTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUx\nITAfBgNVBAoMGEludGVybmFsIFJvb3QgQ0EgKGV4YW1wbGUpMIIBIjANBgkqhkiG\n9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4eXAMPLEcaPUBLIcfakeforopeNAPIspecON\nLYexampleCAcontentsoExpandedFakeKeyMaterial==\n-----END CERTIFICATE-----\n", + "update_time": 1684373523 + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "putSSLWithBodyId" + } + }, + "/apisix/admin/ssls/{id}": { + "put": { + "summary": "Create or Replace an SSL Certificate", + "description": "Upload a new SSL certificate with a specified ID, or replace an existing one.", + "tags": [ + "SSL Certificates" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/SslUpsertPutId" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SSLWriteEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/ssls/1", + "value": { + "type": "server", + "key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDh5cAWYXAMPLEbe\nfakekeyForOpenAPISpecPlaceholderOnlyDoNotUseForRealKeysNopeNopeNo\npeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNo\npeNopeNopeNopeNopeNopeNope==\n-----END PRIVATE KEY-----\n", + "snis": [ + "test.com" + ], + "id": "1", + "create_time": 1684373523, + "status": 1, + "cert": "-----BEGIN CERTIFICATE-----\nMIIDazCCAlOgAwIBAgIUJ9hQk3kXgZeZpY/m/0V6RmH1AbcwDQYJKoZIhvcNAQEL\nBQAwRTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM\nGEludGVybmFsIFJvb3QgQ0EgKGV4YW1wbGUpMB4XDTI1MDEwMTAwMDAwMFoXDTM1\nMDEwMTAwMDAwMFowRTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUx\nITAfBgNVBAoMGEludGVybmFsIFJvb3QgQ0EgKGV4YW1wbGUpMIIBIjANBgkqhkiG\n9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4eXAMPLEcaPUBLIcfakeforopeNAPIspecON\nLYexampleCAcontentsoExpandedFakeKeyMaterial==\n-----END CERTIFICATE-----\n", + "update_time": 1684373523 + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "createSSLCertificateById" + }, + "get": { + "summary": "Get an SSL Certificate", + "description": "Retrieve a single SSL certificate by its ID. The private key is **not** included in the response.", + "tags": [ + "SSL Certificates" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SSLEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "value": { + "type": "server", + "snis": [ + "test.com" + ], + "id": "1", + "create_time": 1684373523, + "status": 1, + "cert": "-----BEGIN CERTIFICATE-----\nMIIDazCCAlOgAwIBAgIUJ9hQk3kXgZeZpY/m/0V6RmH1AbcwDQYJKoZIhvcNAQEL\nBQAwRTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM\nGEludGVybmFsIFJvb3QgQ0EgKGV4YW1wbGUpMB4XDTI1MDEwMTAwMDAwMFoXDTM1\nMDEwMTAwMDAwMFowRTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUx\nITAfBgNVBAoMGEludGVybmFsIFJvb3QgQ0EgKGV4YW1wbGUpMIIBIjANBgkqhkiG\n9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4eXAMPLEcaPUBLIcfakeforopeNAPIspecON\nLYexampleCAcontentsoExpandedFakeKeyMaterial==\n-----END CERTIFICATE-----\n", + "update_time": 1684373523 + }, + "key": "/apisix/ssls/1", + "modifiedIndex": 133, + "createdIndex": 133 + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "getSSLCertificate" + }, + "patch": { + "summary": "Update an SSL Certificate (Partial)", + "description": "Partially update an SSL certificate's configuration. Only the fields included in the request body are modified; all other fields remain unchanged.", + "tags": [ + "SSL Certificates" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/SslUpsertPatchId" + }, + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SSLWriteEnvelope" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "updateSSLCertificate" + }, + "delete": { + "summary": "Delete an SSL Certificate", + "description": "Delete an SSL certificate by its ID.", + "tags": [ + "SSL Certificates" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + }, + { + "name": "force", + "in": "query", + "description": "Delete resource by force even if the resource is in use.", + "required": false, + "example": "true", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "key": { + "description": "The storage key or path identifier for the SSL object in the data store.", + "type": "string" + }, + "deleted": { + "description": "Number of deleted keys, serialized as a string for compatibility.", + "type": "string" + } + } + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/ssls/00000000000000000134", + "deleted": "1" + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Missing identifier or resource is still referenced (where dependency checks apply)." + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "deleteSSLCertificate" + } + }, + "/apisix/admin/global_rules": { + "get": { + "summary": "List All Global Rules", + "description": "Retrieve all configured global rules.", + "tags": [ + "Global Rules" + ], + "parameters": [ + { + "$ref": "#/components/parameters/NameFilter" + }, + { + "$ref": "#/components/parameters/LabelFilter" + }, + { + "$ref": "#/components/parameters/Page" + }, + { + "$ref": "#/components/parameters/PageSize" + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GlobalRuleListEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "list": [ + { + "value": { + "plugins": { + "limit-count": { + "rejected_code": 503, + "time_window": 60, + "key": "remote_addr", + "key_type": "var", + "count": 2, + "allow_degradation": false, + "show_limit_quota_header": true, + "policy": "local" + } + }, + "update_time": 1684375502, + "create_time": 1684375502, + "id": "1" + }, + "key": "/apisix/global_rules/1", + "modifiedIndex": 137, + "createdIndex": 137 + } + ], + "total": 1 + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "listGlobalRules" + }, + "put": { + "summary": "Create or replace global_rules using a body ID", + "description": "Equivalent to PUT on the item URL, with id required in the request body. The resource handler injects and validates this ID before persistence.", + "tags": [ + "Global Rules" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "description": "`GlobalRule` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "allOf": [ + { + "$ref": "#/components/schemas/GlobalRule" + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + }, + { + "required": [ + "id" + ] + } + ] + }, + "example": { + "plugins": { + "prometheus": {}, + "ip-restriction": { + "whitelist": [ + "10.0.0.0/8", + "172.16.0.0/12" + ] + } + }, + "id": "1" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GlobalRuleEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/global_rules/1", + "value": { + "plugins": { + "limit-count": { + "time_window": 60, + "rejected_code": 503, + "key": "remote_addr", + "key_type": "var", + "count": 2, + "show_limit_quota_header": true, + "allow_degradation": false, + "policy": "local" + } + }, + "id": "1", + "create_time": 1684375502, + "update_time": 1684375502 + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "putGlobalRuleWithBodyId" + } + }, + "/apisix/admin/global_rules/{id}": { + "put": { + "summary": "Create or Replace a Global Rule", + "description": "Create a new global rule with a specified ID, or fully replace an existing one.", + "tags": [ + "Global Rules" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/GlobalRuleUpsertPutId" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GlobalRuleEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/global_rules/1", + "value": { + "plugins": { + "limit-count": { + "time_window": 60, + "rejected_code": 503, + "key": "remote_addr", + "key_type": "var", + "count": 2, + "show_limit_quota_header": true, + "allow_degradation": false, + "policy": "local" + } + }, + "id": "1", + "create_time": 1684375502, + "update_time": 1684375502 + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "createGlobalRuleById" + }, + "get": { + "summary": "Get a Global Rule", + "description": "Retrieve a single global rule by its ID.", + "tags": [ + "Global Rules" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GlobalRuleEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "value": { + "plugins": { + "limit-count": { + "rejected_code": 503, + "time_window": 60, + "key": "remote_addr", + "key_type": "var", + "count": 2, + "allow_degradation": false, + "show_limit_quota_header": true, + "policy": "local" + } + }, + "update_time": 1684375502, + "create_time": 1684375502, + "id": "1" + }, + "key": "/apisix/global_rules/1", + "modifiedIndex": 137, + "createdIndex": 137 + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "getGlobalRule" + }, + "patch": { + "summary": "Update a Global Rule (Partial)", + "description": "Partially update a global rule's configuration.", + "tags": [ + "Global Rules" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/GlobalRuleUpsertPatchId" + }, + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GlobalRuleEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/global_rules/1", + "value": { + "plugins": { + "limit-count": { + "rejected_code": 503, + "time_window": 60, + "key": "remote_addr", + "key_type": "var", + "count": 3, + "allow_degradation": false, + "show_limit_quota_header": true, + "policy": "local" + } + }, + "update_time": 1684376614, + "create_time": 1684375502, + "id": "1" + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "updateGlobalRule" + }, + "delete": { + "summary": "Delete a Global Rule", + "description": "Delete a global rule by its ID.", + "tags": [ + "Global Rules" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + }, + { + "name": "force", + "in": "query", + "description": "Delete resource by force even if the resource is in use.", + "required": false, + "example": "true", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "key": { + "description": "The storage key or path identifier for the global rule.", + "type": "string" + }, + "deleted": { + "description": "Number of deleted keys, serialized as a string for compatibility.", + "type": "string" + } + } + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/global_rules/1", + "deleted": "1" + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Missing identifier or resource is still referenced (where dependency checks apply)." + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "deleteGlobalRule" + } + }, + "/apisix/admin/consumer_groups": { + "get": { + "summary": "List All Consumer Groups", + "description": "Retrieve all configured consumer groups.", + "tags": [ + "Consumer Groups" + ], + "parameters": [ + { + "$ref": "#/components/parameters/NameFilter" + }, + { + "$ref": "#/components/parameters/LabelFilter" + }, + { + "$ref": "#/components/parameters/Page" + }, + { + "$ref": "#/components/parameters/PageSize" + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConsumerGroupListEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "list": [ + { + "value": { + "plugins": { + "limit-count": { + "allow_degradation": false, + "count": 2, + "group": "consumer_group_name", + "time_window": 60, + "show_limit_quota_header": true, + "key": "remote_addr", + "rejected_code": 503, + "key_type": "var", + "policy": "local" + } + }, + "desc": "desc", + "id": "company_a", + "create_time": 1684377809, + "update_time": 1684378459 + }, + "key": "/apisix/consumer_groups/company_a", + "modifiedIndex": 144, + "createdIndex": 143 + } + ], + "total": 1 + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "listConsumerGroups" + }, + "put": { + "summary": "Create or replace consumer_groups using a body ID", + "description": "Equivalent to PUT on the item URL, with id required in the request body. The resource handler injects and validates this ID before persistence.", + "tags": [ + "Consumer Groups" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "description": "`ConsumerGroup` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "allOf": [ + { + "$ref": "#/components/schemas/ConsumerGroup" + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + }, + { + "required": [ + "id" + ] + } + ] + }, + "example": { + "plugins": { + "limit-count": { + "count": 200, + "time_window": 60, + "rejected_code": 429, + "key_type": "var", + "key": "consumer_name" + } + }, + "id": "1" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConsumerGroupEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/consumer_groups/company_a", + "value": { + "plugins": { + "limit-count": { + "allow_degradation": false, + "count": 2, + "group": "company_a", + "time_window": 60, + "key": "remote_addr", + "key_type": "var", + "show_limit_quota_header": true, + "rejected_code": 503, + "policy": "local" + } + }, + "desc": "desc", + "id": "company_a", + "create_time": 1684377809, + "update_time": 1684378459 + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "putConsumerGroupWithBodyId" + } + }, + "/apisix/admin/consumer_groups/{id}": { + "put": { + "summary": "Create or Replace a Consumer Group", + "description": "Create a new consumer group with a specified ID, or fully replace an existing one.", + "tags": [ + "Consumer Groups" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/ConsumerGroupUpsertPutId" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConsumerGroupEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/consumer_groups/company_a", + "value": { + "plugins": { + "limit-count": { + "allow_degradation": false, + "count": 2, + "group": "company_a", + "time_window": 60, + "key": "remote_addr", + "key_type": "var", + "show_limit_quota_header": true, + "rejected_code": 503, + "policy": "local" + } + }, + "desc": "desc", + "id": "company_a", + "create_time": 1684377809, + "update_time": 1684378459 + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "createConsumerGroupById" + }, + "get": { + "summary": "Get a Consumer Group", + "description": "Retrieve a single consumer group by its ID.", + "tags": [ + "Consumer Groups" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConsumerGroupEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "value": { + "plugins": { + "limit-count": { + "allow_degradation": false, + "count": 2, + "group": "consumer_group_name", + "time_window": 60, + "show_limit_quota_header": true, + "key": "remote_addr", + "rejected_code": 503, + "key_type": "var", + "policy": "local" + } + }, + "desc": "desc", + "id": "company_a", + "create_time": 1684377809, + "update_time": 1684378459 + }, + "key": "/apisix/consumer_groups/company_a", + "modifiedIndex": 144, + "createdIndex": 143 + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "getConsumerGroup" + }, + "patch": { + "summary": "Update a Consumer Group (Partial)", + "description": "Partially update a consumer group's configuration.", + "tags": [ + "Consumer Groups" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/ConsumerGroupUpsertPatchId" + }, + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConsumerGroupEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/consumer_groups/company_a", + "value": { + "plugins": { + "limit-count": { + "allow_degradation": false, + "count": 2, + "group": "consumer_group_name", + "time_window": 60, + "show_limit_quota_header": true, + "key": "remote_addr", + "rejected_code": 503, + "key_type": "var", + "policy": "local" + }, + "prometheus": { + "prefer_name": false + } + }, + "desc": "desc", + "id": "company_a", + "create_time": 1684377809, + "update_time": 1684379208 + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "updateConsumerGroup" + }, + "delete": { + "summary": "Delete a Consumer Group", + "description": "Delete a consumer group by its ID.", + "tags": [ + "Consumer Groups" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + }, + { + "name": "force", + "in": "query", + "description": "Delete resource by force even if the resource is in use.", + "required": false, + "example": "true", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "key": { + "description": "The storage key or path identifier for the consumer group.", + "type": "string" + }, + "deleted": { + "description": "Number of deleted keys, serialized as a string for compatibility.", + "type": "string" + } + } + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/consumer_groups/company_a", + "deleted": "1" + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Missing identifier or resource is still referenced (where dependency checks apply)." + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "deleteConsumerGroup" + } + }, + "/apisix/admin/plugin_configs": { + "get": { + "summary": "List All Plugin Configs", + "description": "Retrieve all configured plugin configs.", + "tags": [ + "Plugin Configs" + ], + "parameters": [ + { + "$ref": "#/components/parameters/NameFilter" + }, + { + "$ref": "#/components/parameters/LabelFilter" + }, + { + "$ref": "#/components/parameters/Page" + }, + { + "$ref": "#/components/parameters/PageSize" + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PluginConfigListEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "list": [ + { + "value": { + "plugins": { + "limit-count": { + "rejected_code": 503, + "time_window": 60, + "key": "remote_addr", + "key_type": "var", + "count": 2, + "allow_degradation": false, + "show_limit_quota_header": true, + "policy": "local" + } + }, + "desc": "desc", + "id": "1", + "create_time": 1684380007, + "update_time": 1684380034 + }, + "key": "/apisix/plugin_configs/1", + "modifiedIndex": 151, + "createdIndex": 150 + } + ], + "total": 1 + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "listPluginConfigs" + }, + "put": { + "summary": "Create or replace plugin_configs using a body ID", + "description": "Equivalent to PUT on the item URL, with id required in the request body. The resource handler injects and validates this ID before persistence.", + "tags": [ + "Plugin Configs" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "description": "`PluginConfig` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "allOf": [ + { + "$ref": "#/components/schemas/PluginConfig" + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + }, + { + "required": [ + "id" + ] + } + ] + }, + "example": { + "plugins": { + "limit-count": { + "count": 100, + "time_window": 60, + "rejected_code": 429, + "key_type": "var", + "key": "remote_addr" + }, + "proxy-rewrite": { + "uri": "/backend$uri" + } + }, + "id": "1" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PluginConfigEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/plugin_configs/1", + "value": { + "plugins": { + "limit-count": { + "time_window": 60, + "rejected_code": 503, + "key": "remote_addr", + "key_type": "var", + "count": 2, + "show_limit_quota_header": true, + "allow_degradation": false, + "policy": "local" + } + }, + "desc": "desc", + "id": "1", + "create_time": 1684380007, + "update_time": 1684380034 + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "putPluginConfigWithBodyId" + } + }, + "/apisix/admin/plugin_configs/{id}": { + "put": { + "summary": "Create or Replace a Plugin Config", + "description": "Create a new plugin config with a specified ID, or fully replace an existing one.", + "tags": [ + "Plugin Configs" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/PluginConfigUpsertPutId" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PluginConfigEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/plugin_configs/1", + "value": { + "plugins": { + "limit-count": { + "time_window": 60, + "rejected_code": 503, + "key": "remote_addr", + "key_type": "var", + "count": 2, + "show_limit_quota_header": true, + "allow_degradation": false, + "policy": "local" + } + }, + "desc": "desc", + "id": "1", + "create_time": 1684380007, + "update_time": 1684380034 + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "createPluginConfigById" + }, + "get": { + "summary": "Get a Plugin Config", + "description": "Retrieve a single plugin config by its ID.", + "tags": [ + "Plugin Configs" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PluginConfigEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "value": { + "plugins": { + "limit-count": { + "rejected_code": 503, + "time_window": 60, + "key": "remote_addr", + "key_type": "var", + "count": 2, + "allow_degradation": false, + "show_limit_quota_header": true, + "policy": "local" + } + }, + "desc": "desc", + "id": "1", + "create_time": 1684380007, + "update_time": 1684380034 + }, + "key": "/apisix/plugin_configs/1", + "modifiedIndex": 151, + "createdIndex": 150 + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "getPluginConfig" + }, + "patch": { + "summary": "Update a Plugin Config (Partial)", + "description": "Partially update a plugin config's configuration.", + "tags": [ + "Plugin Configs" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/PluginConfigUpsertPatchId" + }, + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PluginConfigEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/plugin_configs/1", + "value": { + "plugins": { + "limit-count": { + "rejected_code": 503, + "time_window": 60, + "key": "remote_addr", + "key_type": "var", + "count": 2, + "allow_degradation": false, + "show_limit_quota_header": true, + "policy": "local" + }, + "prometheus": { + "prefer_name": false + } + }, + "desc": "desc", + "id": "1", + "create_time": 1684380007, + "update_time": 1684380472 + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "updatePluginConfig" + }, + "delete": { + "summary": "Delete a Plugin Config", + "description": "Delete a plugin config by its ID.", + "tags": [ + "Plugin Configs" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + }, + { + "name": "force", + "in": "query", + "description": "Delete resource by force even if the resource is in use.", + "required": false, + "example": "true", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "key": { + "description": "Unique key identifier for this plugin configuration in the data store.", + "type": "string" + }, + "deleted": { + "description": "Number of deleted keys, serialized as a string for compatibility.", + "type": "string" + } + } + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/plugin_configs/1", + "deleted": "1" + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Missing identifier or resource is still referenced (where dependency checks apply)." + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "deletePluginConfig" + } + }, + "/apisix/admin/plugin_metadata": { + "get": { + "summary": "List All Plugin Metadata", + "description": "Retrieve metadata for all plugins that have metadata configured.", + "tags": [ + "Plugin Metadata" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Page" + }, + { + "$ref": "#/components/parameters/PageSize" + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "list": { + "description": "A list of plugin metadata.", + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "description": "Plugin metadata values.", + "type": "object", + "properties": {} + }, + "key": { + "description": "Unique key identifier for this plugin metadata in the data store.", + "type": "string" + }, + "modifiedIndex": { + "description": "The index number representing the most recent update to the object. It starts with the same value as `createdIndex` and increases by 1 each time the resource is modified.", + "type": "integer" + }, + "createdIndex": { + "description": "The index number representing when the object was first created. It indicates the creation order of the resource.", + "type": "integer" + } + } + } + }, + "total": { + "description": "Total number of plugin metadata entries available.", + "type": "integer" + } + } + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "list": [ + { + "value": { + "log_format": { + "host": "$host", + "@timestamp": "$time_iso8601", + "client_ip": "$remote_addr" + } + }, + "key": "/apisix/plugin_metadata/syslog", + "modifiedIndex": 283, + "createdIndex": 283 + } + ], + "total": 1 + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "listPluginMetadata" + } + }, + "/apisix/admin/plugin_metadata/{plugin_name}": { + "put": { + "summary": "Create or Replace Plugin Metadata", + "description": "Set metadata for a specific plugin. This applies to all instances of the plugin across the cluster.", + "tags": [ + "Plugin Metadata" + ], + "parameters": [ + { + "$ref": "#/components/parameters/PluginNameInPath" + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/PluginMetadataUpsert" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "key": { + "description": "Unique key identifier for this plugin metadata in the data store.", + "type": "string" + }, + "value": { + "description": "Plugin metadata values.", + "type": "object", + "properties": {} + } + } + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/plugin_metadata/syslog", + "value": { + "log_format": { + "host": "$host", + "@timestamp": "$time_iso8601", + "client_ip": "$remote_addr" + } + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "createPluginMetadata" + }, + "get": { + "summary": "Get Plugin Metadata", + "description": "Retrieve metadata for a specific plugin by plugin name.", + "tags": [ + "Plugin Metadata" + ], + "parameters": [ + { + "$ref": "#/components/parameters/PluginNameInPath" + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "value": { + "description": "Plugin metadata values.", + "type": "object", + "properties": {} + }, + "key": { + "description": "Unique key identifier for this plugin metadata in the data store.", + "type": "string" + }, + "modifiedIndex": { + "description": "The index number representing the most recent update to the object. It starts with the same value as `createdIndex` and increases by 1 each time the resource is modified.", + "type": "integer" + }, + "createdIndex": { + "description": "The index number representing when the object was first created. It indicates the creation order of the resource.", + "type": "integer" + } + } + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "value": { + "log_format": { + "host": "$host", + "@timestamp": "$time_iso8601", + "client_ip": "$remote_addr" + } + }, + "key": "/apisix/plugin_metadata/syslog", + "modifiedIndex": 156, + "createdIndex": 156 + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "getPluginMetadata" + }, + "delete": { + "summary": "Delete Plugin Metadata", + "description": "Delete metadata for a specific plugin.", + "tags": [ + "Plugin Metadata" + ], + "parameters": [ + { + "$ref": "#/components/parameters/PluginNameInPath" + }, + { + "name": "force", + "in": "query", + "description": "Delete resource by force even if the resource is in use.", + "required": false, + "example": "true", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "key": { + "description": "Unique key identifier for this plugin metadata in the data store.", + "type": "string" + }, + "deleted": { + "description": "Number of deleted keys, serialized as a string for compatibility.", + "type": "string" + } + } + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/plugin_metadata/syslog", + "deleted": "1" + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Missing identifier or resource is still referenced (where dependency checks apply)." + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "deletePluginMetadata" + } + }, + "/apisix/admin/plugins/list": { + "get": { + "summary": "List Plugin Names", + "description": "Returns successfully loaded configured plugins in the selected subsystem, sorted by descending priority. The installed plugin inventory is deployment-dependent.", + "tags": [ + "Plugins" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Subsystem" + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "type": "array", + "description": "An array of plugin names.", + "items": { + "type": "string" + } + }, + "examples": { + "configuredSubset": { + "summary": "An instance configured with key-auth, proxy-rewrite and limit-count, sorted by priority", + "value": [ + "key-auth", + "proxy-rewrite", + "limit-count" + ] + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "400": { + "description": "Unsupported subsystem or missing schema. /plugins/list returns a JSON string; other plugin endpoints return an error object.", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/components/schemas/ErrorResponse" + } + ] + } + } + } + } + }, + "operationId": "listPluginNames" + } + }, + "/apisix/admin/plugins/{plugin_name}": { + "get": { + "summary": "Get Plugin JSON Schema", + "description": "Retrieve the JSON Schema definition for a specific plugin. This is useful for validating plugin configurations before applying them.", + "tags": [ + "Plugins" + ], + "parameters": [ + { + "$ref": "#/components/parameters/PluginNameInPath" + }, + { + "$ref": "#/components/parameters/Subsystem" + }, + { + "in": "query", + "name": "schema_type", + "description": "Use consumer to retrieve the consumer schema; otherwise returns the plugin schema.", + "schema": { + "type": "string" + }, + "example": "consumer" + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "JSON Schema of the loaded plugin, selected by subsystem and schema_type. Plugin schemas and Lua validation depend on the installed plugin set." + }, + "examples": { + "keyAuthConsumer": { + "summary": "key-auth with schema_type=consumer (from release/3.18 plugin source)", + "value": { + "type": "object", + "properties": { + "key": { + "type": "string" + } + }, + "encrypt_fields": [ + "key" + ], + "required": [ + "key" + ] + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "400": { + "description": "Unsupported subsystem or missing schema. /plugins/list returns a JSON string; other plugin endpoints return an error object.", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/components/schemas/ErrorResponse" + } + ] + } + } + } + } + }, + "operationId": "getPluginSchema" + } + }, + "/apisix/admin/plugins": { + "get": { + "summary": "List Plugin Attributes", + "description": "Get attributes of all plugins configured on the APISIX instance.\n\n> **Deprecated:** This endpoint is being replaced. Use `GET /plugins/list` and `GET /plugins/{plugin_name}` instead.", + "tags": [ + "Plugins" + ], + "parameters": [ + { + "$ref": "#/components/parameters/PluginListAll" + }, + { + "$ref": "#/components/parameters/Subsystem" + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "Map of loaded plugin names to plugin attributes. Attribute availability depends on the plugin.", + "additionalProperties": { + "type": "object", + "properties": { + "version": { + "type": "number" + }, + "priority": { + "type": "integer" + }, + "schema": { + "type": "object" + }, + "consumer_schema": { + "type": "object" + }, + "metadata_schema": { + "type": "object" + }, + "type": { + "type": "string" + }, + "scope": { + "type": "string" + } + } + } + } + } + } + }, + "400": { + "description": "Unsupported subsystem or missing schema. /plugins/list returns a JSON string; other plugin endpoints return an error object.", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/components/schemas/ErrorResponse" + } + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + } + }, + "operationId": "listPluginAttributes" + } + }, + "/apisix/admin/plugins/reload": { + "put": { + "summary": "Hot-Reload Plugins", + "description": "Trigger a hot reload of all plugins. This re-reads plugin configurations from etcd without restarting APISIX.\n\n**Important:** Only the `PUT` method is accepted.", + "tags": [ + "Plugins" + ], + "parameters": [], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "type": "string" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": "done" + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + } + }, + "operationId": "reloadPlugins", + "x-codeSamples": [ + { + "lang": "cURL", + "label": "cURL", + "source": "curl -i http://127.0.0.1:9180/apisix/admin/plugins/reload \\\n -H 'X-API-KEY: your-api-key' -X PUT" + } + ] + } + }, + "/apisix/admin/stream_routes": { + "get": { + "summary": "List All Stream Routes", + "description": "Retrieve all configured stream (L4) routes.\n\n**Prerequisite:** Stream mode must be enabled in `config.yaml`.", + "tags": [ + "Stream Routes" + ], + "parameters": [ + { + "$ref": "#/components/parameters/NameFilter" + }, + { + "$ref": "#/components/parameters/LabelFilter" + }, + { + "$ref": "#/components/parameters/FilterExpr" + }, + { + "$ref": "#/components/parameters/Page" + }, + { + "$ref": "#/components/parameters/PageSize" + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StreamRouteListEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "total": 1, + "list": [ + { + "createdIndex": 173, + "key": "/apisix/stream_routes/1", + "value": { + "id": "1", + "create_time": 1684392990, + "server_addr": "127.0.0.1", + "upstream": { + "hash_on": "vars", + "pass_host": "pass", + "nodes": { + "127.0.0.1:1995": 1 + }, + "type": "roundrobin", + "scheme": "http" + }, + "update_time": 1684392990, + "server_port": 2000 + }, + "modifiedIndex": 173 + } + ] + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "listStreamRoutes" + }, + "post": { + "summary": "Create a Stream Route", + "description": "Create a new stream route with an auto-generated ID.", + "tags": [ + "Stream Routes" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/StreamRouteUpsertPostCollection" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StreamRouteEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "value": { + "id": "00000000000000000176", + "create_time": 1684393167, + "server_addr": "127.0.0.1", + "upstream": { + "hash_on": "vars", + "pass_host": "pass", + "nodes": { + "127.0.0.1:1995": 1 + }, + "type": "roundrobin", + "scheme": "http" + }, + "update_time": 1684393167, + "server_port": 2000 + }, + "key": "/apisix/stream_routes/00000000000000000176" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "createStreamRoute" + }, + "put": { + "summary": "Create or replace stream_routes using a body ID", + "description": "Equivalent to PUT on the item URL, with id required in the request body. The resource handler injects and validates this ID before persistence.", + "tags": [ + "Stream Routes" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "description": "`StreamRoute` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "allOf": [ + { + "$ref": "#/components/schemas/StreamRoute" + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + }, + { + "required": [ + "id" + ] + } + ] + }, + "example": { + "server_addr": "127.0.0.1", + "server_port": 2000, + "upstream": { + "nodes": { + "127.0.0.1:1995": 1 + }, + "type": "roundrobin" + }, + "id": "1" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StreamRouteEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "value": { + "id": "00000000000000000176", + "create_time": 1684393167, + "server_addr": "127.0.0.1", + "upstream": { + "hash_on": "vars", + "pass_host": "pass", + "nodes": { + "127.0.0.1:1995": 1 + }, + "type": "roundrobin", + "scheme": "http" + }, + "update_time": 1684393167, + "server_port": 2000 + }, + "key": "/apisix/stream_routes/00000000000000000176" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "putStreamRouteWithBodyId" + } + }, + "/apisix/admin/secrets": { + "get": { + "summary": "List All Secrets", + "description": "Retrieve all configured secret manager integrations.", + "tags": [ + "Secrets" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Page" + }, + { + "$ref": "#/components/parameters/PageSize" + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "list": { + "type": "array", + "description": "An array of secret configuration objects.", + "items": { + "type": "object", + "properties": { + "value": { + "type": "object", + "description": "Secret values.", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for the secret configuration, including the secret manager type and ID." + }, + "prefix": { + "type": "string", + "description": "Path prefix of the secret engine that Vault should route to." + }, + "token": { + "type": "string", + "description": "Token used for APISIX to authenticate to Vault." + }, + "create_time": { + "type": "integer", + "description": "Creation time of the secret." + }, + "update_time": { + "type": "integer", + "description": "Update time of the secret." + }, + "uri": { + "type": "string", + "description": "Address of the Vault server." + } + } + }, + "key": { + "description": "Unique key identifier for this secret in the data store.", + "type": "string" + }, + "modifiedIndex": { + "description": "The index number representing the most recent update to the object. It starts with the same value as `createdIndex` and increases by 1 each time the resource is modified.", + "type": "integer" + }, + "createdIndex": { + "description": "The index number representing when the object was first created. It indicates the creation order of the resource.", + "type": "integer" + } + } + } + }, + "total": { + "description": "Total number of secrets matching the query criteria.", + "type": "integer" + } + } + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "total": 1, + "list": [ + { + "createdIndex": 179, + "key": "/apisix/secrets/vault/1", + "value": { + "token": "343effad", + "id": "vault/1", + "uri": "https://localhost/vault", + "prefix": "/apisix/kv", + "update_time": 1684395392, + "create_time": 1684395392 + }, + "modifiedIndex": 179 + } + ] + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "listSecrets" + } + }, + "/apisix/admin/protos/{id}": { + "put": { + "summary": "Create or Replace a Proto", + "description": "Upload a protobuf definition with a specified ID, or replace an existing one.", + "tags": [ + "Protos" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "schema": { + "type": "string" + }, + "example": "my-resource-1" + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/ProtoUpsertPutId" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProtoEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/protos/1", + "value": { + "id": "1", + "create_time": 1684752213, + "update_time": 1684752213, + "content": "syntax = \"proto3\";\n package helloworld;\n service Greeter {\n rpc GetErrResp (HelloRequest) returns (HelloReply) {}\n }\n message HelloRequest {\n string name = 1;\n repeated string items = 2;\n }\n message HelloReply {\n string message = 1;\n repeated string items = 2;\n }\n message ErrorDetail {\n int64 code = 1;\n string message = 2;\n string type = 3;\n }" + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "createProtoById" + }, + "get": { + "summary": "Get a Proto", + "description": "Retrieve a single protobuf definition by its ID.", + "tags": [ + "Protos" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "schema": { + "type": "string" + }, + "example": "my-resource-1" + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProtoEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "value": { + "id": "1", + "content": "syntax = \"proto3\";\n package helloworld;\n service Greeter {\n rpc GetErrResp (HelloRequest) returns (HelloReply) {}\n }\n message HelloRequest {\n string name = 1;\n repeated string items = 2;\n }\n message HelloReply {\n string message = 1;\n repeated string items = 2;\n }\n message ErrorDetail {\n int64 code = 1;\n string message = 2;\n string type = 3;\n }", + "update_time": 1684752213, + "create_time": 1684752213 + }, + "key": "/apisix/protos/1", + "modifiedIndex": 292, + "createdIndex": 292 + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "getProto" + }, + "delete": { + "summary": "Delete a Proto", + "description": "Delete a protobuf definition by its ID.", + "tags": [ + "Protos" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "schema": { + "type": "string" + }, + "example": "my-resource-1" + }, + { + "name": "force", + "in": "query", + "description": "Delete resource by force even if the resource is in use.", + "required": false, + "example": "true", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "Unique key identifier for this proto in the data store." + }, + "deleted": { + "type": "string", + "description": "Number of deleted keys, serialized as a string for compatibility." + } + } + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/protos/1", + "deleted": "1" + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Missing identifier or resource is still referenced (where dependency checks apply)." + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "deleteProto" + } + }, + "/apisix/admin/protos": { + "get": { + "summary": "List All Protos", + "description": "Retrieve all stored protobuf definitions.", + "tags": [ + "Protos" + ], + "parameters": [ + { + "$ref": "#/components/parameters/NameFilter" + }, + { + "$ref": "#/components/parameters/LabelFilter" + }, + { + "$ref": "#/components/parameters/Page" + }, + { + "$ref": "#/components/parameters/PageSize" + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProtoListEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "list": [ + { + "value": { + "id": "1", + "content": "syntax = \"proto3\";\n package helloworld;\n service Greeter {\n rpc GetErrResp (HelloRequest) returns (HelloReply) {}\n }\n message HelloRequest {\n string name = 1;\n repeated string items = 2;\n }\n message HelloReply {\n string message = 1;\n repeated string items = 2;\n }\n message ErrorDetail {\n int64 code = 1;\n string message = 2;\n string type = 3;\n }", + "update_time": 1684752213, + "create_time": 1684752213 + }, + "key": "/apisix/protos/1", + "modifiedIndex": 292, + "createdIndex": 292 + } + ], + "total": 1 + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "listProtos" + }, + "post": { + "summary": "Create a Proto", + "description": "Upload a new protobuf definition with an auto-generated ID.", + "tags": [ + "Protos" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/ProtoUpsertPostCollection" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProtoEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/protos/00000000000000000293", + "value": { + "create_time": 1684752743, + "update_time": 1684752743, + "content": "syntax = \"proto3\";\n package helloworld;\n service Greeter {\n rpc GetErrResp (HelloRequest) returns (HelloReply) {}\n }\n message HelloRequest {\n string name = 1;\n repeated string items = 2;\n }\n message HelloReply {\n string message = 1;\n repeated string items = 2;\n }\n message ErrorDetail {\n int64 code = 1;\n string message = 2;\n string type = 3;\n }", + "id": "00000000000000000293" + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "createProto" + }, + "put": { + "summary": "Create or replace protos using a body ID", + "description": "Equivalent to PUT on the item URL, with id required in the request body. The resource handler injects and validates this ID before persistence.", + "tags": [ + "Protos" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "description": "`Proto` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "allOf": [ + { + "$ref": "#/components/schemas/Proto" + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + }, + { + "required": [ + "id" + ] + } + ] + }, + "example": { + "content": "syntax = \"proto3\";\n\npackage helloworld;\n\nservice Greeter {\n rpc SayHello (HelloRequest) returns (HelloReply) {}\n}\n\nmessage HelloRequest {\n string name = 1;\n}\n\nmessage HelloReply {\n string message = 1;\n}", + "id": "1" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProtoEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/protos/1", + "value": { + "id": "1", + "create_time": 1684752213, + "update_time": 1684752213, + "content": "syntax = \"proto3\";\n package helloworld;\n service Greeter {\n rpc GetErrResp (HelloRequest) returns (HelloReply) {}\n }\n message HelloRequest {\n string name = 1;\n repeated string items = 2;\n }\n message HelloReply {\n string message = 1;\n repeated string items = 2;\n }\n message ErrorDetail {\n int64 code = 1;\n string message = 2;\n string type = 3;\n }" + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "putProtoWithBodyId" + } + }, + "/apisix/admin/schema/validate/{resource}": { + "post": { + "summary": "Validate Resource Configuration", + "description": "Validate against the selected resource JSON Schema only. Does not run the resource Lua checker, plugin check_schema, reference lookups, or persist configuration. Unlike normal PUT, server-injected IDs required by a resource schema must be included in this validation payload.", + "tags": [ + "Schema Validation" + ], + "parameters": [ + { + "$ref": "#/components/parameters/ResourceKindInPath" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "The resource configuration to validate. The structure depends on the resource type specified in the path parameter.", + "additionalProperties": true + }, + "example": { + "uri": "/api/v1/*", + "upstream": { + "scheme": "https", + "type": "roundrobin", + "nodes": { + "httpbin.org:443": 1 + } + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful response.", + "content": {} + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Bad Request" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + } + }, + "operationId": "validateResourceSchema" + } + }, + "/apisix/admin/stream_routes/{id}": { + "get": { + "summary": "Get Stream Route by ID", + "description": "Get stream route by ID.", + "tags": [ + "Stream Routes" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "schema": { + "type": "string" + }, + "example": "my-resource-1" + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StreamRouteEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/stream_routes/1", + "value": { + "id": "1", + "create_time": 1684392990, + "server_addr": "127.0.0.1", + "upstream": { + "hash_on": "vars", + "pass_host": "pass", + "nodes": { + "127.0.0.1:1995": 1 + }, + "type": "roundrobin", + "scheme": "http" + }, + "update_time": 1684392990, + "server_port": 2000 + } + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "getStreamRoute" + }, + "put": { + "summary": "Create Stream Route by ID", + "description": "Create a stream route with a specified ID.", + "tags": [ + "Stream Routes" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/StreamRouteUpsertPutId" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StreamRouteEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "value": { + "id": "00000000000000000176", + "create_time": 1684393167, + "server_addr": "127.0.0.1", + "upstream": { + "hash_on": "vars", + "pass_host": "pass", + "nodes": { + "127.0.0.1:1995": 1 + }, + "type": "roundrobin", + "scheme": "http" + }, + "update_time": 1684393167, + "server_port": 2000 + }, + "key": "/apisix/stream_routes/00000000000000000176" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "putStreamRouteById" + }, + "delete": { + "summary": "Delete Stream Route by ID", + "description": "Delete a stream route by ID.", + "tags": [ + "Stream Routes" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + }, + { + "name": "force", + "in": "query", + "description": "Delete resource by force even if the resource is in use.", + "required": false, + "example": "true", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "key": { + "description": "Unique key identifier for this stream route in the data store.", + "type": "string" + }, + "deleted": { + "description": "Number of deleted keys, serialized as a string for compatibility.", + "type": "string" + } + } + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "deleted": "1", + "key": "/apisix/stream_routes/1" + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Missing identifier or resource is still referenced (where dependency checks apply)." + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "deleteStreamRoute" + } + }, + "/apisix/admin/consumers/{username}/credentials": { + "get": { + "summary": "List Consumer Credentials", + "description": "Retrieve all credentials for a specific consumer.", + "tags": [ + "Credentials" + ], + "parameters": [ + { + "$ref": "#/components/parameters/UsernameInPath" + }, + { + "$ref": "#/components/parameters/NameFilter" + }, + { + "$ref": "#/components/parameters/LabelFilter" + }, + { + "$ref": "#/components/parameters/Page" + }, + { + "$ref": "#/components/parameters/PageSize" + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CredentialListEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "total": 2, + "list": [ + { + "key": "/apisix/consumers/john/credentials/cred-john-basic-auth", + "value": { + "update_time": 1725991703, + "id": "cred-john-basic-auth", + "plugins": { + "basic-auth": { + "password": "john-pass", + "username": "john-key" + } + }, + "create_time": 1725991529 + }, + "createdIndex": 42, + "modifiedIndex": 44 + }, + { + "key": "/apisix/consumers/john/credentials/cred-john-key-auth", + "value": { + "update_time": 1725991703, + "id": "cred-john-key-auth", + "plugins": { + "key-auth": { + "key": "john-key" + } + }, + "create_time": 1725991703 + }, + "createdIndex": 43, + "modifiedIndex": 43 + } + ] + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "listCredentials" + }, + "put": { + "summary": "Create or replace a credential using a body ID", + "description": "The id is required in the body; the parent consumer must exist.", + "tags": [ + "Credentials" + ], + "parameters": [ + { + "$ref": "#/components/parameters/UsernameInPath" + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "description": "`Credential` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "allOf": [ + { + "$ref": "#/components/schemas/Credential" + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + }, + { + "required": [ + "id" + ] + } + ] + }, + "example": { + "plugins": { + "key-auth": { + "key": "credential-secret-key-456" + } + }, + "id": "cred1" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CredentialEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/consumers/john/credentials/cred-john-key-auth", + "value": { + "create_time": 1725991703, + "id": "cred-john-key-auth", + "plugins": { + "key-auth": { + "key": "john-key" + } + }, + "update_time": 1725991703 + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "putCredentialWithBodyId" + } + }, + "/apisix/admin/consumers/{username}/credentials/{id}": { + "get": { + "summary": "Get a Credential", + "description": "Retrieve a specific credential for a consumer.", + "tags": [ + "Credentials" + ], + "parameters": [ + { + "$ref": "#/components/parameters/UsernameInPath" + }, + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CredentialEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/consumers/john/credentials/cred-john-basic-auth", + "value": { + "update_time": 1725991529, + "id": "cred-john-basic-auth", + "plugins": { + "basic-auth": { + "password": "john-pass", + "username": "john-key" + } + }, + "create_time": 1725991529 + }, + "createdIndex": 42, + "modifiedIndex": 42 + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "getCredential" + }, + "put": { + "summary": "Create or Replace a Credential", + "description": "Create a new credential for a consumer with a specified ID, or replace an existing one.\n\nA credential can hold at most one authentication plugin.", + "tags": [ + "Credentials" + ], + "parameters": [ + { + "$ref": "#/components/parameters/UsernameInPath" + }, + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/CredentialUpsert" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CredentialEnvelope" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/consumers/john/credentials/cred-john-key-auth", + "value": { + "create_time": 1725991703, + "id": "cred-john-key-auth", + "plugins": { + "key-auth": { + "key": "john-key" + } + }, + "update_time": 1725991703 + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "createCredentialById" + }, + "delete": { + "summary": "Delete a Credential", + "description": "Delete a specific credential from a consumer.", + "tags": [ + "Credentials" + ], + "parameters": [ + { + "$ref": "#/components/parameters/UsernameInPath" + }, + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/Force" + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "key": { + "description": "The storage key or path identifier for the consumer credential.", + "type": "string" + }, + "deleted": { + "description": "Number of deleted keys, serialized as a string for compatibility.", + "type": "string" + } + } + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/consumers/john/credentials/cred-john-key-auth", + "deleted": "1" + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Missing identifier or resource is still referenced (where dependency checks apply)." + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "deleteCredential" + } + }, + "/apisix/admin/secrets/{secret_type}/{id}": { + "get": { + "summary": "Get a Secret", + "description": "Retrieve a specific secret manager configuration.", + "tags": [ + "Secrets" + ], + "parameters": [ + { + "name": "secret_type", + "in": "path", + "description": "Type of secret manager (e.g., `vault`, `aws`).", + "required": true, + "example": "vault", + "schema": { + "type": "string", + "enum": [ + "vault", + "aws", + "gcp" + ] + } + }, + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "value": { + "type": "object", + "description": "Secret values.", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for the secret configuration, including the secret manager type and ID." + }, + "prefix": { + "type": "string", + "description": "Path prefix for the secret engine or storage location." + }, + "token": { + "type": "string", + "description": "Authentication token or credential for accessing the secret manager." + }, + "create_time": { + "type": "integer", + "description": "Creation time of the secret." + }, + "update_time": { + "type": "integer", + "description": "Update time of the secret." + }, + "uri": { + "type": "string", + "description": "Endpoint URI of the secret manager service." + } + } + }, + "key": { + "description": "Unique key identifier for this secret in the data store.", + "type": "string" + }, + "modifiedIndex": { + "description": "The index number representing the most recent update to the object. It starts with the same value as `createdIndex` and increases by 1 each time the resource is modified.", + "type": "integer" + }, + "createdIndex": { + "description": "The index number representing when the object was first created. It indicates the creation order of the resource.", + "type": "integer" + } + } + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "createdIndex": 179, + "key": "/apisix/secrets/vault/1", + "value": { + "token": "343effad", + "id": "vault/1", + "uri": "https://localhost/vault", + "prefix": "/apisix/kv", + "update_time": 1684395392, + "create_time": 1684395392 + }, + "modifiedIndex": 179 + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "getSecret" + }, + "put": { + "summary": "Create or Replace a Secret", + "description": "Configure a secret manager integration with a specified ID.", + "tags": [ + "Secrets" + ], + "parameters": [ + { + "name": "secret_type", + "in": "path", + "description": "Type of secret manager (e.g., `vault`, `aws`).", + "required": true, + "example": "vault", + "schema": { + "type": "string", + "enum": [ + "vault", + "aws", + "gcp" + ] + } + }, + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/SecretUpsertPutCollection" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "Unique key identifier for this secret in the data store." + }, + "value": { + "type": "object", + "description": "Secret values.", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for the secret configuration, including the secret manager type and ID." + }, + "prefix": { + "type": "string", + "description": "Path prefix for the secret engine or storage location." + }, + "token": { + "type": "string", + "description": "Authentication token or credential for accessing the secret manager." + }, + "namespace": { + "type": "string", + "description": "Used to set the [namespace](https://developer.hashicorp.com/vault/docs/enterprise/namespaces#vault-api-and-namespaces) of HashiCorp Vault Enterprise and HCP Vault." + }, + "create_time": { + "type": "integer", + "description": "Creation time of the secret." + }, + "update_time": { + "type": "integer", + "description": "Update time of the secret." + }, + "uri": { + "type": "string", + "description": "Endpoint URI of the secret manager service." + } + } + } + } + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "value": { + "prefix": "/apisix/kv", + "token": "343effad", + "uri": "https://localhost/vault", + "namespace": "apisix", + "id": "vault/1", + "update_time": 1684395392, + "create_time": 1684395392 + }, + "key": "/apisix/secrets/vault/1" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "createSecretById" + }, + "patch": { + "summary": "Update a Secret (Partial)", + "description": "Partially update a secret manager configuration.", + "tags": [ + "Secrets" + ], + "parameters": [ + { + "name": "secret_type", + "in": "path", + "description": "Type of secret manager (e.g., `vault`, `aws`).", + "required": true, + "example": "vault", + "schema": { + "type": "string", + "enum": [ + "vault", + "aws", + "gcp" + ] + } + }, + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/SecretUpsertPatchCollection" + }, + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "Unique key identifier for this secret in the data store." + }, + "value": { + "type": "object", + "description": "Secret values.", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for the secret configuration, including the secret manager type and ID." + }, + "prefix": { + "type": "string", + "description": "Path prefix for the secret engine or storage location." + }, + "token": { + "type": "string", + "description": "Authentication token or credential for accessing the secret manager." + }, + "create_time": { + "type": "integer", + "description": "Creation time of the secret." + }, + "update_time": { + "type": "integer", + "description": "Update time of the secret." + }, + "uri": { + "type": "string", + "description": "Endpoint URI of the secret manager service." + } + } + } + } + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "value": { + "token": "apisix", + "id": "vault/1", + "uri": "https://localhost/vault", + "prefix": "/apisix/kv", + "update_time": 1684396605, + "create_time": 1684395392 + }, + "key": "/apisix/secrets/vault/1" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "updateSecret" + }, + "delete": { + "summary": "Delete a Secret", + "description": "Delete a secret manager configuration.", + "tags": [ + "Secrets" + ], + "parameters": [ + { + "name": "secret_type", + "in": "path", + "description": "Type of secret manager (e.g., `vault`, `aws`).", + "required": true, + "example": "vault", + "schema": { + "type": "string", + "enum": [ + "vault", + "aws", + "gcp" + ] + } + }, + { + "name": "id", + "in": "path", + "description": "Unique identifier of the resource. Can be a string (alphanumeric, `-`, `_`, `.`) or a positive integer.", + "required": true, + "example": "my-resource-1", + "schema": { + "type": "string" + } + }, + { + "name": "force", + "in": "query", + "description": "Delete resource by force even if the resource is in use.", + "required": false, + "example": "true", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "Unique key identifier for this secret in the data store." + }, + "deleted": { + "type": "string", + "description": "Number of deleted keys, serialized as a string for compatibility." + } + } + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "deleted": "1", + "key": "/apisix/secrets/vault/1" + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Missing identifier or resource is still referenced (where dependency checks apply)." + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "operationId": "deleteSecret" + } + }, + "/apisix/admin": { + "head": { + "tags": [ + "Health" + ], + "operationId": "ping", + "summary": "Liveness probe", + "description": "Returns `200 OK` if the Admin API is reachable. Useful as a Kubernetes liveness probe target. This endpoint does not require authentication.", + "security": [], + "responses": { + "200": { + "description": "APISIX Admin API is alive." + } + }, + "parameters": [] + } + }, + "/apisix/admin/configs": { + "get": { + "tags": [ + "Standalone" + ], + "operationId": "getStandaloneConfig", + "summary": "Get the standalone configuration", + "description": "Available in API-driven standalone mode, with the YAML configuration provider and Admin API enabled. Returns the accepted configuration snapshot and its version and digest metadata, not a worker synchronization report. See [API-Driven Standalone Mode Usage](/apisix/reference/api-standalone-usage).", + "responses": { + "200": { + "description": "Current standalone configuration.", + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "application/yaml": { + "schema": { + "type": "string" + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "500": { + "$ref": "#/components/responses/BadRequest", + "description": "Failed to retrieve or encode stored configuration." + } + }, + "parameters": [] + }, + "put": { + "tags": [ + "Standalone" + ], + "operationId": "putStandaloneConfig", + "summary": "Replace the standalone configuration", + "description": "Replace the complete API-driven standalone configuration. X-Digest is a required client-supplied identifier. A matching digest returns 204 without applying the body. Otherwise the snapshot is stored and broadcast asynchronously; 202 does not confirm synchronization of all workers. Omitted resource sections may remove existing resources unless the corresponding configuration version is retained.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "application/yaml": { + "schema": { + "type": "string" + } + } + } + }, + "responses": { + "204": { + "description": "Digest unchanged; no update." + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "500": { + "description": "Failed to store or broadcast the configuration.", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ErrorResponse" + }, + { + "type": "string" + } + ] + } + } + } + }, + "202": { + "description": "Configuration accepted; asynchronous worker update scheduled." + } + }, + "parameters": [ + { + "name": "X-Digest", + "in": "header", + "required": true, + "description": "Client-chosen identifier for this snapshot. Use a new value for changed content.", + "schema": { + "type": "string" + } + } + ] + }, + "head": { + "tags": [ + "Standalone" + ], + "operationId": "headStandaloneConfig", + "summary": "Probe standalone configuration availability", + "description": "Returns `200 OK` when the API-driven standalone configuration endpoint is reachable, including before any snapshot is loaded. When available, digest and last-modified metadata are returned in headers. This endpoint does not confirm worker synchronization.", + "responses": { + "200": { + "description": "Standalone configuration is reachable." + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "500": { + "$ref": "#/components/responses/BadRequest", + "description": "Failed to retrieve or encode stored configuration." + } + }, + "parameters": [] + } + }, + "/apisix/admin/configs/validate": { + "post": { + "tags": [ + "Schema Validation" + ], + "operationId": "validateConfiguration", + "summary": "Validate a Configuration Without Applying It", + "description": "Validates a declarative APISIX configuration when the deployment uses either the `etcd` or `yaml` configuration provider. The endpoint accepts JSON and YAML request bodies up to 1.5 MiB and uses the normal Admin API authentication.\n\nValidation covers known resource arrays, numeric `*_conf_version` fields, duplicate resource identifiers, resource JSON Schemas, and plugin `check_schema` functions. All detected errors are returned together. Cross-resource existence is not checked, so a reference such as an `upstream_id` can pass even when the referenced resource is absent. The operation does not persist or apply any configuration.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConfigValidationDocument" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/ConfigValidationDocument" + } + } + } + }, + "responses": { + "200": { + "description": "The supplied configuration passed all applicable checks.", + "content": { + "application/json": { + "schema": { + "type": "object", + "maxProperties": 0 + }, + "example": {} + } + } + }, + "400": { + "description": "The request body could not be parsed, exceeded 1.5 MiB, or failed one or more configuration checks.", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ErrorResponse" + }, + { + "$ref": "#/components/schemas/ConfigValidationErrorResponse" + } + ] + }, + "examples": { + "invalidBody": { + "summary": "Malformed or empty request body", + "value": { + "error_msg": "invalid request body: empty request body" + } + }, + "configurationErrors": { + "summary": "Multiple configuration errors", + "value": { + "error_msg": "Configuration validation failed", + "errors": [ + { + "resource_type": "routes", + "resource_id": "route-a", + "index": 0, + "error": "property \"uri\" validation failed: wrong type" + }, + { + "resource_type": "upstreams", + "resource_id": "upstream-a", + "index": 0, + "error": "missing key" + } + ] + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + } + }, + "parameters": [] + } + }, + "/apisix/admin/routes/{id}/{sub_path}": { + "parameters": [ + { + "$ref": "#/components/parameters/IdInPath" + }, + { + "name": "sub_path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Slash-separated nested key path within the resource document.", + "example": "status" + } + ], + "patch": { + "tags": [ + "Routes" + ], + "operationId": "patchRoutesSubPath", + "summary": "Replace a single nested field of a route", + "description": "Replaces the value at `sub_path` within the route document with the request body. The body is any valid JSON value, not necessarily an object. The updated complete resource must pass validation.", + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": {}, + "example": 1 + } + } + }, + "responses": { + "200": { + "description": "Sub-field replaced." + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + } + } + }, + "/apisix/admin/services/{id}/{sub_path}": { + "parameters": [ + { + "$ref": "#/components/parameters/IdInPath" + }, + { + "name": "sub_path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Slash-separated nested key path within the resource document.", + "example": "enable_websocket" + } + ], + "patch": { + "tags": [ + "Services" + ], + "operationId": "patchServicesSubPath", + "summary": "Replace a single nested field of a service", + "description": "Replaces the value at `sub_path` within the service document with the request body. The body is any valid JSON value, not necessarily an object. The updated complete resource must pass validation.", + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": {}, + "example": true + } + } + }, + "responses": { + "200": { + "description": "Sub-field replaced." + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + } + } + }, + "/apisix/admin/upstreams/{id}/{sub_path}": { + "parameters": [ + { + "$ref": "#/components/parameters/IdInPath" + }, + { + "name": "sub_path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Slash-separated nested key path within the resource document.", + "example": "retries" + } + ], + "patch": { + "tags": [ + "Upstreams" + ], + "operationId": "patchUpstreamsSubPath", + "summary": "Replace a single nested field of an upstream", + "description": "Replaces the value at `sub_path` within the upstream document with the request body. The body is any valid JSON value, not necessarily an object. The updated complete resource must pass validation.", + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": {}, + "example": 3 + } + } + }, + "responses": { + "200": { + "description": "Sub-field replaced." + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + } + } + }, + "/apisix/admin/ssls/{id}/{sub_path}": { + "parameters": [ + { + "$ref": "#/components/parameters/IdInPath" + }, + { + "name": "sub_path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Slash-separated nested key path within the resource document.", + "example": "status" + } + ], + "patch": { + "tags": [ + "SSL Certificates" + ], + "operationId": "patchSSLCertificatesSubPath", + "summary": "Replace a single nested field of an SSL certificate", + "description": "Replaces the value at `sub_path` within the SSL certificate document with the request body. The body is any valid JSON value, not necessarily an object. The updated complete resource must pass validation.", + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": {}, + "example": 1 + } + } + }, + "responses": { + "200": { + "description": "Sub-field replaced." + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + } + } + }, + "/apisix/admin/consumer_groups/{id}/{sub_path}": { + "parameters": [ + { + "$ref": "#/components/parameters/IdInPath" + }, + { + "name": "sub_path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Slash-separated nested key path within the resource document.", + "example": "plugins" + } + ], + "patch": { + "tags": [ + "Consumer Groups" + ], + "operationId": "patchConsumerGroupsSubPath", + "summary": "Replace a single nested field of a consumer group", + "description": "Replaces the value at `sub_path` within the consumer group document with the request body. The body is any valid JSON value, not necessarily an object. The updated complete resource must pass validation.", + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": {}, + "example": { + "limit-count": { + "count": 100, + "time_window": 60, + "rejected_code": 429 + } + } + } + } + }, + "responses": { + "200": { + "description": "Sub-field replaced." + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + } + } + }, + "/apisix/admin/global_rules/{id}/{sub_path}": { + "parameters": [ + { + "$ref": "#/components/parameters/IdInPath" + }, + { + "name": "sub_path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Slash-separated nested key path within the resource document.", + "example": "plugins" + } + ], + "patch": { + "tags": [ + "Global Rules" + ], + "operationId": "patchGlobalRulesSubPath", + "summary": "Replace a single nested field of a global rule", + "description": "Replaces the value at `sub_path` within the global rule document with the request body. The body is any valid JSON value, not necessarily an object. The updated complete resource must pass validation.", + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": {}, + "example": { + "limit-count": { + "count": 100, + "time_window": 60, + "rejected_code": 429 + } + } + } + } + }, + "responses": { + "200": { + "description": "Sub-field replaced." + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + } + } + }, + "/apisix/admin/plugin_configs/{id}/{sub_path}": { + "parameters": [ + { + "$ref": "#/components/parameters/IdInPath" + }, + { + "name": "sub_path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Slash-separated nested key path within the resource document.", + "example": "plugins" + } + ], + "patch": { + "tags": [ + "Plugin Configs" + ], + "operationId": "patchPluginConfigsSubPath", + "summary": "Replace a single nested field of a plugin config", + "description": "Replaces the value at `sub_path` within the plugin config document with the request body. The body is any valid JSON value, not necessarily an object. The updated complete resource must pass validation.", + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": {}, + "example": { + "limit-count": { + "count": 100, + "time_window": 60, + "rejected_code": 429 + } + } + } + } + }, + "responses": { + "200": { + "description": "Sub-field replaced." + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + } + } + }, + "/apisix/admin/secrets/{secret_type}": { + "parameters": [ + { + "$ref": "#/components/parameters/SecretType" + } + ], + "get": { + "tags": [ + "Secrets" + ], + "operationId": "listSecretsByType", + "summary": "List secrets of a single secret-manager type", + "description": "Returns secrets registered under the given manager (`vault`, `aws`, or `gcp`). For listing across all managers, use `GET /apisix/admin/secrets`.", + "parameters": [], + "responses": { + "200": { + "description": "Paginated list of secrets for this manager.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "total": { + "type": "integer" + }, + "list": { + "type": "array", + "items": { + "type": "object" + } + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + } + } + }, + "/apisix/admin/secrets/{secret_type}/{id}/{sub_path}": { + "parameters": [ + { + "name": "secret_type", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": [ + "vault", + "aws", + "gcp" + ] + }, + "description": "Secret manager backend.", + "example": "gcp" + }, + { + "$ref": "#/components/parameters/IdInPath" + }, + { + "name": "sub_path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Slash-separated nested key path within the resource document.", + "example": "auth_config" + } + ], + "patch": { + "tags": [ + "Secrets" + ], + "operationId": "patchSecretSubPath", + "summary": "Replace a single nested field of a secret", + "description": "Replaces the value at `sub_path` within the secret document with the request body. The body is any valid JSON value, not necessarily an object. The updated complete resource must pass validation.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": {}, + "example": { + "client_email": "apisix@example.iam.gserviceaccount.com", + "private_key": "example-private-key", + "project_id": "example-project" + } + } + } + }, + "responses": { + "200": { + "description": "Sub-field replaced." + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "503": { + "$ref": "#/components/responses/BadRequest", + "description": "Storage access or write failed." + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ] + } + }, + "/apisix/admin/schema/{resource}": { + "parameters": [ + { + "$ref": "#/components/parameters/SchemaNameInPath" + } + ], + "get": { + "tags": [ + "Schema Validation" + ], + "operationId": "getResourceSchema", + "summary": "Get the JSON Schema of a built-in resource", + "description": "Returns the JSON Schema document used internally by APISIX to validate the named resource type.", + "responses": { + "200": { + "description": "JSON Schema document.", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + } + }, + "parameters": [] + } + }, + "/apisix/admin/schema/plugins/{plugin_name}": { + "parameters": [ + { + "$ref": "#/components/parameters/PluginNameInPath" + } + ], + "get": { + "tags": [ + "Schema Validation" + ], + "operationId": "getPluginSchemaAlt", + "summary": "Get a plugin's JSON Schema (alternative path)", + "description": "Equivalent to `GET /apisix/admin/plugins/{plugin_name}`. Provided for symmetry with `GET /apisix/admin/schema/{resource}`.", + "responses": { + "200": { + "description": "Plugin JSON Schema.", + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "JSON Schema of the loaded plugin, selected by subsystem and schema_type. Plugin schemas and Lua validation depend on the installed plugin set." + }, + "examples": { + "keyAuthConsumer": { + "summary": "key-auth with schema_type=consumer (from release/3.18 plugin source)", + "value": { + "type": "object", + "properties": { + "key": { + "type": "string" + } + }, + "encrypt_fields": [ + "key" + ], + "required": [ + "key" + ] + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + } + }, + "parameters": [ + { + "in": "query", + "name": "schema_type", + "description": "Use consumer to retrieve the consumer schema; otherwise returns the plugin schema.", + "schema": { + "type": "string" + }, + "example": "consumer" + } + ] + } + } + }, + "servers": [ + { + "url": "{protocol}://{host}:{port}", + "description": "APISIX Admin API Server", + "variables": { + "protocol": { + "default": "http", + "enum": [ + "http", + "https" + ], + "description": "Protocol scheme. Use `https` when `deployment.admin.https_admin` is enabled and `deployment.admin.admin_api_mtls.admin_ssl_cert` and `admin_ssl_cert_key` are configured." + }, + "host": { + "default": "127.0.0.1", + "description": "Admin API host address. Configure via `deployment.admin.admin_listen` in `config.yaml`." + }, + "port": { + "default": "9180", + "description": "Admin API port. Default is `9180`." + } + } + } + ], + "components": { + "securitySchemes": { + "adminKey": { + "type": "apiKey", + "in": "header", + "name": "X-API-KEY", + "description": "Admin API key configured in `config.yaml` under `deployment.admin.admin_key`. You can also pass the key as a query parameter `api_key` or cookie `x_api_key`." + }, + "apiKeyQuery": { + "type": "apiKey", + "in": "query", + "name": "api_key", + "description": "Admin API key passed as a query parameter. Convenient for `curl` experiments, but avoid in production — keys may be logged." + }, + "apiKeyCookie": { + "type": "apiKey", + "in": "cookie", + "name": "x_api_key", + "description": "Admin API key passed in a cookie." + } + }, + "schemas": { + "ErrorResponse": { + "type": "object", + "description": "Standard error response returned by the APISIX Admin API.", + "required": [ + "error_msg" + ], + "properties": { + "error_msg": { + "type": "string", + "description": "Human-readable error message describing what went wrong.", + "examples": [ + "invalid configuration: property \"uri\" is required" + ] + } + }, + "examples": [ + { + "error_msg": "invalid configuration: property \"uri\" is required" + } + ] + }, + "AuthErrorResponse": { + "type": "object", + "description": "Authentication error returned when the API key is missing or invalid.", + "required": [ + "error_msg" + ], + "properties": { + "error_msg": { + "type": "string", + "description": "Authentication error message.", + "examples": [ + "failed to check token" + ] + }, + "description": { + "type": "string", + "description": "Detailed reason for the authentication failure.", + "examples": [ + "missing apikey" + ] + } + }, + "examples": [ + { + "error_msg": "failed to check token", + "description": "missing apikey" + } + ] + }, + "NotFoundResponse": { + "description": "Etcd missing keys return message; handler and plugin failures return error_msg.", + "anyOf": [ + { + "type": "object", + "required": [ + "message" + ], + "properties": { + "message": { + "type": "string" + } + } + }, + { + "$ref": "#/components/schemas/ErrorResponse" + } + ] + }, + "ResourceId": { + "anyOf": [ + { + "minLength": 1, + "pattern": "^[a-zA-Z0-9-_.]+$", + "maxLength": 64, + "type": "string" + }, + { + "minimum": 1, + "type": "integer" + } + ], + "x-source": "apisix/schema_def.lua" + }, + "Timeout": { + "required": [ + "connect", + "send", + "read" + ], + "properties": { + "connect": { + "exclusiveMinimum": 0, + "type": "number" + }, + "send": { + "exclusiveMinimum": 0, + "type": "number" + }, + "read": { + "exclusiveMinimum": 0, + "type": "number" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "Labels": { + "description": "key/value pairs to specify attributes", + "patternProperties": { + ".*": { + "pattern": "^\\S+$", + "minLength": 1, + "description": "value of label", + "maxLength": 256, + "type": "string" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "HealthCheckActiveHealthy": { + "properties": { + "interval": { + "default": 1, + "minimum": 1, + "type": "integer" + }, + "http_statuses": { + "items": { + "maximum": 599, + "minimum": 200, + "type": "integer" + }, + "minItems": 1, + "uniqueItems": true, + "default": [ + 200, + 302 + ], + "type": "array" + }, + "successes": { + "default": 2, + "maximum": 254, + "minimum": 1, + "type": "integer" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "HealthCheckActiveUnhealthy": { + "properties": { + "interval": { + "default": 1, + "minimum": 1, + "type": "integer" + }, + "http_statuses": { + "items": { + "maximum": 599, + "minimum": 200, + "type": "integer" + }, + "minItems": 1, + "uniqueItems": true, + "default": [ + 429, + 404, + 500, + 501, + 502, + 503, + 504, + 505 + ], + "type": "array" + }, + "timeouts": { + "default": 3, + "maximum": 254, + "minimum": 1, + "type": "integer" + }, + "tcp_failures": { + "default": 2, + "maximum": 254, + "minimum": 1, + "type": "integer" + }, + "http_failures": { + "default": 5, + "maximum": 254, + "minimum": 1, + "type": "integer" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "HealthCheckActive": { + "properties": { + "http_method": { + "default": "GET", + "enum": [ + "GET", + "POST", + "PUT", + "DELETE", + "PATCH", + "HEAD", + "OPTIONS", + "CONNECT", + "TRACE", + "PURGE" + ], + "type": "string" + }, + "port": { + "maximum": 65535, + "minimum": 1, + "type": "integer" + }, + "host": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "timeout": { + "default": 1, + "type": "number" + }, + "unhealthy": { + "$ref": "#/components/schemas/HealthCheckActiveUnhealthy" + }, + "healthy": { + "$ref": "#/components/schemas/HealthCheckActiveHealthy" + }, + "https_verify_certificate": { + "default": true, + "type": "boolean" + }, + "req_headers": { + "items": { + "uniqueItems": true, + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "http_path": { + "default": "/", + "type": "string" + }, + "http_req_body": { + "default": "", + "type": "string" + }, + "concurrency": { + "default": 10, + "type": "integer" + }, + "type": { + "default": "http", + "enum": [ + "http", + "https", + "tcp" + ], + "type": "string" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "HealthCheckPassiveHealthy": { + "properties": { + "successes": { + "default": 5, + "maximum": 254, + "minimum": 0, + "type": "integer" + }, + "http_statuses": { + "items": { + "maximum": 599, + "minimum": 200, + "type": "integer" + }, + "minItems": 1, + "uniqueItems": true, + "default": [ + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 226, + 300, + 301, + 302, + 303, + 304, + 305, + 306, + 307, + 308 + ], + "type": "array" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "HealthCheckPassiveUnhealthy": { + "properties": { + "http_failures": { + "default": 5, + "maximum": 254, + "minimum": 0, + "type": "integer" + }, + "http_statuses": { + "items": { + "maximum": 599, + "minimum": 200, + "type": "integer" + }, + "minItems": 1, + "uniqueItems": true, + "default": [ + 429, + 500, + 503 + ], + "type": "array" + }, + "tcp_failures": { + "default": 2, + "maximum": 254, + "minimum": 0, + "type": "integer" + }, + "timeouts": { + "default": 7, + "maximum": 254, + "minimum": 0, + "type": "integer" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "HealthCheckPassive": { + "properties": { + "unhealthy": { + "$ref": "#/components/schemas/HealthCheckPassiveUnhealthy" + }, + "healthy": { + "$ref": "#/components/schemas/HealthCheckPassiveHealthy" + }, + "type": { + "default": "http", + "enum": [ + "http", + "https", + "tcp" + ], + "type": "string" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "HealthCheck": { + "anyOf": [ + { + "required": [ + "active" + ] + }, + { + "required": [ + "active", + "passive" + ] + } + ], + "properties": { + "passive": { + "$ref": "#/components/schemas/HealthCheckPassive" + }, + "active": { + "$ref": "#/components/schemas/HealthCheckActive" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "UpstreamNodesMap": { + "patternProperties": { + ".*": { + "description": "weight of node", + "minimum": 0, + "type": "integer" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "UpstreamNodeItem": { + "required": [ + "host", + "weight" + ], + "properties": { + "weight": { + "description": "weight of node", + "minimum": 0, + "type": "integer" + }, + "port": { + "maximum": 65535, + "description": "port of node", + "minimum": 1, + "type": "integer" + }, + "host": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "metadata": { + "type": "object", + "description": "metadata of node" + }, + "priority": { + "description": "priority of node", + "default": 0, + "type": "integer" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "UpstreamNodes": { + "anyOf": [ + { + "$ref": "#/components/schemas/UpstreamNodesMap" + }, + { + "items": { + "$ref": "#/components/schemas/UpstreamNodeItem" + }, + "type": "array" + } + ], + "x-source": "apisix/schema_def.lua" + }, + "UpstreamTLS": { + "properties": { + "client_key": { + "minLength": 64, + "maxLength": 65536, + "type": "string" + }, + "client_cert": { + "minLength": 128, + "maxLength": 65536, + "type": "string" + }, + "client_cert_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "verify": { + "description": "Turn on server certificate verification, currently only kafka upstream is supported", + "default": false, + "type": "boolean" + } + }, + "type": "object", + "dependentSchemas": { + "client_key": { + "required": [ + "client_cert" + ] + }, + "client_cert": { + "required": [ + "client_key" + ] + }, + "client_cert_id": { + "not": { + "required": [ + "client_cert", + "client_key" + ] + } + } + }, + "x-source": "apisix/schema_def.lua" + }, + "KeepalivePool": { + "properties": { + "size": { + "default": 320, + "minimum": 1, + "type": "integer" + }, + "requests": { + "default": 1000, + "minimum": 1, + "type": "integer" + }, + "idle_timeout": { + "default": 60, + "minimum": 0, + "type": "number" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "Upstream": { + "additionalProperties": false, + "properties": { + "tls": { + "$ref": "#/components/schemas/UpstreamTLS" + }, + "update_time": { + "type": "integer", + "readOnly": true + }, + "retry_timeout": { + "minimum": 0, + "type": "number" + }, + "scheme": { + "default": "http", + "description": "The scheme of the upstream. For L7 proxy, it can be one of grpc/grpcs/http/https. For L4 proxy, it can be one of tcp/tls/udp. For specific protocols, it can be kafka.", + "enum": [ + "grpc", + "grpcs", + "http", + "https", + "tcp", + "tls", + "udp", + "kafka" + ] + }, + "retries": { + "minimum": 0, + "type": "integer" + }, + "hash_on": { + "default": "vars", + "enum": [ + "vars", + "header", + "cookie", + "consumer", + "vars_combinations" + ], + "type": "string" + }, + "service_name": { + "minLength": 1, + "maxLength": 256, + "type": "string" + }, + "nodes": { + "$ref": "#/components/schemas/UpstreamNodes" + }, + "discovery_type": { + "type": "string", + "description": "discovery type" + }, + "upstream_host": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "timeout": { + "$ref": "#/components/schemas/Timeout" + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "keepalive_pool": { + "$ref": "#/components/schemas/KeepalivePool" + }, + "checks": { + "$ref": "#/components/schemas/HealthCheck" + }, + "key": { + "type": "string", + "description": "the key of chash for dynamic load balancing" + }, + "discovery_args": { + "properties": { + "group_name": { + "type": "string", + "description": "group name" + }, + "namespace_id": { + "type": "string", + "description": "namespace id" + } + }, + "type": "object" + }, + "name": { + "minLength": 1, + "maxLength": 256, + "type": "string" + }, + "pass_host": { + "default": "pass", + "description": "mod of host passing", + "enum": [ + "pass", + "node", + "rewrite" + ], + "type": "string" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "desc": { + "maxLength": 256, + "type": "string" + }, + "type": { + "description": "algorithms of load balancing", + "default": "roundrobin", + "type": "string" + } + }, + "oneOf": [ + { + "required": [ + "nodes" + ] + }, + { + "required": [ + "service_name", + "discovery_type" + ] + } + ], + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "Plugins": { + "type": "object", + "description": "Map of loaded plugin names to configuration objects. Each plugin has its own schema and Lua check_schema validation; retrieve it from /apisix/admin/schema/plugins/{plugin_name}.", + "additionalProperties": { + "type": "object" + } + }, + "Route": { + "anyOf": [ + { + "required": [ + "plugins", + "uri" + ] + }, + { + "required": [ + "upstream", + "uri" + ] + }, + { + "required": [ + "upstream_id", + "uri" + ] + }, + { + "required": [ + "service_id", + "uri" + ] + }, + { + "required": [ + "plugins", + "uris" + ] + }, + { + "required": [ + "upstream", + "uris" + ] + }, + { + "required": [ + "upstream_id", + "uris" + ] + }, + { + "required": [ + "service_id", + "uris" + ] + }, + { + "required": [ + "script", + "uri" + ] + }, + { + "required": [ + "script", + "uris" + ] + } + ], + "allOf": [ + { + "oneOf": [ + { + "required": [ + "uri" + ] + }, + { + "required": [ + "uris" + ] + } + ] + }, + { + "oneOf": [ + { + "not": { + "anyOf": [ + { + "required": [ + "host" + ] + }, + { + "required": [ + "hosts" + ] + } + ] + } + }, + { + "required": [ + "host" + ] + }, + { + "required": [ + "hosts" + ] + } + ] + }, + { + "oneOf": [ + { + "not": { + "anyOf": [ + { + "required": [ + "remote_addr" + ] + }, + { + "required": [ + "remote_addrs" + ] + } + ] + } + }, + { + "required": [ + "remote_addr" + ] + }, + { + "required": [ + "remote_addrs" + ] + } + ] + } + ], + "additionalProperties": false, + "not": { + "anyOf": [ + { + "required": [ + "script", + "plugins" + ] + }, + { + "required": [ + "script", + "plugin_config_id" + ] + } + ] + }, + "properties": { + "update_time": { + "type": "integer", + "readOnly": true + }, + "upstream": { + "$ref": "#/components/schemas/Upstream" + }, + "script": { + "minLength": 10, + "maxLength": 102400, + "type": "string" + }, + "plugins": { + "type": "object" + }, + "remote_addrs": { + "items": { + "description": "client IP", + "anyOf": [ + { + "format": "ipv4", + "type": "string", + "title": "IPv4" + }, + { + "pattern": "^([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/([12]?[0-9]|3[0-2])$", + "type": "string", + "title": "IPv4/CIDR" + }, + { + "format": "ipv6", + "type": "string", + "title": "IPv6" + }, + { + "pattern": "^([a-fA-F0-9]{0,4}:){1,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?/[0-9]{1,3}$", + "type": "string", + "title": "IPv6/CIDR" + } + ], + "type": "string" + }, + "minItems": 1, + "uniqueItems": true, + "type": "array" + }, + "priority": { + "default": 0, + "type": "integer" + }, + "hosts": { + "items": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "minItems": 1, + "uniqueItems": true, + "type": "array" + }, + "plugin_config_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "methods": { + "items": { + "description": "HTTP method", + "enum": [ + "GET", + "POST", + "PUT", + "DELETE", + "PATCH", + "HEAD", + "OPTIONS", + "CONNECT", + "TRACE", + "PURGE" + ], + "type": "string" + }, + "uniqueItems": true, + "type": "array" + }, + "uri": { + "minLength": 1, + "maxLength": 4096, + "type": "string" + }, + "enable_websocket": { + "type": "boolean", + "description": "enable websocket for request" + }, + "upstream_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "service_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "host": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "timeout": { + "$ref": "#/components/schemas/Timeout" + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "script_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "remote_addr": { + "description": "client IP", + "anyOf": [ + { + "format": "ipv4", + "type": "string", + "title": "IPv4" + }, + { + "pattern": "^([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/([12]?[0-9]|3[0-2])$", + "type": "string", + "title": "IPv4/CIDR" + }, + { + "format": "ipv6", + "type": "string", + "title": "IPv6" + }, + { + "pattern": "^([a-fA-F0-9]{0,4}:){1,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?/[0-9]{1,3}$", + "type": "string", + "title": "IPv6/CIDR" + } + ], + "type": "string" + }, + "status": { + "default": 1, + "description": "route status, 1 to enable, 0 to disable", + "enum": [ + 1, + 0 + ], + "type": "integer" + }, + "filter_func": { + "pattern": "^function", + "minLength": 10, + "type": "string" + }, + "vars": { + "type": "array" + }, + "name": { + "minLength": 1, + "maxLength": 256, + "type": "string" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "desc": { + "maxLength": 256, + "type": "string" + }, + "uris": { + "items": { + "type": "string", + "description": "HTTP uri" + }, + "minItems": 1, + "uniqueItems": true, + "type": "array" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "Service": { + "additionalProperties": false, + "properties": { + "update_time": { + "type": "integer", + "readOnly": true + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "upstream": { + "$ref": "#/components/schemas/Upstream" + }, + "upstream_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "plugins": { + "type": "object" + }, + "name": { + "minLength": 1, + "maxLength": 256, + "type": "string" + }, + "script": { + "minLength": 10, + "maxLength": 102400, + "type": "string" + }, + "hosts": { + "items": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "minItems": 1, + "uniqueItems": true, + "type": "array" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "desc": { + "maxLength": 256, + "type": "string" + }, + "enable_websocket": { + "type": "boolean", + "description": "enable websocket for request" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "Consumer": { + "required": [ + "username" + ], + "additionalProperties": false, + "properties": { + "plugins": { + "type": "object" + }, + "update_time": { + "type": "integer", + "readOnly": true + }, + "group_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "desc": { + "maxLength": 256, + "type": "string" + }, + "username": { + "minLength": 1, + "pattern": "^[a-zA-Z0-9_\\-]+$", + "maxLength": 256, + "type": "string" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "Credential": { + "additionalProperties": false, + "properties": { + "update_time": { + "type": "integer", + "readOnly": true + }, + "plugins": { + "maxProperties": 1, + "type": "object" + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "id": { + "oneOf": [ + { + "$ref": "#/components/schemas/ResourceId" + }, + { + "minLength": 15, + "pattern": "^[a-zA-Z0-9-_]+/credentials/[a-zA-Z0-9-_.]+$", + "maxLength": 128, + "type": "string" + } + ] + }, + "desc": { + "maxLength": 256, + "type": "string" + }, + "name": { + "minLength": 1, + "maxLength": 256, + "type": "string" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "ConsumerGroup": { + "required": [ + "plugins" + ], + "additionalProperties": false, + "properties": { + "update_time": { + "type": "integer", + "readOnly": true + }, + "plugins": { + "type": "object" + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "desc": { + "maxLength": 256, + "type": "string" + }, + "name": { + "minLength": 1, + "maxLength": 256, + "type": "string" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "SSL": { + "additionalProperties": false, + "else": { + "required": [ + "key", + "cert" + ] + }, + "if": { + "properties": { + "type": { + "enum": [ + "server" + ] + } + } + }, + "then": { + "oneOf": [ + { + "required": [ + "sni", + "key", + "cert" + ] + }, + { + "required": [ + "snis", + "key", + "cert" + ] + } + ] + }, + "properties": { + "snis": { + "items": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "update_time": { + "type": "integer", + "readOnly": true + }, + "ssl_protocols": { + "items": { + "enum": [ + "TLSv1.1", + "TLSv1.2", + "TLSv1.3" + ] + }, + "maxItems": 3, + "description": "set ssl protocols", + "uniqueItems": true, + "type": "array" + }, + "key": { + "minLength": 64, + "maxLength": 65536, + "type": "string" + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "status": { + "default": 1, + "description": "ssl status, 1 to enable, 0 to disable", + "enum": [ + 1, + 0 + ], + "type": "integer" + }, + "cert": { + "minLength": 128, + "maxLength": 65536, + "type": "string" + }, + "client": { + "required": [ + "ca" + ], + "properties": { + "ca": { + "minLength": 128, + "maxLength": 65536, + "type": "string" + }, + "skip_mtls_uri_regex": { + "uniqueItems": true, + "minItems": 1, + "items": { + "type": "string", + "description": "uri regular expression to skip mtls" + }, + "type": "array" + }, + "depth": { + "default": 1, + "minimum": 0, + "type": "integer" + } + }, + "type": "object" + }, + "sni": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "certs": { + "items": { + "minLength": 128, + "maxLength": 65536, + "type": "string" + }, + "type": "array" + }, + "keys": { + "items": { + "minLength": 64, + "maxLength": 65536, + "type": "string" + }, + "type": "array" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "desc": { + "maxLength": 256, + "type": "string" + }, + "type": { + "enum": [ + "server", + "client" + ], + "description": "ssl certificate type, server to server certificate, client to client certificate for upstream", + "default": "server", + "type": "string" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "GlobalRule": { + "required": [ + "plugins" + ], + "additionalProperties": false, + "properties": { + "plugins": { + "type": "object" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "update_time": { + "type": "integer", + "readOnly": true + }, + "create_time": { + "type": "integer", + "readOnly": true + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "PluginConfig": { + "required": [ + "plugins" + ], + "additionalProperties": false, + "properties": { + "update_time": { + "type": "integer", + "readOnly": true + }, + "plugins": { + "type": "object" + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "desc": { + "maxLength": 256, + "type": "string" + }, + "name": { + "type": "string" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "StreamRoute": { + "additionalProperties": false, + "properties": { + "update_time": { + "type": "integer", + "readOnly": true + }, + "protocol": { + "required": [ + "name" + ], + "properties": { + "superior_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "name": { + "type": "string" + }, + "logger": { + "items": { + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "conf": { + "type": "object", + "description": "logger plugin configuration" + }, + "filter": { + "type": "array", + "description": "logger filter rules" + } + }, + "dependentRequired": { + "name": [ + "conf" + ] + } + }, + "type": "array" + }, + "conf": { + "type": "object", + "description": "protocol-specific configuration" + } + }, + "type": "object" + }, + "upstream_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "service_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "upstream": { + "$ref": "#/components/schemas/Upstream" + }, + "remote_addr": { + "description": "client IP", + "anyOf": [ + { + "format": "ipv4", + "type": "string", + "title": "IPv4" + }, + { + "pattern": "^([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/([12]?[0-9]|3[0-2])$", + "type": "string", + "title": "IPv4/CIDR" + }, + { + "format": "ipv6", + "type": "string", + "title": "IPv6" + }, + { + "pattern": "^([a-fA-F0-9]{0,4}:){1,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?/[0-9]{1,3}$", + "type": "string", + "title": "IPv6/CIDR" + } + ], + "type": "string" + }, + "plugins": { + "type": "object" + }, + "sni": { + "description": "server name indication", + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "server_port": { + "maximum": 65535, + "description": "server port", + "minimum": 1, + "type": "integer" + }, + "server_addr": { + "description": "server IP", + "anyOf": [ + { + "format": "ipv4", + "type": "string", + "title": "IPv4" + }, + { + "pattern": "^([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/([12]?[0-9]|3[0-2])$", + "type": "string", + "title": "IPv4/CIDR" + }, + { + "format": "ipv6", + "type": "string", + "title": "IPv6" + }, + { + "pattern": "^([a-fA-F0-9]{0,4}:){1,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?/[0-9]{1,3}$", + "type": "string", + "title": "IPv6/CIDR" + } + ], + "type": "string" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "desc": { + "maxLength": 256, + "type": "string" + }, + "name": { + "minLength": 1, + "maxLength": 256, + "type": "string" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "Proto": { + "required": [ + "content" + ], + "additionalProperties": false, + "properties": { + "update_time": { + "type": "integer", + "readOnly": true + }, + "content": { + "minLength": 1, + "maxLength": 1048576, + "type": "string" + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "desc": { + "maxLength": 256, + "type": "string" + }, + "name": { + "minLength": 1, + "maxLength": 256, + "type": "string" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "SSLRead": { + "additionalProperties": false, + "properties": { + "snis": { + "items": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "update_time": { + "type": "integer", + "readOnly": true + }, + "ssl_protocols": { + "items": { + "enum": [ + "TLSv1.1", + "TLSv1.2", + "TLSv1.3" + ] + }, + "maxItems": 3, + "description": "set ssl protocols", + "uniqueItems": true, + "type": "array" + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "status": { + "default": 1, + "description": "ssl status, 1 to enable, 0 to disable", + "enum": [ + 1, + 0 + ], + "type": "integer" + }, + "cert": { + "minLength": 128, + "maxLength": 65536, + "type": "string" + }, + "client": { + "required": [ + "ca" + ], + "properties": { + "ca": { + "minLength": 128, + "maxLength": 65536, + "type": "string" + }, + "skip_mtls_uri_regex": { + "uniqueItems": true, + "minItems": 1, + "items": { + "type": "string", + "description": "uri regular expression to skip mtls" + }, + "type": "array" + }, + "depth": { + "default": 1, + "minimum": 0, + "type": "integer" + } + }, + "type": "object" + }, + "sni": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "certs": { + "items": { + "minLength": 128, + "maxLength": 65536, + "type": "string" + }, + "type": "array" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "desc": { + "maxLength": 256, + "type": "string" + }, + "type": { + "enum": [ + "server", + "client" + ], + "description": "ssl certificate type, server to server certificate, client to client certificate for upstream", + "default": "server", + "type": "string" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "CredentialEnvelope": { + "type": "object", + "description": "`Credential` wrapped in the etcd-style envelope.", + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/Credential" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "required": [ + "key", + "value" + ] + }, + "CredentialListEnvelope": { + "type": "object", + "description": "Paginated list of `Credential` envelopes.", + "properties": { + "total": { + "type": "integer" + }, + "list": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CredentialEnvelope" + } + } + }, + "required": [ + "total", + "list" + ] + }, + "RouteEnvelope": { + "type": "object", + "description": "`Route` wrapped in the etcd-style envelope.", + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/Route" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "required": [ + "key", + "value" + ] + }, + "RouteListEnvelope": { + "type": "object", + "description": "Paginated list of `Route` envelopes.", + "properties": { + "total": { + "type": "integer" + }, + "list": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RouteEnvelope" + } + } + }, + "required": [ + "total", + "list" + ] + }, + "ServiceEnvelope": { + "type": "object", + "description": "`Service` wrapped in the etcd-style envelope.", + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/Service" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "required": [ + "key", + "value" + ] + }, + "ServiceListEnvelope": { + "type": "object", + "description": "Paginated list of `Service` envelopes.", + "properties": { + "total": { + "type": "integer" + }, + "list": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ServiceEnvelope" + } + } + }, + "required": [ + "total", + "list" + ] + }, + "UpstreamEnvelope": { + "type": "object", + "description": "`Upstream` wrapped in the etcd-style envelope.", + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/Upstream" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "required": [ + "key", + "value" + ] + }, + "UpstreamListEnvelope": { + "type": "object", + "description": "Paginated list of `Upstream` envelopes.", + "properties": { + "total": { + "type": "integer" + }, + "list": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UpstreamEnvelope" + } + } + }, + "required": [ + "total", + "list" + ] + }, + "ConsumerEnvelope": { + "type": "object", + "description": "`Consumer` wrapped in the etcd-style envelope.", + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/Consumer" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "required": [ + "key", + "value" + ] + }, + "ConsumerListEnvelope": { + "type": "object", + "description": "Paginated list of `Consumer` envelopes.", + "properties": { + "total": { + "type": "integer" + }, + "list": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ConsumerEnvelope" + } + } + }, + "required": [ + "total", + "list" + ] + }, + "ConsumerGroupEnvelope": { + "type": "object", + "description": "`ConsumerGroup` wrapped in the etcd-style envelope.", + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/ConsumerGroup" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "required": [ + "key", + "value" + ] + }, + "ConsumerGroupListEnvelope": { + "type": "object", + "description": "Paginated list of `ConsumerGroup` envelopes.", + "properties": { + "total": { + "type": "integer" + }, + "list": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ConsumerGroupEnvelope" + } + } + }, + "required": [ + "total", + "list" + ] + }, + "SSLEnvelope": { + "type": "object", + "description": "`SSLRead` wrapped in the etcd-style envelope.", + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/SSLRead" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "required": [ + "key", + "value" + ] + }, + "SSLListEnvelope": { + "type": "object", + "description": "Paginated list of `SSLRead` envelopes.", + "properties": { + "total": { + "type": "integer" + }, + "list": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SSLEnvelope" + } + } + }, + "required": [ + "total", + "list" + ] + }, + "GlobalRuleEnvelope": { + "type": "object", + "description": "`GlobalRule` wrapped in the etcd-style envelope.", + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/GlobalRule" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "required": [ + "key", + "value" + ] + }, + "GlobalRuleListEnvelope": { + "type": "object", + "description": "Paginated list of `GlobalRule` envelopes.", + "properties": { + "total": { + "type": "integer" + }, + "list": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GlobalRuleEnvelope" + } + } + }, + "required": [ + "total", + "list" + ] + }, + "PluginConfigEnvelope": { + "type": "object", + "description": "`PluginConfig` wrapped in the etcd-style envelope.", + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/PluginConfig" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "required": [ + "key", + "value" + ] + }, + "PluginConfigListEnvelope": { + "type": "object", + "description": "Paginated list of `PluginConfig` envelopes.", + "properties": { + "total": { + "type": "integer" + }, + "list": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PluginConfigEnvelope" + } + } + }, + "required": [ + "total", + "list" + ] + }, + "StreamRouteEnvelope": { + "type": "object", + "description": "`StreamRoute` wrapped in the etcd-style envelope.", + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/StreamRoute" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "required": [ + "key", + "value" + ] + }, + "StreamRouteListEnvelope": { + "type": "object", + "description": "Paginated list of `StreamRoute` envelopes.", + "properties": { + "total": { + "type": "integer" + }, + "list": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StreamRouteEnvelope" + } + } + }, + "required": [ + "total", + "list" + ] + }, + "ProtoEnvelope": { + "type": "object", + "description": "`Proto` wrapped in the etcd-style envelope.", + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/Proto" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "required": [ + "key", + "value" + ] + }, + "ProtoListEnvelope": { + "type": "object", + "description": "Paginated list of `Proto` envelopes.", + "properties": { + "total": { + "type": "integer" + }, + "list": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProtoEnvelope" + } + } + }, + "required": [ + "total", + "list" + ] + }, + "RoutePatch": { + "type": "object", + "properties": { + "upstream": { + "anyOf": [ + { + "type": "object", + "properties": { + "tls": { + "anyOf": [ + { + "type": "object", + "properties": { + "client_key": { + "anyOf": [ + { + "minLength": 64, + "maxLength": 65536, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "client_cert": { + "anyOf": [ + { + "minLength": 128, + "maxLength": 65536, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "client_cert_id": { + "anyOf": [ + { + "anyOf": [ + { + "minLength": 1, + "pattern": "^[a-zA-Z0-9-_.]+$", + "maxLength": 64, + "type": "string" + }, + { + "type": "null" + } + ] + }, + { + "anyOf": [ + { + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + { + "type": "null" + } + ] + }, + "verify": { + "anyOf": [ + { + "description": "Turn on server certificate verification, currently only kafka upstream is supported", + "type": "boolean" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "update_time": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "retry_timeout": { + "anyOf": [ + { + "minimum": 0, + "type": "number" + }, + { + "type": "null" + } + ] + }, + "scheme": { + "anyOf": [ + { + "description": "The scheme of the upstream. For L7 proxy, it can be one of grpc/grpcs/http/https. For L4 proxy, it can be one of tcp/tls/udp. For specific protocols, it can be kafka.", + "enum": [ + "grpc", + "grpcs", + "http", + "https", + "tcp", + "tls", + "udp", + "kafka" + ] + }, + { + "type": "null" + } + ] + }, + "retries": { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "hash_on": { + "anyOf": [ + { + "enum": [ + "vars", + "header", + "cookie", + "consumer", + "vars_combinations" + ], + "type": "string" + }, + { + "type": "null" + } + ] + }, + "service_name": { + "anyOf": [ + { + "minLength": 1, + "maxLength": 256, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodes": { + "anyOf": [ + { + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ] + }, + { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/UpstreamNodeItem" + }, + "type": "array" + }, + { + "type": "null" + } + ] + }, + { + "type": "null" + } + ] + }, + "discovery_type": { + "anyOf": [ + { + "type": "string", + "description": "discovery type" + }, + { + "type": "null" + } + ] + }, + "upstream_host": { + "anyOf": [ + { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + { + "type": "null" + } + ] + }, + "timeout": { + "anyOf": [ + { + "type": "object", + "properties": { + "connect": { + "anyOf": [ + { + "exclusiveMinimum": 0, + "type": "number" + }, + { + "type": "null" + } + ] + }, + "send": { + "anyOf": [ + { + "exclusiveMinimum": 0, + "type": "number" + }, + { + "type": "null" + } + ] + }, + "read": { + "anyOf": [ + { + "exclusiveMinimum": 0, + "type": "number" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "create_time": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "labels": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ] + }, + "keepalive_pool": { + "anyOf": [ + { + "type": "object", + "properties": { + "size": { + "anyOf": [ + { + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "requests": { + "anyOf": [ + { + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "idle_timeout": { + "anyOf": [ + { + "minimum": 0, + "type": "number" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "checks": { + "anyOf": [ + { + "type": "object", + "properties": { + "passive": { + "anyOf": [ + { + "type": "object", + "properties": { + "unhealthy": { + "anyOf": [ + { + "type": "object", + "properties": { + "http_failures": { + "anyOf": [ + { + "maximum": 254, + "minimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "http_statuses": { + "anyOf": [ + { + "items": { + "maximum": 599, + "minimum": 200, + "type": "integer" + }, + "minItems": 1, + "uniqueItems": true, + "type": "array" + }, + { + "type": "null" + } + ] + }, + "tcp_failures": { + "anyOf": [ + { + "maximum": 254, + "minimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "timeouts": { + "anyOf": [ + { + "maximum": 254, + "minimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "healthy": { + "anyOf": [ + { + "type": "object", + "properties": { + "successes": { + "anyOf": [ + { + "maximum": 254, + "minimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "http_statuses": { + "anyOf": [ + { + "items": { + "maximum": 599, + "minimum": 200, + "type": "integer" + }, + "minItems": 1, + "uniqueItems": true, + "type": "array" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "type": { + "anyOf": [ + { + "enum": [ + "http", + "https", + "tcp" + ], + "type": "string" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "active": { + "anyOf": [ + { + "type": "object", + "properties": { + "http_method": { + "anyOf": [ + { + "enum": [ + "GET", + "POST", + "PUT", + "DELETE", + "PATCH", + "HEAD", + "OPTIONS", + "CONNECT", + "TRACE", + "PURGE" + ], + "type": "string" + }, + { + "type": "null" + } + ] + }, + "port": { + "anyOf": [ + { + "maximum": 65535, + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "host": { + "anyOf": [ + { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + { + "type": "null" + } + ] + }, + "timeout": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "unhealthy": { + "anyOf": [ + { + "type": "object", + "properties": { + "interval": { + "anyOf": [ + { + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "http_statuses": { + "anyOf": [ + { + "items": { + "maximum": 599, + "minimum": 200, + "type": "integer" + }, + "minItems": 1, + "uniqueItems": true, + "type": "array" + }, + { + "type": "null" + } + ] + }, + "timeouts": { + "anyOf": [ + { + "maximum": 254, + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "tcp_failures": { + "anyOf": [ + { + "maximum": 254, + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "http_failures": { + "anyOf": [ + { + "maximum": 254, + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "healthy": { + "anyOf": [ + { + "type": "object", + "properties": { + "interval": { + "anyOf": [ + { + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "http_statuses": { + "anyOf": [ + { + "items": { + "maximum": 599, + "minimum": 200, + "type": "integer" + }, + "minItems": 1, + "uniqueItems": true, + "type": "array" + }, + { + "type": "null" + } + ] + }, + "successes": { + "anyOf": [ + { + "maximum": 254, + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "https_verify_certificate": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "req_headers": { + "anyOf": [ + { + "items": { + "uniqueItems": true, + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + { + "type": "null" + } + ] + }, + "http_path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "http_req_body": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "concurrency": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "type": { + "anyOf": [ + { + "enum": [ + "http", + "https", + "tcp" + ], + "type": "string" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "key": { + "anyOf": [ + { + "type": "string", + "description": "the key of chash for dynamic load balancing" + }, + { + "type": "null" + } + ] + }, + "discovery_args": { + "anyOf": [ + { + "type": "object", + "properties": { + "group_name": { + "anyOf": [ + { + "type": "string", + "description": "group name" + }, + { + "type": "null" + } + ] + }, + "namespace_id": { + "anyOf": [ + { + "type": "string", + "description": "namespace id" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "name": { + "anyOf": [ + { + "minLength": 1, + "maxLength": 256, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "pass_host": { + "anyOf": [ + { + "description": "mod of host passing", + "enum": [ + "pass", + "node", + "rewrite" + ], + "type": "string" + }, + { + "type": "null" + } + ] + }, + "id": { + "anyOf": [ + { + "anyOf": [ + { + "minLength": 1, + "pattern": "^[a-zA-Z0-9-_.]+$", + "maxLength": 64, + "type": "string" + }, + { + "type": "null" + } + ] + }, + { + "anyOf": [ + { + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + { + "type": "null" + } + ] + }, + "desc": { + "anyOf": [ + { + "maxLength": 256, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "type": { + "anyOf": [ + { + "description": "algorithms of load balancing", + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "script": { + "anyOf": [ + { + "minLength": 10, + "maxLength": 102400, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "plugins": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ] + }, + "remote_addrs": { + "anyOf": [ + { + "items": { + "description": "client IP", + "anyOf": [ + { + "format": "ipv4", + "type": "string", + "title": "IPv4" + }, + { + "pattern": "^([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/([12]?[0-9]|3[0-2])$", + "type": "string", + "title": "IPv4/CIDR" + }, + { + "format": "ipv6", + "type": "string", + "title": "IPv6" + }, + { + "pattern": "^([a-fA-F0-9]{0,4}:){1,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?/[0-9]{1,3}$", + "type": "string", + "title": "IPv6/CIDR" + } + ], + "type": "string" + }, + "minItems": 1, + "uniqueItems": true, + "type": "array" + }, + { + "type": "null" + } + ] + }, + "priority": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "hosts": { + "anyOf": [ + { + "items": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "minItems": 1, + "uniqueItems": true, + "type": "array" + }, + { + "type": "null" + } + ] + }, + "plugin_config_id": { + "anyOf": [ + { + "anyOf": [ + { + "minLength": 1, + "pattern": "^[a-zA-Z0-9-_.]+$", + "maxLength": 64, + "type": "string" + }, + { + "type": "null" + } + ] + }, + { + "anyOf": [ + { + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + { + "type": "null" + } + ] + }, + "methods": { + "anyOf": [ + { + "items": { + "description": "HTTP method", + "enum": [ + "GET", + "POST", + "PUT", + "DELETE", + "PATCH", + "HEAD", + "OPTIONS", + "CONNECT", + "TRACE", + "PURGE" + ], + "type": "string" + }, + "uniqueItems": true, + "type": "array" + }, + { + "type": "null" + } + ] + }, + "uri": { + "anyOf": [ + { + "minLength": 1, + "maxLength": 4096, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "enable_websocket": { + "anyOf": [ + { + "type": "boolean", + "description": "enable websocket for request" + }, + { + "type": "null" + } + ] + }, + "upstream_id": { + "anyOf": [ + { + "anyOf": [ + { + "minLength": 1, + "pattern": "^[a-zA-Z0-9-_.]+$", + "maxLength": 64, + "type": "string" + }, + { + "type": "null" + } + ] + }, + { + "anyOf": [ + { + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + { + "type": "null" + } + ] + }, + "service_id": { + "anyOf": [ + { + "anyOf": [ + { + "minLength": 1, + "pattern": "^[a-zA-Z0-9-_.]+$", + "maxLength": 64, + "type": "string" + }, + { + "type": "null" + } + ] + }, + { + "anyOf": [ + { + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + { + "type": "null" + } + ] + }, + "host": { + "anyOf": [ + { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + { + "type": "null" + } + ] + }, + "timeout": { + "anyOf": [ + { + "type": "object", + "properties": { + "connect": { + "anyOf": [ + { + "exclusiveMinimum": 0, + "type": "number" + }, + { + "type": "null" + } + ] + }, + "send": { + "anyOf": [ + { + "exclusiveMinimum": 0, + "type": "number" + }, + { + "type": "null" + } + ] + }, + "read": { + "anyOf": [ + { + "exclusiveMinimum": 0, + "type": "number" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "labels": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ] + }, + "script_id": { + "anyOf": [ + { + "anyOf": [ + { + "minLength": 1, + "pattern": "^[a-zA-Z0-9-_.]+$", + "maxLength": 64, + "type": "string" + }, + { + "type": "null" + } + ] + }, + { + "anyOf": [ + { + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + { + "type": "null" + } + ] + }, + "remote_addr": { + "anyOf": [ + { + "anyOf": [ + { + "format": "ipv4", + "type": "string", + "title": "IPv4" + }, + { + "type": "null" + } + ] + }, + { + "anyOf": [ + { + "pattern": "^([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/([12]?[0-9]|3[0-2])$", + "type": "string", + "title": "IPv4/CIDR" + }, + { + "type": "null" + } + ] + }, + { + "anyOf": [ + { + "format": "ipv6", + "type": "string", + "title": "IPv6" + }, + { + "type": "null" + } + ] + }, + { + "anyOf": [ + { + "pattern": "^([a-fA-F0-9]{0,4}:){1,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?/[0-9]{1,3}$", + "type": "string", + "title": "IPv6/CIDR" + }, + { + "type": "null" + } + ] + }, + { + "type": "null" + } + ] + }, + "status": { + "anyOf": [ + { + "description": "route status, 1 to enable, 0 to disable", + "enum": [ + 1, + 0 + ], + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "filter_func": { + "anyOf": [ + { + "pattern": "^function", + "minLength": 10, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "vars": { + "anyOf": [ + { + "type": "array" + }, + { + "type": "null" + } + ] + }, + "name": { + "anyOf": [ + { + "minLength": 1, + "maxLength": 256, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "desc": { + "anyOf": [ + { + "maxLength": 256, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "uris": { + "anyOf": [ + { + "items": { + "type": "string", + "description": "HTTP uri" + }, + "minItems": 1, + "uniqueItems": true, + "type": "array" + }, + { + "type": "null" + } + ] + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "update_time": { + "type": "integer", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Recursive merge fragment. Null deletes object fields; arrays are replaced. APISIX validates the resulting resource, including plugin and reference checks." + }, + "ServicePatch": { + "type": "object", + "properties": { + "labels": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ] + }, + "upstream": { + "anyOf": [ + { + "type": "object", + "properties": { + "tls": { + "anyOf": [ + { + "type": "object", + "properties": { + "client_key": { + "anyOf": [ + { + "minLength": 64, + "maxLength": 65536, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "client_cert": { + "anyOf": [ + { + "minLength": 128, + "maxLength": 65536, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "client_cert_id": { + "anyOf": [ + { + "anyOf": [ + { + "minLength": 1, + "pattern": "^[a-zA-Z0-9-_.]+$", + "maxLength": 64, + "type": "string" + }, + { + "type": "null" + } + ] + }, + { + "anyOf": [ + { + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + { + "type": "null" + } + ] + }, + "verify": { + "anyOf": [ + { + "description": "Turn on server certificate verification, currently only kafka upstream is supported", + "type": "boolean" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "update_time": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "retry_timeout": { + "anyOf": [ + { + "minimum": 0, + "type": "number" + }, + { + "type": "null" + } + ] + }, + "scheme": { + "anyOf": [ + { + "description": "The scheme of the upstream. For L7 proxy, it can be one of grpc/grpcs/http/https. For L4 proxy, it can be one of tcp/tls/udp. For specific protocols, it can be kafka.", + "enum": [ + "grpc", + "grpcs", + "http", + "https", + "tcp", + "tls", + "udp", + "kafka" + ] + }, + { + "type": "null" + } + ] + }, + "retries": { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "hash_on": { + "anyOf": [ + { + "enum": [ + "vars", + "header", + "cookie", + "consumer", + "vars_combinations" + ], + "type": "string" + }, + { + "type": "null" + } + ] + }, + "service_name": { + "anyOf": [ + { + "minLength": 1, + "maxLength": 256, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodes": { + "anyOf": [ + { + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ] + }, + { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/UpstreamNodeItem" + }, + "type": "array" + }, + { + "type": "null" + } + ] + }, + { + "type": "null" + } + ] + }, + "discovery_type": { + "anyOf": [ + { + "type": "string", + "description": "discovery type" + }, + { + "type": "null" + } + ] + }, + "upstream_host": { + "anyOf": [ + { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + { + "type": "null" + } + ] + }, + "timeout": { + "anyOf": [ + { + "type": "object", + "properties": { + "connect": { + "anyOf": [ + { + "exclusiveMinimum": 0, + "type": "number" + }, + { + "type": "null" + } + ] + }, + "send": { + "anyOf": [ + { + "exclusiveMinimum": 0, + "type": "number" + }, + { + "type": "null" + } + ] + }, + "read": { + "anyOf": [ + { + "exclusiveMinimum": 0, + "type": "number" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "create_time": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "labels": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ] + }, + "keepalive_pool": { + "anyOf": [ + { + "type": "object", + "properties": { + "size": { + "anyOf": [ + { + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "requests": { + "anyOf": [ + { + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "idle_timeout": { + "anyOf": [ + { + "minimum": 0, + "type": "number" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "checks": { + "anyOf": [ + { + "type": "object", + "properties": { + "passive": { + "anyOf": [ + { + "type": "object", + "properties": { + "unhealthy": { + "anyOf": [ + { + "type": "object", + "properties": { + "http_failures": { + "anyOf": [ + { + "maximum": 254, + "minimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "http_statuses": { + "anyOf": [ + { + "items": { + "maximum": 599, + "minimum": 200, + "type": "integer" + }, + "minItems": 1, + "uniqueItems": true, + "type": "array" + }, + { + "type": "null" + } + ] + }, + "tcp_failures": { + "anyOf": [ + { + "maximum": 254, + "minimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "timeouts": { + "anyOf": [ + { + "maximum": 254, + "minimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "healthy": { + "anyOf": [ + { + "type": "object", + "properties": { + "successes": { + "anyOf": [ + { + "maximum": 254, + "minimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "http_statuses": { + "anyOf": [ + { + "items": { + "maximum": 599, + "minimum": 200, + "type": "integer" + }, + "minItems": 1, + "uniqueItems": true, + "type": "array" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "type": { + "anyOf": [ + { + "enum": [ + "http", + "https", + "tcp" + ], + "type": "string" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "active": { + "anyOf": [ + { + "type": "object", + "properties": { + "http_method": { + "anyOf": [ + { + "enum": [ + "GET", + "POST", + "PUT", + "DELETE", + "PATCH", + "HEAD", + "OPTIONS", + "CONNECT", + "TRACE", + "PURGE" + ], + "type": "string" + }, + { + "type": "null" + } + ] + }, + "port": { + "anyOf": [ + { + "maximum": 65535, + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "host": { + "anyOf": [ + { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + { + "type": "null" + } + ] + }, + "timeout": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "unhealthy": { + "anyOf": [ + { + "type": "object", + "properties": { + "interval": { + "anyOf": [ + { + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "http_statuses": { + "anyOf": [ + { + "items": { + "maximum": 599, + "minimum": 200, + "type": "integer" + }, + "minItems": 1, + "uniqueItems": true, + "type": "array" + }, + { + "type": "null" + } + ] + }, + "timeouts": { + "anyOf": [ + { + "maximum": 254, + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "tcp_failures": { + "anyOf": [ + { + "maximum": 254, + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "http_failures": { + "anyOf": [ + { + "maximum": 254, + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "healthy": { + "anyOf": [ + { + "type": "object", + "properties": { + "interval": { + "anyOf": [ + { + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "http_statuses": { + "anyOf": [ + { + "items": { + "maximum": 599, + "minimum": 200, + "type": "integer" + }, + "minItems": 1, + "uniqueItems": true, + "type": "array" + }, + { + "type": "null" + } + ] + }, + "successes": { + "anyOf": [ + { + "maximum": 254, + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "https_verify_certificate": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "req_headers": { + "anyOf": [ + { + "items": { + "uniqueItems": true, + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + { + "type": "null" + } + ] + }, + "http_path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "http_req_body": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "concurrency": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "type": { + "anyOf": [ + { + "enum": [ + "http", + "https", + "tcp" + ], + "type": "string" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "key": { + "anyOf": [ + { + "type": "string", + "description": "the key of chash for dynamic load balancing" + }, + { + "type": "null" + } + ] + }, + "discovery_args": { + "anyOf": [ + { + "type": "object", + "properties": { + "group_name": { + "anyOf": [ + { + "type": "string", + "description": "group name" + }, + { + "type": "null" + } + ] + }, + "namespace_id": { + "anyOf": [ + { + "type": "string", + "description": "namespace id" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "name": { + "anyOf": [ + { + "minLength": 1, + "maxLength": 256, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "pass_host": { + "anyOf": [ + { + "description": "mod of host passing", + "enum": [ + "pass", + "node", + "rewrite" + ], + "type": "string" + }, + { + "type": "null" + } + ] + }, + "id": { + "anyOf": [ + { + "anyOf": [ + { + "minLength": 1, + "pattern": "^[a-zA-Z0-9-_.]+$", + "maxLength": 64, + "type": "string" + }, + { + "type": "null" + } + ] + }, + { + "anyOf": [ + { + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + { + "type": "null" + } + ] + }, + "desc": { + "anyOf": [ + { + "maxLength": 256, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "type": { + "anyOf": [ + { + "description": "algorithms of load balancing", + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "upstream_id": { + "anyOf": [ + { + "anyOf": [ + { + "minLength": 1, + "pattern": "^[a-zA-Z0-9-_.]+$", + "maxLength": 64, + "type": "string" + }, + { + "type": "null" + } + ] + }, + { + "anyOf": [ + { + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + { + "type": "null" + } + ] + }, + "plugins": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ] + }, + "name": { + "anyOf": [ + { + "minLength": 1, + "maxLength": 256, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "script": { + "anyOf": [ + { + "minLength": 10, + "maxLength": 102400, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "hosts": { + "anyOf": [ + { + "items": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "minItems": 1, + "uniqueItems": true, + "type": "array" + }, + { + "type": "null" + } + ] + }, + "desc": { + "anyOf": [ + { + "maxLength": 256, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "enable_websocket": { + "anyOf": [ + { + "type": "boolean", + "description": "enable websocket for request" + }, + { + "type": "null" + } + ] + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "update_time": { + "type": "integer", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Recursive merge fragment. Null deletes object fields; arrays are replaced. APISIX validates the resulting resource, including plugin and reference checks." + }, + "UpstreamPatch": { + "type": "object", + "properties": { + "tls": { + "anyOf": [ + { + "type": "object", + "properties": { + "client_key": { + "anyOf": [ + { + "minLength": 64, + "maxLength": 65536, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "client_cert": { + "anyOf": [ + { + "minLength": 128, + "maxLength": 65536, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "client_cert_id": { + "anyOf": [ + { + "anyOf": [ + { + "minLength": 1, + "pattern": "^[a-zA-Z0-9-_.]+$", + "maxLength": 64, + "type": "string" + }, + { + "type": "null" + } + ] + }, + { + "anyOf": [ + { + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + { + "type": "null" + } + ] + }, + "verify": { + "anyOf": [ + { + "description": "Turn on server certificate verification, currently only kafka upstream is supported", + "type": "boolean" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "retry_timeout": { + "anyOf": [ + { + "minimum": 0, + "type": "number" + }, + { + "type": "null" + } + ] + }, + "scheme": { + "anyOf": [ + { + "description": "The scheme of the upstream. For L7 proxy, it can be one of grpc/grpcs/http/https. For L4 proxy, it can be one of tcp/tls/udp. For specific protocols, it can be kafka.", + "enum": [ + "grpc", + "grpcs", + "http", + "https", + "tcp", + "tls", + "udp", + "kafka" + ] + }, + { + "type": "null" + } + ] + }, + "retries": { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "hash_on": { + "anyOf": [ + { + "enum": [ + "vars", + "header", + "cookie", + "consumer", + "vars_combinations" + ], + "type": "string" + }, + { + "type": "null" + } + ] + }, + "service_name": { + "anyOf": [ + { + "minLength": 1, + "maxLength": 256, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodes": { + "anyOf": [ + { + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ] + }, + { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/UpstreamNodeItem" + }, + "type": "array" + }, + { + "type": "null" + } + ] + }, + { + "type": "null" + } + ] + }, + "discovery_type": { + "anyOf": [ + { + "type": "string", + "description": "discovery type" + }, + { + "type": "null" + } + ] + }, + "upstream_host": { + "anyOf": [ + { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + { + "type": "null" + } + ] + }, + "timeout": { + "anyOf": [ + { + "type": "object", + "properties": { + "connect": { + "anyOf": [ + { + "exclusiveMinimum": 0, + "type": "number" + }, + { + "type": "null" + } + ] + }, + "send": { + "anyOf": [ + { + "exclusiveMinimum": 0, + "type": "number" + }, + { + "type": "null" + } + ] + }, + "read": { + "anyOf": [ + { + "exclusiveMinimum": 0, + "type": "number" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "labels": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ] + }, + "keepalive_pool": { + "anyOf": [ + { + "type": "object", + "properties": { + "size": { + "anyOf": [ + { + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "requests": { + "anyOf": [ + { + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "idle_timeout": { + "anyOf": [ + { + "minimum": 0, + "type": "number" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "checks": { + "anyOf": [ + { + "type": "object", + "properties": { + "passive": { + "anyOf": [ + { + "type": "object", + "properties": { + "unhealthy": { + "anyOf": [ + { + "type": "object", + "properties": { + "http_failures": { + "anyOf": [ + { + "maximum": 254, + "minimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "http_statuses": { + "anyOf": [ + { + "items": { + "maximum": 599, + "minimum": 200, + "type": "integer" + }, + "minItems": 1, + "uniqueItems": true, + "type": "array" + }, + { + "type": "null" + } + ] + }, + "tcp_failures": { + "anyOf": [ + { + "maximum": 254, + "minimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "timeouts": { + "anyOf": [ + { + "maximum": 254, + "minimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "healthy": { + "anyOf": [ + { + "type": "object", + "properties": { + "successes": { + "anyOf": [ + { + "maximum": 254, + "minimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "http_statuses": { + "anyOf": [ + { + "items": { + "maximum": 599, + "minimum": 200, + "type": "integer" + }, + "minItems": 1, + "uniqueItems": true, + "type": "array" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "type": { + "anyOf": [ + { + "enum": [ + "http", + "https", + "tcp" + ], + "type": "string" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "active": { + "anyOf": [ + { + "type": "object", + "properties": { + "http_method": { + "anyOf": [ + { + "enum": [ + "GET", + "POST", + "PUT", + "DELETE", + "PATCH", + "HEAD", + "OPTIONS", + "CONNECT", + "TRACE", + "PURGE" + ], + "type": "string" + }, + { + "type": "null" + } + ] + }, + "port": { + "anyOf": [ + { + "maximum": 65535, + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "host": { + "anyOf": [ + { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + { + "type": "null" + } + ] + }, + "timeout": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "unhealthy": { + "anyOf": [ + { + "type": "object", + "properties": { + "interval": { + "anyOf": [ + { + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "http_statuses": { + "anyOf": [ + { + "items": { + "maximum": 599, + "minimum": 200, + "type": "integer" + }, + "minItems": 1, + "uniqueItems": true, + "type": "array" + }, + { + "type": "null" + } + ] + }, + "timeouts": { + "anyOf": [ + { + "maximum": 254, + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "tcp_failures": { + "anyOf": [ + { + "maximum": 254, + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "http_failures": { + "anyOf": [ + { + "maximum": 254, + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "healthy": { + "anyOf": [ + { + "type": "object", + "properties": { + "interval": { + "anyOf": [ + { + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "http_statuses": { + "anyOf": [ + { + "items": { + "maximum": 599, + "minimum": 200, + "type": "integer" + }, + "minItems": 1, + "uniqueItems": true, + "type": "array" + }, + { + "type": "null" + } + ] + }, + "successes": { + "anyOf": [ + { + "maximum": 254, + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "https_verify_certificate": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "req_headers": { + "anyOf": [ + { + "items": { + "uniqueItems": true, + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + { + "type": "null" + } + ] + }, + "http_path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "http_req_body": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "concurrency": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "type": { + "anyOf": [ + { + "enum": [ + "http", + "https", + "tcp" + ], + "type": "string" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "key": { + "anyOf": [ + { + "type": "string", + "description": "the key of chash for dynamic load balancing" + }, + { + "type": "null" + } + ] + }, + "discovery_args": { + "anyOf": [ + { + "type": "object", + "properties": { + "group_name": { + "anyOf": [ + { + "type": "string", + "description": "group name" + }, + { + "type": "null" + } + ] + }, + "namespace_id": { + "anyOf": [ + { + "type": "string", + "description": "namespace id" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "name": { + "anyOf": [ + { + "minLength": 1, + "maxLength": 256, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "pass_host": { + "anyOf": [ + { + "description": "mod of host passing", + "enum": [ + "pass", + "node", + "rewrite" + ], + "type": "string" + }, + { + "type": "null" + } + ] + }, + "desc": { + "anyOf": [ + { + "maxLength": 256, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "type": { + "anyOf": [ + { + "description": "algorithms of load balancing", + "type": "string" + }, + { + "type": "null" + } + ] + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "update_time": { + "type": "integer", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Recursive merge fragment. Null deletes object fields; arrays are replaced. APISIX validates the resulting resource, including plugin and reference checks." + }, + "GlobalRulePatch": { + "type": "object", + "properties": { + "plugins": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ] + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "update_time": { + "type": "integer", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Recursive merge fragment. Null deletes object fields; arrays are replaced. APISIX validates the resulting resource, including plugin and reference checks." + }, + "ConsumerGroupPatch": { + "type": "object", + "properties": { + "plugins": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ] + }, + "labels": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ] + }, + "desc": { + "anyOf": [ + { + "maxLength": 256, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "name": { + "anyOf": [ + { + "minLength": 1, + "maxLength": 256, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "update_time": { + "type": "integer", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Recursive merge fragment. Null deletes object fields; arrays are replaced. APISIX validates the resulting resource, including plugin and reference checks." + }, + "PluginConfigPatch": { + "type": "object", + "properties": { + "plugins": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ] + }, + "labels": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ] + }, + "desc": { + "anyOf": [ + { + "maxLength": 256, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "update_time": { + "type": "integer", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Recursive merge fragment. Null deletes object fields; arrays are replaced. APISIX validates the resulting resource, including plugin and reference checks." + }, + "VaultSecret": { + "type": "object", + "description": "HashiCorp Vault secret-manager configuration.", + "properties": { + "uri": { + "pattern": "^[^\\/]+:\\/\\/([\\da-zA-Z.-]+|\\[[\\da-fA-F:]+\\])(:\\d+)?", + "type": "string" + }, + "prefix": { + "type": "string", + "description": "Path prefix for secrets in Vault." + }, + "token": { + "type": "string", + "description": "Vault authentication token or environment-variable URI." + }, + "namespace": { + "type": "string", + "description": "Vault Enterprise or HCP Vault namespace." + } + }, + "required": [ + "uri", + "prefix", + "token" + ], + "x-source": "apisix/secret/vault.lua" + }, + "AwsSecret": { + "type": "object", + "description": "AWS Secrets Manager configuration.", + "properties": { + "access_key_id": { + "type": "string", + "description": "AWS access key ID or environment-variable URI." + }, + "secret_access_key": { + "type": "string", + "description": "AWS secret access key or environment-variable URI." + }, + "session_token": { + "type": "string", + "description": "Optional AWS session token or environment-variable URI." + }, + "region": { + "type": "string", + "default": "us-east-1", + "description": "AWS region." + }, + "endpoint_url": { + "pattern": "^[^\\/]+:\\/\\/([\\da-zA-Z.-]+|\\[[\\da-fA-F:]+\\])(:\\d+)?", + "type": "string" + } + }, + "required": [ + "access_key_id", + "secret_access_key" + ], + "x-source": "apisix/secret/aws.lua" + }, + "GcpSecret": { + "type": "object", + "description": "Google Cloud Secret Manager configuration.", + "properties": { + "auth_config": { + "type": "object", + "properties": { + "client_email": { + "type": "string" + }, + "private_key": { + "type": "string" + }, + "project_id": { + "type": "string" + }, + "token_uri": { + "type": "string", + "default": "https://oauth2.googleapis.com/token" + }, + "scope": { + "type": "array", + "items": { + "type": "string" + }, + "default": [ + "https://www.googleapis.com/auth/cloud-platform" + ] + }, + "entries_uri": { + "type": "string", + "default": "https://secretmanager.googleapis.com/v1" + } + }, + "required": [ + "client_email", + "private_key", + "project_id" + ] + }, + "ssl_verify": { + "type": "boolean", + "default": true + }, + "auth_file": { + "type": "string", + "description": "Path to a Google service-account JSON file." + } + }, + "oneOf": [ + { + "required": [ + "auth_config" + ] + }, + { + "required": [ + "auth_file" + ] + } + ], + "x-source": "apisix/secret/gcp.lua" + }, + "Secret": { + "description": "Secret-manager configuration. The body must match the manager selected by the `secret_type` path parameter.", + "anyOf": [ + { + "$ref": "#/components/schemas/VaultSecret" + }, + { + "$ref": "#/components/schemas/AwsSecret" + }, + { + "$ref": "#/components/schemas/GcpSecret" + } + ] + }, + "SSLPatch": { + "type": "object", + "properties": { + "snis": { + "anyOf": [ + { + "items": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + { + "type": "null" + } + ] + }, + "ssl_protocols": { + "anyOf": [ + { + "items": { + "enum": [ + "TLSv1.1", + "TLSv1.2", + "TLSv1.3" + ] + }, + "maxItems": 3, + "description": "set ssl protocols", + "uniqueItems": true, + "type": "array" + }, + { + "type": "null" + } + ] + }, + "key": { + "anyOf": [ + { + "minLength": 64, + "maxLength": 65536, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "labels": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ] + }, + "status": { + "anyOf": [ + { + "description": "ssl status, 1 to enable, 0 to disable", + "enum": [ + 1, + 0 + ], + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "cert": { + "anyOf": [ + { + "minLength": 128, + "maxLength": 65536, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "client": { + "anyOf": [ + { + "type": "object", + "properties": { + "ca": { + "anyOf": [ + { + "minLength": 128, + "maxLength": 65536, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "skip_mtls_uri_regex": { + "anyOf": [ + { + "uniqueItems": true, + "minItems": 1, + "items": { + "type": "string", + "description": "uri regular expression to skip mtls" + }, + "type": "array" + }, + { + "type": "null" + } + ] + }, + "depth": { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ] + } + } + }, + { + "type": "null" + } + ] + }, + "sni": { + "anyOf": [ + { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + { + "type": "null" + } + ] + }, + "certs": { + "anyOf": [ + { + "items": { + "minLength": 128, + "maxLength": 65536, + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ] + }, + "keys": { + "anyOf": [ + { + "items": { + "minLength": 64, + "maxLength": 65536, + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ] + }, + "desc": { + "anyOf": [ + { + "maxLength": 256, + "type": "string" + }, + { + "type": "null" + } + ] + }, + "type": { + "anyOf": [ + { + "enum": [ + "server", + "client" + ], + "description": "ssl certificate type, server to server certificate, client to client certificate for upstream", + "type": "string" + }, + { + "type": "null" + } + ] + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "update_time": { + "type": "integer", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Recursive merge fragment. Null deletes object fields; arrays are replaced. APISIX validates the resulting resource, including plugin and reference checks." + }, + "SecretPatch": { + "type": "object", + "description": "Manager-specific recursive merge fragment. The resulting configuration must match the selected secret manager schema." + }, + "ConfigValidationDocument": { + "type": "object", + "description": "Declarative APISIX configuration to validate. Only the documented resource arrays and their `*_conf_version` fields are evaluated.", + "properties": { + "routes": { + "type": "array", + "description": "Declarative `routes` resources to validate.", + "items": { + "$ref": "#/components/schemas/ConfigValidationRoute" + } + }, + "routes_conf_version": { + "type": "number", + "description": "Optional `routes` configuration version. When present, the value must be numeric." + }, + "services": { + "type": "array", + "description": "Declarative `services` resources to validate.", + "items": { + "$ref": "#/components/schemas/ConfigValidationService" + } + }, + "services_conf_version": { + "type": "number", + "description": "Optional `services` configuration version. When present, the value must be numeric." + }, + "upstreams": { + "type": "array", + "description": "Declarative `upstreams` resources to validate.", + "items": { + "$ref": "#/components/schemas/ConfigValidationUpstream" + } + }, + "upstreams_conf_version": { + "type": "number", + "description": "Optional `upstreams` configuration version. When present, the value must be numeric." + }, + "consumers": { + "type": "array", + "description": "Declarative `consumers` resources to validate.", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/ConfigValidationConsumer" + }, + { + "$ref": "#/components/schemas/ConfigValidationCredential" + } + ] + } + }, + "consumers_conf_version": { + "type": "number", + "description": "Optional `consumers` configuration version. When present, the value must be numeric." + }, + "ssls": { + "type": "array", + "description": "Declarative `ssls` resources to validate.", + "items": { + "$ref": "#/components/schemas/ConfigValidationSSL" + } + }, + "ssls_conf_version": { + "type": "number", + "description": "Optional `ssls` configuration version. When present, the value must be numeric." + }, + "global_rules": { + "type": "array", + "description": "Declarative `global_rules` resources to validate.", + "items": { + "$ref": "#/components/schemas/ConfigValidationGlobalRule" + } + }, + "global_rules_conf_version": { + "type": "number", + "description": "Optional `global_rules` configuration version. When present, the value must be numeric." + }, + "stream_routes": { + "type": "array", + "description": "Declarative `stream_routes` resources to validate.", + "items": { + "$ref": "#/components/schemas/ConfigValidationStreamRoute" + } + }, + "stream_routes_conf_version": { + "type": "number", + "description": "Optional `stream_routes` configuration version. When present, the value must be numeric." + }, + "protos": { + "type": "array", + "description": "Declarative `protos` resources to validate.", + "items": { + "$ref": "#/components/schemas/ConfigValidationProto" + } + }, + "protos_conf_version": { + "type": "number", + "description": "Optional `protos` configuration version. When present, the value must be numeric." + }, + "plugin_configs": { + "type": "array", + "description": "Declarative `plugin_configs` resources to validate.", + "items": { + "$ref": "#/components/schemas/ConfigValidationPluginConfig" + } + }, + "plugin_configs_conf_version": { + "type": "number", + "description": "Optional `plugin_configs` configuration version. When present, the value must be numeric." + }, + "consumer_groups": { + "type": "array", + "description": "Declarative `consumer_groups` resources to validate.", + "items": { + "$ref": "#/components/schemas/ConfigValidationConsumerGroup" + } + }, + "consumer_groups_conf_version": { + "type": "number", + "description": "Optional `consumer_groups` configuration version. When present, the value must be numeric." + }, + "secrets": { + "type": "array", + "description": "Declarative `secrets` resources to validate.", + "items": { + "$ref": "#/components/schemas/Secret" + } + }, + "secrets_conf_version": { + "type": "number", + "description": "Optional `secrets` configuration version. When present, the value must be numeric." + }, + "plugin_metadata": { + "type": "array", + "description": "Declarative `plugin_metadata` resources to validate.", + "items": { + "type": "object" + } + }, + "plugin_metadata_conf_version": { + "type": "number", + "description": "Optional `plugin_metadata` configuration version. When present, the value must be numeric." + }, + "plugins": { + "type": "array", + "description": "Declarative `plugins` resources to validate.", + "items": { + "type": "object" + } + }, + "plugins_conf_version": { + "type": "number", + "description": "Optional `plugins` configuration version. When present, the value must be numeric." + } + }, + "additionalProperties": true + }, + "ConfigValidationError": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "resource_type": { + "type": "string", + "description": "Top-level resource section that failed validation." + }, + "resource_id": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ], + "description": "Resource ID or consumer username, when one is available. An empty string indicates that the invalid item has no ID." + }, + "index": { + "type": "integer", + "minimum": 0, + "description": "Zero-based index of the invalid resource." + }, + "error": { + "type": "string", + "description": "Validation error for this resource or section." + } + } + }, + "ConfigValidationErrorResponse": { + "type": "object", + "required": [ + "error_msg", + "errors" + ], + "properties": { + "error_msg": { + "type": "string", + "const": "Configuration validation failed" + }, + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ConfigValidationError" + } + } + } + }, + "ConfigValidationCredential": { + "additionalProperties": false, + "properties": { + "update_time": { + "type": "integer", + "readOnly": true + }, + "plugins": { + "maxProperties": 1, + "type": "object" + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "id": { + "oneOf": [ + { + "$ref": "#/components/schemas/ResourceId" + }, + { + "minLength": 15, + "pattern": "^[a-zA-Z0-9-_]+/credentials/[a-zA-Z0-9-_.]+$", + "maxLength": 128, + "type": "string" + } + ] + }, + "desc": { + "maxLength": 256, + "type": "string" + }, + "name": { + "minLength": 1, + "maxLength": 256, + "type": "string" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "ConfigValidationRoute": { + "anyOf": [ + { + "required": [ + "plugins", + "uri" + ] + }, + { + "required": [ + "upstream", + "uri" + ] + }, + { + "required": [ + "upstream_id", + "uri" + ] + }, + { + "required": [ + "service_id", + "uri" + ] + }, + { + "required": [ + "plugins", + "uris" + ] + }, + { + "required": [ + "upstream", + "uris" + ] + }, + { + "required": [ + "upstream_id", + "uris" + ] + }, + { + "required": [ + "service_id", + "uris" + ] + }, + { + "required": [ + "script", + "uri" + ] + }, + { + "required": [ + "script", + "uris" + ] + } + ], + "allOf": [ + { + "oneOf": [ + { + "required": [ + "uri" + ] + }, + { + "required": [ + "uris" + ] + } + ] + }, + { + "oneOf": [ + { + "not": { + "anyOf": [ + { + "required": [ + "host" + ] + }, + { + "required": [ + "hosts" + ] + } + ] + } + }, + { + "required": [ + "host" + ] + }, + { + "required": [ + "hosts" + ] + } + ] + }, + { + "oneOf": [ + { + "not": { + "anyOf": [ + { + "required": [ + "remote_addr" + ] + }, + { + "required": [ + "remote_addrs" + ] + } + ] + } + }, + { + "required": [ + "remote_addr" + ] + }, + { + "required": [ + "remote_addrs" + ] + } + ] + } + ], + "additionalProperties": false, + "not": { + "anyOf": [ + { + "required": [ + "script", + "plugins" + ] + }, + { + "required": [ + "script", + "plugin_config_id" + ] + } + ] + }, + "properties": { + "update_time": { + "type": "integer", + "readOnly": true + }, + "upstream": { + "$ref": "#/components/schemas/Upstream" + }, + "script": { + "minLength": 10, + "maxLength": 102400, + "type": "string" + }, + "plugins": { + "type": "object" + }, + "remote_addrs": { + "items": { + "description": "client IP", + "anyOf": [ + { + "format": "ipv4", + "type": "string", + "title": "IPv4" + }, + { + "pattern": "^([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/([12]?[0-9]|3[0-2])$", + "type": "string", + "title": "IPv4/CIDR" + }, + { + "format": "ipv6", + "type": "string", + "title": "IPv6" + }, + { + "pattern": "^([a-fA-F0-9]{0,4}:){1,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?/[0-9]{1,3}$", + "type": "string", + "title": "IPv6/CIDR" + } + ], + "type": "string" + }, + "minItems": 1, + "uniqueItems": true, + "type": "array" + }, + "priority": { + "default": 0, + "type": "integer" + }, + "hosts": { + "items": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "minItems": 1, + "uniqueItems": true, + "type": "array" + }, + "plugin_config_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "methods": { + "items": { + "description": "HTTP method", + "enum": [ + "GET", + "POST", + "PUT", + "DELETE", + "PATCH", + "HEAD", + "OPTIONS", + "CONNECT", + "TRACE", + "PURGE" + ], + "type": "string" + }, + "uniqueItems": true, + "type": "array" + }, + "uri": { + "minLength": 1, + "maxLength": 4096, + "type": "string" + }, + "enable_websocket": { + "type": "boolean", + "description": "enable websocket for request" + }, + "upstream_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "service_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "host": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "timeout": { + "$ref": "#/components/schemas/Timeout" + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "script_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "remote_addr": { + "description": "client IP", + "anyOf": [ + { + "format": "ipv4", + "type": "string", + "title": "IPv4" + }, + { + "pattern": "^([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/([12]?[0-9]|3[0-2])$", + "type": "string", + "title": "IPv4/CIDR" + }, + { + "format": "ipv6", + "type": "string", + "title": "IPv6" + }, + { + "pattern": "^([a-fA-F0-9]{0,4}:){1,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?/[0-9]{1,3}$", + "type": "string", + "title": "IPv6/CIDR" + } + ], + "type": "string" + }, + "status": { + "default": 1, + "description": "route status, 1 to enable, 0 to disable", + "enum": [ + 1, + 0 + ], + "type": "integer" + }, + "filter_func": { + "pattern": "^function", + "minLength": 10, + "type": "string" + }, + "vars": { + "type": "array" + }, + "name": { + "minLength": 1, + "maxLength": 256, + "type": "string" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "desc": { + "maxLength": 256, + "type": "string" + }, + "uris": { + "items": { + "type": "string", + "description": "HTTP uri" + }, + "minItems": 1, + "uniqueItems": true, + "type": "array" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "ConfigValidationService": { + "additionalProperties": false, + "properties": { + "update_time": { + "type": "integer", + "readOnly": true + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "upstream": { + "$ref": "#/components/schemas/Upstream" + }, + "upstream_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "plugins": { + "type": "object" + }, + "name": { + "minLength": 1, + "maxLength": 256, + "type": "string" + }, + "script": { + "minLength": 10, + "maxLength": 102400, + "type": "string" + }, + "hosts": { + "items": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "minItems": 1, + "uniqueItems": true, + "type": "array" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "desc": { + "maxLength": 256, + "type": "string" + }, + "enable_websocket": { + "type": "boolean", + "description": "enable websocket for request" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "ConfigValidationUpstream": { + "additionalProperties": false, + "properties": { + "tls": { + "$ref": "#/components/schemas/UpstreamTLS" + }, + "update_time": { + "type": "integer", + "readOnly": true + }, + "retry_timeout": { + "minimum": 0, + "type": "number" + }, + "scheme": { + "default": "http", + "description": "The scheme of the upstream. For L7 proxy, it can be one of grpc/grpcs/http/https. For L4 proxy, it can be one of tcp/tls/udp. For specific protocols, it can be kafka.", + "enum": [ + "grpc", + "grpcs", + "http", + "https", + "tcp", + "tls", + "udp", + "kafka" + ] + }, + "retries": { + "minimum": 0, + "type": "integer" + }, + "hash_on": { + "default": "vars", + "enum": [ + "vars", + "header", + "cookie", + "consumer", + "vars_combinations" + ], + "type": "string" + }, + "service_name": { + "minLength": 1, + "maxLength": 256, + "type": "string" + }, + "nodes": { + "$ref": "#/components/schemas/UpstreamNodes" + }, + "discovery_type": { + "type": "string", + "description": "discovery type" + }, + "upstream_host": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "timeout": { + "$ref": "#/components/schemas/Timeout" + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "keepalive_pool": { + "$ref": "#/components/schemas/KeepalivePool" + }, + "checks": { + "$ref": "#/components/schemas/HealthCheck" + }, + "key": { + "type": "string", + "description": "the key of chash for dynamic load balancing" + }, + "discovery_args": { + "properties": { + "group_name": { + "type": "string", + "description": "group name" + }, + "namespace_id": { + "type": "string", + "description": "namespace id" + } + }, + "type": "object" + }, + "name": { + "minLength": 1, + "maxLength": 256, + "type": "string" + }, + "pass_host": { + "default": "pass", + "description": "mod of host passing", + "enum": [ + "pass", + "node", + "rewrite" + ], + "type": "string" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "desc": { + "maxLength": 256, + "type": "string" + }, + "type": { + "description": "algorithms of load balancing", + "default": "roundrobin", + "type": "string" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "oneOf": [ + { + "required": [ + "nodes" + ] + }, + { + "required": [ + "service_name", + "discovery_type" + ] + } + ], + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "ConfigValidationConsumer": { + "required": [ + "username" + ], + "additionalProperties": false, + "properties": { + "plugins": { + "type": "object" + }, + "update_time": { + "type": "integer", + "readOnly": true + }, + "group_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "desc": { + "maxLength": 256, + "type": "string" + }, + "username": { + "minLength": 1, + "pattern": "^[a-zA-Z0-9_\\-]+$", + "maxLength": 256, + "type": "string" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "ConfigValidationSSL": { + "additionalProperties": false, + "else": { + "required": [ + "key", + "cert" + ] + }, + "if": { + "properties": { + "type": { + "enum": [ + "server" + ] + } + } + }, + "then": { + "oneOf": [ + { + "required": [ + "sni", + "key", + "cert" + ] + }, + { + "required": [ + "snis", + "key", + "cert" + ] + } + ] + }, + "properties": { + "snis": { + "items": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "update_time": { + "type": "integer", + "readOnly": true + }, + "ssl_protocols": { + "items": { + "enum": [ + "TLSv1.1", + "TLSv1.2", + "TLSv1.3" + ] + }, + "maxItems": 3, + "description": "set ssl protocols", + "uniqueItems": true, + "type": "array" + }, + "key": { + "minLength": 64, + "maxLength": 65536, + "type": "string" + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "status": { + "default": 1, + "description": "ssl status, 1 to enable, 0 to disable", + "enum": [ + 1, + 0 + ], + "type": "integer" + }, + "cert": { + "minLength": 128, + "maxLength": 65536, + "type": "string" + }, + "client": { + "required": [ + "ca" + ], + "properties": { + "ca": { + "minLength": 128, + "maxLength": 65536, + "type": "string" + }, + "skip_mtls_uri_regex": { + "uniqueItems": true, + "minItems": 1, + "items": { + "type": "string", + "description": "uri regular expression to skip mtls" + }, + "type": "array" + }, + "depth": { + "default": 1, + "minimum": 0, + "type": "integer" + } + }, + "type": "object" + }, + "sni": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "certs": { + "items": { + "minLength": 128, + "maxLength": 65536, + "type": "string" + }, + "type": "array" + }, + "keys": { + "items": { + "minLength": 64, + "maxLength": 65536, + "type": "string" + }, + "type": "array" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "desc": { + "maxLength": 256, + "type": "string" + }, + "type": { + "enum": [ + "server", + "client" + ], + "description": "ssl certificate type, server to server certificate, client to client certificate for upstream", + "default": "server", + "type": "string" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "ConfigValidationStreamRoute": { + "additionalProperties": false, + "properties": { + "update_time": { + "type": "integer", + "readOnly": true + }, + "protocol": { + "required": [ + "name" + ], + "properties": { + "superior_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "name": { + "type": "string" + }, + "logger": { + "items": { + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "conf": { + "type": "object", + "description": "logger plugin configuration" + }, + "filter": { + "type": "array", + "description": "logger filter rules" + } + }, + "dependentRequired": { + "name": [ + "conf" + ] + } + }, + "type": "array" + }, + "conf": { + "type": "object", + "description": "protocol-specific configuration" + } + }, + "type": "object" + }, + "upstream_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "service_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "upstream": { + "$ref": "#/components/schemas/Upstream" + }, + "remote_addr": { + "description": "client IP", + "anyOf": [ + { + "format": "ipv4", + "type": "string", + "title": "IPv4" + }, + { + "pattern": "^([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/([12]?[0-9]|3[0-2])$", + "type": "string", + "title": "IPv4/CIDR" + }, + { + "format": "ipv6", + "type": "string", + "title": "IPv6" + }, + { + "pattern": "^([a-fA-F0-9]{0,4}:){1,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?/[0-9]{1,3}$", + "type": "string", + "title": "IPv6/CIDR" + } + ], + "type": "string" + }, + "plugins": { + "type": "object" + }, + "sni": { + "description": "server name indication", + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "server_port": { + "maximum": 65535, + "description": "server port", + "minimum": 1, + "type": "integer" + }, + "server_addr": { + "description": "server IP", + "anyOf": [ + { + "format": "ipv4", + "type": "string", + "title": "IPv4" + }, + { + "pattern": "^([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/([12]?[0-9]|3[0-2])$", + "type": "string", + "title": "IPv4/CIDR" + }, + { + "format": "ipv6", + "type": "string", + "title": "IPv6" + }, + { + "pattern": "^([a-fA-F0-9]{0,4}:){1,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?/[0-9]{1,3}$", + "type": "string", + "title": "IPv6/CIDR" + } + ], + "type": "string" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "desc": { + "maxLength": 256, + "type": "string" + }, + "name": { + "minLength": 1, + "maxLength": 256, + "type": "string" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "ConfigValidationPluginConfig": { + "required": [ + "id", + "plugins" + ], + "additionalProperties": false, + "properties": { + "update_time": { + "type": "integer", + "readOnly": true + }, + "plugins": { + "type": "object" + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "desc": { + "maxLength": 256, + "type": "string" + }, + "name": { + "type": "string" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "ConfigValidationConsumerGroup": { + "required": [ + "id", + "plugins" + ], + "additionalProperties": false, + "properties": { + "update_time": { + "type": "integer", + "readOnly": true + }, + "plugins": { + "type": "object" + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "desc": { + "maxLength": 256, + "type": "string" + }, + "name": { + "minLength": 1, + "maxLength": 256, + "type": "string" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "ConfigValidationGlobalRule": { + "required": [ + "id", + "plugins" + ], + "additionalProperties": false, + "properties": { + "plugins": { + "type": "object" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "update_time": { + "type": "integer", + "readOnly": true + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "modifiedIndex": { + "type": "integer" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "ConfigValidationProto": { + "required": [ + "content" + ], + "additionalProperties": false, + "properties": { + "update_time": { + "type": "integer", + "readOnly": true + }, + "content": { + "minLength": 1, + "maxLength": 1048576, + "type": "string" + }, + "create_time": { + "type": "integer", + "readOnly": true + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "desc": { + "maxLength": 256, + "type": "string" + }, + "name": { + "minLength": 1, + "maxLength": 256, + "type": "string" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "type": "object", + "x-source": "apisix/schema_def.lua" + }, + "SSLWriteEnvelope": { + "type": "object", + "description": "SSL write response, which may contain encrypted private-key fields.", + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/SSL" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "required": [ + "key", + "value" + ] + } + }, + "responses": { + "BadRequest": { + "description": "**Bad Request** — The request body is invalid or missing required fields.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "examples": { + "validation_error": { + "summary": "Schema validation error", + "value": { + "error_msg": "invalid configuration: property \"uri\" is required" + } + }, + "invalid_json": { + "summary": "Malformed JSON body", + "value": { + "error_msg": "invalid request body: unexpected character at line 1 column 2" + } + } + } + } + } + }, + "Unauthorized": { + "description": "**Unauthorized** — The API key is missing, invalid, or lacks permission.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthErrorResponse" + }, + "examples": { + "missing_key": { + "summary": "No API key provided", + "value": { + "error_msg": "failed to check token", + "description": "missing apikey" + } + }, + "wrong_key": { + "summary": "Invalid API key", + "value": { + "error_msg": "failed to check token", + "description": "wrong apikey" + } + }, + "viewer_role": { + "summary": "Viewer role attempting write operation", + "value": { + "error_msg": "failed to check token", + "description": "invalid method for role viewer" + } + } + } + } + } + }, + "NotFound": { + "description": "**Not Found** — The specified resource does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundResponse" + }, + "example": { + "error_msg": "Key not found" + } + } + } + } + }, + "parameters": { + "Ttl": { + "name": "ttl", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 1 + }, + "description": "Time-to-live in seconds. The resource is automatically removed when it expires. The dispatcher requires a numeric value; etcd lease handling imposes further constraints.", + "example": 3600 + }, + "Force": { + "name": "force", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": [ + "true" + ] + }, + "description": "When `true`, skip dependency checks before deletion.", + "example": "true" + }, + "Page": { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 1 + }, + "description": "Page number. Pagination is applied only when both page and page_size are numeric. Values below 1 are treated as 1.", + "example": 1 + }, + "PageSize": { + "name": "page_size", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 10, + "maximum": 500 + }, + "description": "Items per page, 10–500. Pagination is applied only when both page and page_size are numeric; otherwise the full filtered list is returned.", + "example": 20 + }, + "IdInPath": { + "name": "id", + "in": "path", + "required": true, + "schema": { + "$ref": "#/components/schemas/ResourceId" + }, + "description": "Resource identifier.", + "example": "r1" + }, + "SecretType": { + "name": "secret_type", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": [ + "vault", + "aws", + "gcp" + ] + }, + "description": "Secret manager backend.", + "example": "vault" + }, + "NameFilter": { + "name": "name", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "Filter list by `name` (regex match)." + }, + "LabelFilter": { + "name": "label", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "Filter list to resources with the given label key.", + "example": "env" + }, + "UriFilter": { + "name": "uri", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "Filter routes by URI (regex match against `uri` or any `uris[]`)." + }, + "FilterExpr": { + "name": "filter", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "Filter expression. Supports `service_id` and `upstream_id`. URL-encoded query string, e.g. `service_id%3D1`." + }, + "UsernameInPath": { + "name": "username", + "in": "path", + "required": true, + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "pattern": "^[a-zA-Z0-9_\\-]+$" + }, + "description": "Consumer username.", + "example": "jack" + }, + "PluginNameInPath": { + "name": "plugin_name", + "in": "path", + "required": true, + "schema": { + "type": "string", + "minLength": 1 + }, + "description": "Plugin name.", + "example": "limit-count" + }, + "Subsystem": { + "name": "subsystem", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": [ + "http", + "stream" + ], + "default": "http" + }, + "description": "Plugin subsystem." + }, + "PluginListAll": { + "name": "all", + "in": "query", + "required": true, + "schema": { + "type": "string", + "enum": [ + "true" + ] + }, + "description": "Deprecated. When `true`, return the full plugin catalog.", + "deprecated": true + }, + "ResourceKindInPath": { + "name": "resource", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": [ + "routes", + "services", + "upstreams", + "consumers", + "ssls", + "plugin_configs", + "global_rules", + "stream_routes", + "protos", + "consumer_groups", + "credentials" + ] + }, + "description": "Resource kind.", + "example": "routes" + }, + "SchemaNameInPath": { + "name": "resource", + "in": "path", + "required": true, + "description": "Singular schema name exported by apisix.core.schema (unlike the plural resource name used for POST /schema/validate/{resource}).", + "schema": { + "type": "string" + }, + "example": "route" + } + }, + "requestBodies": { + "CredentialUpsert": { + "description": "`Credential` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Credential" + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + }, + "example": { + "plugins": { + "key-auth": { + "key": "credential-secret-key-456" + } + } + } + } + } + }, + "PluginMetadataUpsert": { + "description": "Plugin metadata. The body schema is plugin-specific (the plugin's `metadata_schema`). Fetch via `GET /apisix/admin/schema/plugins/{plugin_name}`.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + } + } + } + }, + "RouteUpsertPostCollection": { + "description": "`Route` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Route" + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + }, + { + "required": [ + "id" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + }, + "example": { + "name": "my-api-route", + "desc": "Route for user management API", + "uri": "/api/v1/users/*", + "hosts": [ + "api.example.com" + ], + "methods": [ + "GET", + "POST", + "PUT", + "DELETE" + ], + "plugins": { + "limit-count": { + "count": 100, + "time_window": 60, + "rejected_code": 429, + "key_type": "var", + "key": "remote_addr" + } + }, + "upstream": { + "type": "roundrobin", + "nodes": { + "10.0.0.1:8080": 3, + "10.0.0.2:8080": 2 + }, + "retries": 2, + "timeout": { + "connect": 6, + "send": 6, + "read": 6 + } + } + } + } + } + }, + "RouteUpsertPutId": { + "description": "`Route` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Route" + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + }, + "example": { + "name": "payments-route", + "desc": "Route for payment processing API", + "uri": "/api/v1/payments", + "methods": [ + "POST" + ], + "plugins": { + "key-auth": {}, + "limit-count": { + "count": 10, + "time_window": 60, + "rejected_code": 429, + "key_type": "var", + "key": "consumer_name" + } + }, + "upstream": { + "type": "roundrobin", + "nodes": { + "payments-service:8080": 1 + } + }, + "timeout": { + "connect": 10, + "send": 10, + "read": 30 + } + } + } + } + }, + "RouteUpsertPatchId": { + "description": "Fields to merge into the existing `Route`. Object fields are merged recursively; arrays are replaced.", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoutePatch" + }, + "examples": { + "disableRoute": { + "summary": "Disable the route", + "value": { + "status": 0 + } + }, + "addUpstreamNode": { + "summary": "Add an upstream node", + "value": { + "upstream": { + "nodes": { + "127.0.0.1:1981": 1 + } + } + } + }, + "deleteUpstreamNode": { + "summary": "Delete an upstream node", + "value": { + "upstream": { + "nodes": { + "127.0.0.1:1980": null + } + } + } + }, + "replaceMethods": { + "summary": "Replace the allowed methods", + "value": { + "methods": [ + "GET", + "POST" + ] + } + } + } + } + } + }, + "ServiceUpsertPostCollection": { + "description": "`Service` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Service" + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + }, + { + "required": [ + "id" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + }, + "example": { + "plugins": {}, + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:1980": 1 + } + }, + "name": "service-test", + "desc": "hello world", + "enable_websocket": true, + "hosts": [ + "foo.com" + ] + } + } + } + }, + "ServiceUpsertPutId": { + "description": "`Service` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Service" + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + }, + "example": { + "name": "user-service", + "desc": "User management microservice", + "upstream": { + "type": "roundrobin", + "nodes": { + "user-svc:8080": 1 + } + }, + "plugins": { + "limit-count": { + "count": 200, + "time_window": 60, + "rejected_code": 429, + "key_type": "var", + "key": "remote_addr" + } + } + } + } + } + }, + "ServiceUpsertPatchId": { + "description": "Fields to merge into the existing `Service`. Object fields are merged recursively; arrays are replaced.", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServicePatch" + }, + "examples": { + "updateDescription": { + "summary": "Update the service description", + "value": { + "desc": "Updated service description" + } + }, + "addUpstreamNode": { + "summary": "Add an inline upstream node", + "value": { + "upstream": { + "nodes": { + "127.0.0.1:1981": 1 + } + } + } + } + } + } + } + }, + "UpstreamUpsertPostCollection": { + "description": "`Upstream` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Upstream" + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + }, + { + "required": [ + "id" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + }, + "example": { + "name": "upstream-for-test", + "desc": "desc", + "scheme": "http", + "retries": 1, + "type": "roundrobin", + "nodes": { + "127.0.0.1:1980": 1 + }, + "timeout": { + "connect": 15, + "send": 15, + "read": 15 + } + } + } + } + }, + "UpstreamUpsertPutId": { + "description": "`Upstream` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Upstream" + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + }, + "example": { + "name": "backend-cluster", + "desc": "Primary backend service cluster", + "type": "roundrobin", + "nodes": { + "10.0.0.1:8080": 3, + "10.0.0.2:8080": 2, + "10.0.0.3:8080": 1 + }, + "retries": 3, + "retry_timeout": 5, + "timeout": { + "connect": 6, + "send": 6, + "read": 6 + }, + "checks": { + "active": { + "type": "http", + "http_path": "/health", + "healthy": { + "interval": 5, + "successes": 2 + }, + "unhealthy": { + "interval": 3, + "http_failures": 3, + "tcp_failures": 2 + } + } + }, + "scheme": "http", + "pass_host": "pass" + } + } + } + }, + "UpstreamUpsertPatchId": { + "description": "Fields to merge into the existing `Upstream`. Object fields are merged recursively; arrays are replaced.", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpstreamPatch" + }, + "examples": { + "updateDescription": { + "summary": "Update the upstream description", + "value": { + "desc": "Updated upstream description" + } + }, + "addNode": { + "summary": "Add an upstream node", + "value": { + "nodes": { + "127.0.0.1:1981": 1 + } + } + } + } + } + } + }, + "ConsumerUpsertPutCollection": { + "description": "`Consumer` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Consumer" + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + }, + "example": { + "username": "my-app", + "desc": "Mobile application consumer", + "labels": { + "team": "mobile", + "env": "production" + }, + "plugins": { + "key-auth": { + "key": "app-secret-key-123" + } + } + } + } + } + }, + "ConsumerGroupUpsertPutId": { + "description": "`ConsumerGroup` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ConsumerGroup" + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + }, + "example": { + "plugins": { + "limit-count": { + "count": 200, + "time_window": 60, + "rejected_code": 429, + "key_type": "var", + "key": "consumer_name" + } + } + } + } + } + }, + "ConsumerGroupUpsertPatchId": { + "description": "Fields to merge into the existing `ConsumerGroup`. Object fields are merged recursively; arrays are replaced.", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConsumerGroupPatch" + }, + "examples": { + "updateDescription": { + "summary": "Update the consumer group description", + "value": { + "desc": "Updated consumer group description" + } + }, + "configurePlugin": { + "summary": "Configure a plugin", + "value": { + "plugins": { + "limit-count": { + "count": 100, + "time_window": 60, + "rejected_code": 429 + } + } + } + } + } + } + } + }, + "SslUpsertPostCollection": { + "description": "`SSL` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SSL" + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + }, + { + "required": [ + "id" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + }, + "example": { + "type": "server", + "sni": "test.com", + "cert": "-----BEGIN CERTIFICATE-----\nMIIDazCCAlOgAwIBAgIUJ9hQk3kXgZeZpY/m/0V6RmH1AbcwDQYJKoZIhvcNAQEL\nBQAwRTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM\nGEludGVybmFsIFJvb3QgQ0EgKGV4YW1wbGUpMB4XDTI1MDEwMTAwMDAwMFoXDTM1\nMDEwMTAwMDAwMFowRTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUx\nITAfBgNVBAoMGEludGVybmFsIFJvb3QgQ0EgKGV4YW1wbGUpMIIBIjANBgkqhkiG\n9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4eXAMPLEcaPUBLIcfakeforopeNAPIspecON\nLYexampleCAcontentsoExpandedFakeKeyMaterial==\n-----END CERTIFICATE-----\n", + "key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDh5cAWYXAMPLEbe\nfakekeyForOpenAPISpecPlaceholderOnlyDoNotUseForRealKeysNopeNopeNo\npeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNo\npeNopeNopeNopeNopeNopeNope==\n-----END PRIVATE KEY-----\n" + } + } + } + }, + "SslUpsertPutId": { + "description": "`SSL` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SSL" + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + }, + "example": { + "type": "server", + "sni": "api.example.com", + "cert": "-----BEGIN CERTIFICATE-----\nMIIDazCCAlOgAwIBAgIUJ9hQk3kXgZeZpY/m/0V6RmH1AbcwDQYJKoZIhvcNAQEL\nBQAwRTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM\nGEludGVybmFsIFJvb3QgQ0EgKGV4YW1wbGUpMB4XDTI1MDEwMTAwMDAwMFoXDTM1\nMDEwMTAwMDAwMFowRTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUx\nITAfBgNVBAoMGEludGVybmFsIFJvb3QgQ0EgKGV4YW1wbGUpMIIBIjANBgkqhkiG\n9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4eXAMPLEcaPUBLIcfakeforopeNAPIspecON\nLYexampleCAcontentsoExpandedFakeKeyMaterial==\n-----END CERTIFICATE-----\n", + "key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDh5cAWYXAMPLEbe\nfakekeyForOpenAPISpecPlaceholderOnlyDoNotUseForRealKeysNopeNopeNo\npeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNopeNo\npeNopeNopeNopeNopeNopeNope==\n-----END PRIVATE KEY-----\n" + } + } + } + }, + "GlobalRuleUpsertPutId": { + "description": "`GlobalRule` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/GlobalRule" + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + }, + "example": { + "plugins": { + "prometheus": {}, + "ip-restriction": { + "whitelist": [ + "10.0.0.0/8", + "172.16.0.0/12" + ] + } + } + } + } + } + }, + "GlobalRuleUpsertPatchId": { + "description": "Fields to merge into the existing `GlobalRule`. Object fields are merged recursively; arrays are replaced.", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GlobalRulePatch" + }, + "examples": { + "enablePlugin": { + "summary": "Enable a plugin", + "value": { + "plugins": { + "serverless-pre-function": { + "phase": "rewrite", + "functions": [ + "return function() end" + ] + } + } + } + }, + "removePlugin": { + "summary": "Remove a plugin", + "value": { + "plugins": { + "serverless-pre-function": null + } + } + } + } + } + } + }, + "PluginConfigUpsertPutId": { + "description": "`PluginConfig` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/PluginConfig" + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + }, + "example": { + "plugins": { + "limit-count": { + "count": 100, + "time_window": 60, + "rejected_code": 429, + "key_type": "var", + "key": "remote_addr" + }, + "proxy-rewrite": { + "uri": "/backend$uri" + } + } + } + } + } + }, + "PluginConfigUpsertPatchId": { + "description": "Fields to merge into the existing `PluginConfig`. Object fields are merged recursively; arrays are replaced.", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PluginConfigPatch" + }, + "examples": { + "updateDescription": { + "summary": "Update the plugin config description", + "value": { + "desc": "Updated plugin config description" + } + }, + "configurePlugin": { + "summary": "Configure a plugin", + "value": { + "plugins": { + "limit-count": { + "count": 100, + "time_window": 60, + "rejected_code": 429 + } + } + } + } + } + } + } + }, + "StreamRouteUpsertPostCollection": { + "description": "`StreamRoute` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/StreamRoute" + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + }, + { + "required": [ + "id" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + }, + "example": { + "server_addr": "127.0.0.1", + "server_port": 2000, + "upstream": { + "nodes": { + "127.0.0.1:1995": 1 + }, + "type": "roundrobin" + } + } + } + } + }, + "StreamRouteUpsertPutId": { + "description": "`StreamRoute` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/StreamRoute" + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + }, + "example": { + "server_addr": "127.0.0.1", + "server_port": 2000, + "upstream": { + "nodes": { + "127.0.0.1:1995": 1 + }, + "type": "roundrobin" + } + } + } + } + }, + "ProtoUpsertPostCollection": { + "description": "`Proto` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Proto" + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + }, + { + "required": [ + "id" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + }, + "example": { + "content": "syntax = \"proto3\";\n\npackage helloworld;\n\nservice Greeter {\n rpc SayHello (HelloRequest) returns (HelloReply) {}\n}\n\nmessage HelloRequest {\n string name = 1;\n}\n\nmessage HelloReply {\n string message = 1;\n}" + } + } + } + }, + "ProtoUpsertPutId": { + "description": "`Proto` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Proto" + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + }, + "example": { + "content": "syntax = \"proto3\";\n\npackage helloworld;\n\nservice Greeter {\n rpc SayHello (HelloRequest) returns (HelloReply) {}\n}\n\nmessage HelloRequest {\n string name = 1;\n}\n\nmessage HelloReply {\n string message = 1;\n}" + } + } + } + }, + "SecretUpsertPutCollection": { + "description": "Complete secret-manager configuration for the manager selected by `secret_type`.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Secret" + }, + { + "not": { + "anyOf": [ + { + "required": [ + "create_time" + ] + }, + { + "required": [ + "update_time" + ] + } + ] + } + } + ], + "description": "Wire request body. APISIX injects timestamps and, where appropriate, the path or generated resource ID. A supplied ID must agree with the path." + }, + "examples": { + "vault": { + "summary": "HashiCorp Vault", + "value": { + "uri": "https://vault.example.com", + "prefix": "apisix", + "token": "$ENV://VAULT_TOKEN", + "namespace": "team-a" + } + }, + "aws": { + "summary": "AWS Secrets Manager", + "value": { + "access_key_id": "$ENV://AWS_ACCESS_KEY_ID", + "secret_access_key": "$ENV://AWS_SECRET_ACCESS_KEY", + "region": "us-east-1" + } + }, + "gcp": { + "summary": "Google Cloud Secret Manager", + "value": { + "auth_file": "/etc/apisix/gcp-service-account.json", + "ssl_verify": true + } + } + } + } + } + }, + "SecretUpsertPatchCollection": { + "description": "Fields to merge into the existing secret-manager configuration.", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SecretPatch" + }, + "examples": { + "vaultToken": { + "summary": "Update a Vault token", + "value": { + "token": "$ENV://VAULT_TOKEN" + } + }, + "awsRegion": { + "summary": "Update an AWS region", + "value": { + "region": "eu-west-1" + } + }, + "gcpTls": { + "summary": "Disable GCP endpoint verification", + "value": { + "ssl_verify": false + } + } + } + } + } + }, + "SslUpsertPatchId": { + "description": "Fields to merge into the existing `SSL`. Object fields are merged recursively; arrays are replaced.", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SSLPatch" + }, + "examples": { + "updateSnis": { + "summary": "Replace the SNI hostnames", + "value": { + "snis": [ + "api.example.com", + "admin.example.com" + ] + } + }, + "disableCertificate": { + "summary": "Disable the certificate", + "value": { + "status": 0 + } + } + } + } + } + } + } + }, + "security": [ + { + "adminKey": [] + }, + { + "apiKeyQuery": [] + }, + { + "apiKeyCookie": [] + } + ], + "x-source-revision": "947d4b47d824657bba6508b1c0cb204a2ec01f82", + "x-verification": { + "source": "apache/apisix release/3.18", + "revision": "947d4b47d824657bba6508b1c0cb204a2ec01f82", + "schemas": "apisix/schema_def.lua; apisix/secret/{vault,aws,gcp}.lua", + "dispatch": "apisix/admin/init.lua", + "crud": "apisix/admin/resource.lua and resource-specific modules", + "responses": "apisix/admin/v3_adapter.lua", + "standalone": "apisix/admin/standalone.lua; apisix/admin/config_validate.lua", + "limits": "Static contract verification. Plugin Lua checks, certificate parsing, secret-reference bypasses, and live etcd/OpenResty behavior require integration testing." + } +} diff --git a/t/openapi/test_admin_api.py b/t/openapi/test_admin_api.py new file mode 100644 index 000000000000..676182408bb8 --- /dev/null +++ b/t/openapi/test_admin_api.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Regression checks for the offline Admin API contract validator.""" +from copy import deepcopy +import importlib.util +import json +from pathlib import Path +import unittest + +ROOT = Path(__file__).resolve().parents[2] +MODULE = importlib.util.spec_from_file_location('admin_contract', ROOT / 'utils/validate-admin-api.py') +validator = importlib.util.module_from_spec(MODULE) +MODULE.loader.exec_module(validator) + + +class ContractValidationTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.document = json.loads((ROOT / 'docs/en/latest/admin-api.json').read_text()) + cls.versions = validator.repository_versions(ROOT) + + def test_release_contract(self): + self.assertEqual([], validator.validate(self.document, self.versions)) + self.assertEqual([], validator.validate_source(self.document, ROOT)) + + def test_unresolved_reference(self): + document = deepcopy(self.document) + document['components']['schemas']['Route']['properties']['upstream'] = { + '$ref': '#/components/schemas/Missing'} + self.assertTrue(validator.validate(document, self.versions)) + + def test_duplicate_operation_and_missing_path_parameter(self): + document = deepcopy(self.document) + operations = document['paths']['/apisix/admin/routes/{id}'] + operations['put']['operationId'] = operations['get']['operationId'] + operations['put']['parameters'] = [] + errors = validator.validate(document, self.versions) + self.assertTrue(errors) + document = deepcopy(self.document) + document['paths']['/apisix/admin/routes/{id}']['put']['parameters'] = [] + self.assertTrue(validator.validate(document, self.versions)) + + def test_invalid_default_and_response_example(self): + document = deepcopy(self.document) + document['components']['schemas']['Route']['properties']['status']['default'] = 9 + media = document['paths']['/apisix/admin/routes']['get']['responses']['200']['content']['application/json'] + media['examples'] = {'invalid': {'value': {'total': 'one', 'list': []}}} + errors = validator.validate(document, self.versions) + self.assertTrue(errors) + document['components']['schemas']['Route']['properties']['status']['default'] = 1 + self.assertTrue(validator.validate(document, self.versions)) + + def test_version_and_release_schema_drift(self): + document = deepcopy(self.document) + document['info']['version'] = '3.19.0' + self.assertTrue(validator.validate(document, self.versions)) + document['components']['schemas']['StreamRoute']['properties']['tls_passthrough'] = { + 'type': 'boolean', 'default': False} + self.assertTrue(validator.validate_source(document, ROOT)) + + def test_unsupported_method(self): + document = deepcopy(self.document) + document['paths']['/apisix/admin/stream_routes/{id}']['patch'] = {} + self.assertTrue(validator.validate_source(document, ROOT)) + + def test_malformed_operation_id(self): + document = deepcopy(self.document) + document['paths']['/apisix/admin/stream_routes/{id}']['get']['operationId'] = 'getId]' + self.assertTrue(validator.validate(document, self.versions)) + + def test_missing_collection_put(self): + document = deepcopy(self.document) + del document['paths']['/apisix/admin/routes']['put'] + self.assertTrue(validator.validate_source(document, ROOT)) + + +if __name__ == '__main__': + unittest.main() diff --git a/utils/requirements-admin-api.txt b/utils/requirements-admin-api.txt new file mode 100644 index 000000000000..2399d4b23684 --- /dev/null +++ b/utils/requirements-admin-api.txt @@ -0,0 +1,21 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Validator versions are explicit; no contract content is downloaded. +openapi-spec-validator==0.7.2 +jsonschema==4.23.0 +referencing==0.36.2 + +lupa==2.8 diff --git a/utils/validate-admin-api.py b/utils/validate-admin-api.py new file mode 100644 index 000000000000..05448cf492fb --- /dev/null +++ b/utils/validate-admin-api.py @@ -0,0 +1,368 @@ +#!/usr/bin/env python3 +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Validate the checked-in Admin API contract; never download contract inputs.""" +import argparse +from copy import deepcopy +import json +from pathlib import Path +import re +import shlex +import sys +from urllib.parse import unquote, urlsplit + +from jsonschema import Draft202012Validator +from jsonschema.exceptions import SchemaError +from openapi_spec_validator import OpenAPIV31SpecValidator +from referencing import Registry, Resource +from referencing.jsonschema import DRAFT202012 + +ROOT = Path(__file__).resolve().parents[1] +METHODS = {'get', 'put', 'post', 'delete', 'patch', 'head', 'options', 'trace'} +BASE = 'urn:apisix:admin-api' +# These contain instance data, not contract objects or schemas. +DATA = {'example', 'default', 'enum', 'const', 'value'} +MAPS = {'properties', 'patternProperties', '$defs', 'dependentSchemas', 'schemas', + 'paths', 'responses', 'content', 'headers', 'links', 'callbacks', 'pathItems', + 'requestBodies', 'parameters', 'securitySchemes', 'examples'} + + +def objects(value, path='#', mapping=False): + """Walk contract objects, keeping arbitrary names and instance data separate.""" + if isinstance(value, dict): + if not mapping: + yield path, value + for key, child in value.items(): + if mapping or (key not in DATA and not key.startswith('x-')): + if not mapping and key == 'examples' and isinstance(child, list): + continue # JSON Schema examples are instance data. + yield from objects(child, path + '/' + key.replace('~', '~0').replace('/', '~1'), + not mapping and key in MAPS and isinstance(child, dict)) + elif isinstance(value, list): + for i, child in enumerate(value): + yield from objects(child, path + '/' + str(i)) + + +def resolve(document, ref): + if not isinstance(ref, str) or not ref.startswith('#/'): + raise ValueError(f'only internal JSON Pointer refs are allowed: {ref!r}') + result = document + for token in unquote(ref[2:]).split('/'): + token = token.replace('~1', '/').replace('~0', '~') + try: + result = result[int(token)] if isinstance(result, list) else result[token] + except (KeyError, IndexError, ValueError, TypeError) as exc: + raise ValueError(f'unresolved ref: {ref}') from exc + return result + + +def dereference(document, obj): + seen = set() + while isinstance(obj, dict) and '$ref' in obj: + ref = obj['$ref'] + if ref in seen: + raise ValueError(f'cyclic Reference Object: {ref}') + seen.add(ref) + obj = resolve(document, ref) + return obj + + +def absolute_refs(value): + result = deepcopy(value) + for _, obj in objects(result): + for key in ('$ref', '$dynamicRef'): + if key in obj: + obj[key] = BASE + obj[key] + return result + + +def validate(document, versions): + """Return actionable errors. Local ref preflight runs before the OAS library.""" + errors = [] + if document.get('openapi') != '3.1.0': + errors.append('openapi must be 3.1.0') + version = document.get('info', {}).get('version') + for source, expected in versions.items(): + if version != expected: + errors.append(f'info.version {version!r} differs from {source}: {expected!r}') + for path, obj in objects(document): + for key in ('$ref', '$dynamicRef'): + if key in obj: + try: + resolve(document, obj[key]) + except ValueError as exc: + errors.append(f'{path}: {exc}') + if '$id' in obj: + errors.append(f'{path}: embedded $id changes local ref scope; use document pointers') + if errors: + return errors + + schemas = list(document.get('components', {}).get('schemas', {}).items()) + schemas += [(path + '/schema', obj['schema']) for path, obj in objects(document) + if 'schema' in obj] + for path, schema in schemas: + try: + Draft202012Validator.check_schema(schema) + except SchemaError as exc: + errors.append(f'{path}: invalid JSON Schema: {exc.message}') + if errors: + return errors + errors.extend('OpenAPI: ' + str(e) for e in OpenAPIV31SpecValidator(document).iter_errors()) + if errors: + return errors + registry = Registry().with_resource( + BASE, Resource.from_contents(absolute_refs(document), default_specification=DRAFT202012)) + + def check_value(schema, value, location): + validator = Draft202012Validator(absolute_refs(schema), registry=registry) + errors.extend(f'{location}: {e.message}' for e in validator.iter_errors(value)) + + def check_schema(schema, location): + for path, obj in objects(schema, location): + if 'default' in obj: + check_value(obj, obj['default'], path + '/default') + if 'examples' in obj: + for i, example in enumerate(obj['examples']): + check_value(obj, example, path + f'/examples/{i}') + if 'example' in obj: + check_value(obj, obj['example'], path + '/example') + + # Components plus schemas on parameters, request bodies, response media and headers. + for name, schema in document.get('components', {}).get('schemas', {}).items(): + check_schema(schema, '#/components/schemas/' + name) + for path, obj in objects(document): + if 'schema' not in obj: + continue + schema = obj['schema'] + check_schema(schema, path + '/schema') + if 'example' in obj: + check_value(schema, obj['example'], path + '/example') + for name, example in obj.get('examples', {}).items(): + example = dereference(document, example) + if 'externalValue' in example: + errors.append(f'{path}/examples/{name}: use a checked-in value, not externalValue') + elif 'value' in example: + check_value(schema, example['value'], path + '/examples/' + name) + + ids = set() + for path, raw_item in document.get('paths', {}).items(): + item = dereference(document, raw_item) + for method in METHODS & item.keys(): + op = item[method] + location = f'{method.upper()} {path}' + if not op.get('responses'): + errors.append(f'{location}: responses must be non-empty') + op_id = op.get('operationId') + if not isinstance(op_id, str) or not re.fullmatch(r'[A-Za-z][A-Za-z0-9]*', op_id) or op_id in ids: + errors.append(f'{location}: missing or duplicate operationId {op_id!r}') + ids.add(op_id) + params = {} + for group in (item.get('parameters', []), op.get('parameters', [])): + for param in group: + param = dereference(document, param) + params[(param['in'], param['name'])] = param + declared = {name for (where, name) in params if where == 'path'} + if declared != set(re.findall(r'{([^{}]+)}', path)): + errors.append(f'{location}: path parameters do not match template') + for (where, name), param in params.items(): + if where == 'path' and param.get('required') is not True: + errors.append(f'{location}: path parameter {name} must be required') + for field in ('requestBody', 'responses'): + entries = op.get('responses', {}).values() if field == 'responses' else [op.get(field)] + for entry in entries: + if entry is None: + continue + entry = dereference(document, entry) + for media, content in entry.get('content', {}).items(): + if 'schema' not in content: + errors.append(f'{location} {field} {media}: missing schema') + + for sample in op.get('x-codeSamples', []): + if sample.get('lang', '').lower() != 'curl': + continue + try: + args = shlex.split(sample['source']) + url = next(x for x in args if x.startswith(('http://', 'https://'))) + expected_path = re.sub(r'{[^}]+}', r'[^/]+', path) + if not re.fullmatch(expected_path, urlsplit(url).path): + errors.append(f'{location}: cURL URL does not match operation') + actual_method = args[args.index('-X') + 1].lower() if '-X' in args else 'get' + if actual_method != method: + errors.append(f'{location}: cURL method does not match operation') + for flag in ('-d', '--data', '--data-raw'): + if flag in args: + body = json.loads(args[args.index(flag) + 1]) + schema = dereference(document, op['requestBody'])['content']['application/json']['schema'] + check_value(schema, body, location + '/curl') + except (ValueError, KeyError, StopIteration) as exc: + errors.append(f'{location}: invalid cURL sample: {exc}') + return errors + + +def repository_versions(root): + versions = {f'docs/{lang}/latest/config.json': json.loads( + (root / f'docs/{lang}/latest/config.json').read_text())['version'] for lang in ('en', 'zh')} + text = (root / 'apisix/core/version.lua').read_text() + versions['apisix/core/version.lua'] = re.search(r'VERSION\s*=\s*"([^"]+)"', text).group(1) + return versions + + +def validate_source(document, root): + """Compare structural constraints with this checkout's pure Lua schema table. + + Lupa evaluates schema_def.lua only: no APISIX module or network is loaded. + This is not an OpenResty integration test or a replacement for Lua checkers. + """ + from lupa import LuaRuntime, lua_type + + def from_lua(value): + if lua_type(value) != 'table': + return value + keys = list(value.keys()) + if keys and all(isinstance(key, int) for key in keys): + return [from_lua(value[i]) for i in range(1, len(keys) + 1)] + result = {key: from_lua(value[key]) for key in keys} + if 'dependencies' in result: + dependencies = result.pop('dependencies') + for keyword, typ in [('dependentSchemas', dict), ('dependentRequired', list)]: + entries = {k: v for k, v in dependencies.items() if isinstance(v, typ)} + if entries: + result[keyword] = entries + return result + + lua = LuaRuntime() + lua_schema = lua.execute((root / 'apisix/schema_def.lua').read_text()) + source = from_lua(lua_schema) + + def constraints(value): + if isinstance(value, list): + return [constraints(item) for item in value] + if not isinstance(value, dict): + return value + if '$ref' in value: + return constraints(resolve(document, value['$ref'])) + return {k: constraints(v) for k, v in value.items() + if k not in {'description', 'title', 'readOnly', 'examples', 'example'} + and not k.startswith('x-')} + + names = {'ResourceId': 'id_schema', 'Route': 'route', 'Service': 'service', + 'Upstream': 'upstream', 'Consumer': 'consumer', 'Credential': 'credential', + 'ConsumerGroup': 'consumer_group', 'GlobalRule': 'global_rule', + 'PluginConfig': 'plugin_config', 'SSL': 'ssl', 'StreamRoute': 'stream_route', + 'Proto': 'proto'} + errors = [] + schemas = document['components']['schemas'] + for name, key in names.items(): + expected = constraints(source[key]) + # resource.check_conf injects the path id before running these schemas. + if name in {'ConsumerGroup', 'GlobalRule', 'PluginConfig'}: + expected['required'].remove('id') + if constraints(schemas[name]) != expected: + errors.append(f'{name}: structural constraints differ from schema_def.lua ({key})') + + lua.globals().core = lua.table_from({'schema': lua_schema}) + for name, backend in [('VaultSecret', 'vault'), ('AwsSecret', 'aws'), ('GcpSecret', 'gcp')]: + text = (root / f'apisix/secret/{backend}.lua').read_text() + declaration = 'local schema =' + text.split('local schema =', 1)[1].split('local _M', 1)[0] + expected = from_lua(lua.execute(declaration + '\nreturn schema')) + if constraints(schemas[name]) != constraints(expected): + errors.append(f'{name}: structural constraints differ from secret/{backend}.lua') + + # Check supported canonical CRUD methods against resource.new declarations. + modules = {'routes': 'routes', 'services': 'services', 'upstreams': 'upstreams', + 'consumers': 'consumers', 'ssls': 'ssl', 'global_rules': 'global_rules', + 'consumer_groups': 'consumer_group', 'plugin_configs': 'plugin_config', + 'plugin_metadata': 'plugin_metadata', 'stream_routes': 'stream_routes', + 'protos': 'proto', 'secrets': 'secrets', 'credentials': 'credentials'} + for resource, module in modules.items(): + text = (root / f'apisix/admin/{module}.lua').read_text() + match = re.search(r'unsupported_methods\s*=\s*{([^}]+)}', text) + unsupported = set(re.findall(r'"([a-z]+)"', match.group(1))) if match else set() + actual = set() + for path, item in document['paths'].items(): + if resource == 'credentials': + relevant = '/consumers/{username}/credentials' in path + else: + relevant = path.startswith('/apisix/admin/' + resource) and ( + path == '/apisix/admin/' + resource or + path.startswith('/apisix/admin/' + resource + '/')) + if resource == 'consumers' and '/credentials' in path: + relevant = False + if relevant: + actual.update(METHODS & item.keys()) + expected = {'get', 'put', 'post', 'patch', 'delete'} - unsupported + if actual != expected: + errors.append(f'{resource}: documented methods {sorted(actual)} != {sorted(expected)}') + + # Resource.put accepts a body ID when the canonical item URL is omitted. + expected_ops = set() + for resource, module in modules.items(): + if resource == 'credentials': + base = '/apisix/admin/consumers/{username}/credentials' + else: + base = '/apisix/admin/' + resource + text = (root / f'apisix/admin/{module}.lua').read_text() + match = re.search(r'unsupported_methods\s*=\s*{([^}]+)}', text) + unsupported = set(re.findall(r'"(\w+)"', match.group(1))) if match else set() + suffix = '/{username}' if resource == 'consumers' else '/{plugin_name}' if resource == 'plugin_metadata' else '/{secret_type}/{id}' if resource == 'secrets' else '/{id}' + item = base + suffix + expected_ops.update([(base, 'get'), (item, 'get'), (item, 'put'), (item, 'delete')]) + if resource not in ('secrets', 'plugin_metadata'): + expected_ops.add((base, 'put')) + if 'post' not in unsupported: + expected_ops.add((base, 'post')) + if 'patch' not in unsupported: + expected_ops.update([(item, 'patch'), (item + '/{sub_path}', 'patch')]) + expected_ops.add(('/apisix/admin/secrets/{secret_type}', 'get')) + expected_ops.update({ + ('/apisix/admin/plugins/{plugin_name}', 'get'), + ('/apisix/admin/schema/plugins/{plugin_name}', 'get'), + ('/apisix/admin/plugins', 'get'), ('/apisix/admin/plugins/list', 'get'), + ('/apisix/admin/plugins/reload', 'put'), + ('/apisix/admin/schema/{resource}', 'get'), + ('/apisix/admin/schema/validate/{resource}', 'post'), + ('/apisix/admin', 'head'), ('/apisix/admin/configs', 'get'), + ('/apisix/admin/configs', 'put'), ('/apisix/admin/configs', 'head'), + ('/apisix/admin/configs/validate', 'post'), + }) + actual_ops = {(path, method) for path, item in document['paths'].items() + for method in METHODS & item.keys()} + if actual_ops != expected_ops: + errors.append(f'operation surface differs: missing {sorted(expected_ops - actual_ops)}, extra {sorted(actual_ops - expected_ops)}') + return errors + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('spec', nargs='?', type=Path, default=ROOT / 'docs/en/latest/admin-api.json') + args = parser.parse_args() + spec_text = args.spec.read_text() + if re.search(r'api7|apiseven', spec_text, re.IGNORECASE): + print('Admin API specification must not contain API7-specific references', file=sys.stderr) + return 1 + document = json.loads(spec_text) + errors = validate(document, repository_versions(ROOT)) + if not errors: + errors.extend(validate_source(document, ROOT)) + if errors: + print('\n'.join(errors), file=sys.stderr) + return 1 + print('Admin API OpenAPI 3.1 contract is valid (refs, operations, schemas, examples, defaults, versions, release source constraints and methods).') + return 0 + + +if __name__ == '__main__': + sys.exit(main())