diff --git a/docs/en/latest/admin-api.json b/docs/en/latest/admin-api.json new file mode 100644 index 000000000000..37c2b2e59b8b --- /dev/null +++ b/docs/en/latest/admin-api.json @@ -0,0 +1,19386 @@ +{ + "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.17/getting-started/README/).\n\nThis specification targets release/3.17 (3.17.0). Resource examples use the default Admin API v3 response format (`deployment.admin.admin_api_version: v3`). The legacy format wraps single resources in `node` and lists in `node.nodes`; the schemas describe both formats. Etcd-backed resource endpoints and schema/plugin endpoints are unavailable with the YAML config provider, which exposes only HEAD /apisix/admin and /configs endpoints. JSON Schema validation does not replace runtime plugin, reference, certificate, or expression validation. The viewer role permits GET only; protected HEAD requests require an admin role. Pagination and list filters are v3-only.", + "version": "3.17.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.17/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.17/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.17/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.17/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.17/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 or for different authentication plugins.\n\nTo learn how to configure consumer credentials, see [Credentials](https://apisix.apache.org/docs/apisix/3.17/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.17/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.17/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.17/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.17/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.17/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.17/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.17/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.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/RouteListResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "404": { + "$ref": "#/components/responses/NotFound" + } + }, + "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 `uri` or `uris`, and one of: `upstream` (inline), `upstream_id`, `service_id`, `plugins`, or `script`.", + "tags": [ + "Routes" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/RouteUpsertPostCollection" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RouteResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "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 body. If both URL and body IDs are supplied they must match.", + "tags": [ + "Routes" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "description": "`Route` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/RouteInput" + }, + { + "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/RouteResponse" + }, + "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" + }, + "200": { + "description": "Existing resource replaced.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RouteResponse" + }, + "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" + } + } + } + } + } + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "operationId": "createRouteWithBodyId", + "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 }'" + } + ] + } + }, + "/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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/RouteUpsertPutId" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RouteResponse" + }, + "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" + }, + "200": { + "description": "Existing resource replaced.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RouteResponse" + }, + "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" + } + } + } + } + } + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RouteResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + } + }, + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/RouteUpsertPatchId" + }, + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RouteResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + }, + { + "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": { + "$ref": "#/components/schemas/DeleteResponse" + }, + "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" + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + } + }, + "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/ServiceListResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "404": { + "$ref": "#/components/responses/NotFound" + } + }, + "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/ServiceResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "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 body. If both URL and body IDs are supplied they must match.", + "tags": [ + "Services" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "description": "`Service` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ServiceInput" + }, + { + "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/ServiceResponse" + }, + "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" + }, + "200": { + "description": "Existing resource replaced.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServiceResponse" + }, + "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" + } + } + } + } + } + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "operationId": "createServiceWithBodyId" + } + }, + "/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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServiceResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + } + }, + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/ServiceUpsertPutId" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServiceResponse" + }, + "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" + }, + "200": { + "description": "Existing resource replaced.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServiceResponse" + }, + "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" + } + } + } + } + } + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/ServiceUpsertPatchId" + }, + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServiceResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + }, + { + "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": { + "$ref": "#/components/schemas/DeleteResponse" + }, + "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" + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + } + }, + "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": { + "$ref": "#/components/schemas/ConsumerResponse" + }, + "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" + }, + "200": { + "description": "Existing resource replaced.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConsumerResponse" + }, + "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 + } + } + } + } + } + } + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "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/ConsumerListResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "404": { + "$ref": "#/components/responses/NotFound" + } + }, + "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/ConsumerResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + } + }, + "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": { + "$ref": "#/components/schemas/DeleteResponse" + }, + "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" + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + } + }, + "operationId": "deleteConsumer" + }, + "put": { + "summary": "Create or Update a Consumer", + "description": "Create or replace a consumer. The body username must equal the path username.", + "tags": [ + "Consumers" + ], + "parameters": [ + { + "$ref": "#/components/parameters/UsernameInPath" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/ConsumerUpsertPutCollection" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConsumerResponse" + }, + "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" + }, + "200": { + "description": "Existing resource replaced.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConsumerResponse" + }, + "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 + } + } + } + } + } + } + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "operationId": "createConsumerByUsername", + "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 }'" + } + ] + } + }, + "/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/UpstreamListResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "404": { + "$ref": "#/components/responses/NotFound" + } + }, + "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/UpstreamResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "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 body. If both URL and body IDs are supplied they must match.", + "tags": [ + "Upstreams" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "description": "`Upstream` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/UpstreamInput" + }, + { + "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/UpstreamResponse" + }, + "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" + }, + "200": { + "description": "Existing resource replaced.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpstreamResponse" + }, + "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" + } + } + } + } + } + } + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "operationId": "createUpstreamWithBodyId" + } + }, + "/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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/UpstreamUpsertPutId" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpstreamResponse" + }, + "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" + }, + "200": { + "description": "Existing resource replaced.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpstreamResponse" + }, + "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" + } + } + } + } + } + } + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/UpstreamUpsertPatchId" + }, + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpstreamResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpstreamResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + } + }, + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + }, + { + "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": { + "$ref": "#/components/schemas/DeleteResponse" + }, + "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" + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + } + }, + "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/SSLReadListResponse" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "list": [ + { + "value": { + "type": "server", + "snis": [ + "test.com" + ], + "id": "1", + "create_time": 1684373523, + "status": 1, + "cert": "-----BEGIN CERTIFICATE-----\nMIIEojCCAwqgAwIBAgIJAK253pMhgCkxMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV\nBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0RvbmcxDzANBgNVBAcMBlpodUhhaTEPMA0G\nA1UECgwGaXJlc3R5MREwDwYDVQQDDAh0ZXN0LmNvbTAgFw0xOTA2MjQyMjE4MDVa\nGA8yMTE5MDUzMTIyMTgwNVowVjELMAkGA1UEBhMCQ04xEjAQBgNVBAgMCUd1YW5n\nRG9uZzEPMA0GA1UEBwwGWmh1SGFpMQ8wDQYDVQQKDAZpcmVzdHkxETAPBgNVBAMM\nCHRlc3QuY29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAyCM0rqJe\ncvgnCfOw4fATotPwk5Ba0gC2YvIrO+gSbQkyxXF5jhZB3W6BkWUWR4oNFLLSqcVb\nVDPitz/Mt46Mo8amuS6zTbQetGnBARzPLtmVhJfoeLj0efMiOepOSZflj9Ob4yKR\n2bGdEFOdHPjm+4ggXU9jMKeLqdVvxll/JiVFBW5smPtW1Oc/BV5terhscJdOgmRr\nabf9xiIis9/qVYfyGn52u9452V0owUuwP7nZ01jt6iMWEGeQU6mwPENgvj1olji2\nWjdG2UwpUVp3jp3l7j1ekQ6mI0F7yI+LeHzfUwiyVt1TmtMWn1ztk6FfLRqwJWR/\nEvm95vnfS3Le4S2ky3XAgn2UnCMyej3wDN6qHR1onpRVeXhrBajbCRDRBMwaNw/1\n/3Uvza8QKK10PzQR6OcQ0xo9psMkd9j9ts/dTuo2fzaqpIfyUbPST4GdqNG9NyIh\n/B9g26/0EWcjyO7mYVkaycrtLMaXm1u9jyRmcQQI1cGrGwyXbrieNp63AgMBAAGj\ncTBvMB0GA1UdDgQWBBSZtSvV8mBwl0bpkvFtgyiOUUcbszAfBgNVHSMEGDAWgBSZ\ntSvV8mBwl0bpkvFtgyiOUUcbszAMBgNVHRMEBTADAQH/MB8GA1UdEQQYMBaCCHRl\nc3QuY29tggoqLnRlc3QuY29tMA0GCSqGSIb3DQEBCwUAA4IBgQAHGEul/x7ViVgC\ntC8CbXEslYEkj1XVr2Y4hXZXAXKd3W7V3TC8rqWWBbr6L/tsSVFt126V5WyRmOaY\n1A5pju8VhnkhYxYfZALQxJN2tZPFVeME9iGJ9BE1wPtpMgITX8Rt9kbNlENfAgOl\nPYzrUZN1YUQjX+X8t8/1VkSmyZysr6ngJ46/M8F16gfYXc9zFj846Z9VST0zCKob\nrJs3GtHOkS9zGGldqKKCj+Awl0jvTstI4qtS1ED92tcnJh5j/SSXCAB5FgnpKZWy\nhme45nBQj86rJ8FhN+/aQ9H9/2Ib6Q4wbpaIvf4lQdLUEcWAeZGW6Rk0JURwEog1\n7/mMgkapDglgeFx9f/XztSTrkHTaX4Obr+nYrZ2V4KOB4llZnK5GeNjDrOOJDk2y\nIJFgBOZJWyS93dQfuKEj42hA79MuX64lMSCVQSjX+ipR289GQZqFrIhiJxLyA+Ve\nU/OOcSRr39Kuis/JJ+DkgHYa/PWHZhnJQBxcqXXk1bJGw9BNbhM=\n-----END CERTIFICATE-----\n", + "update_time": 1684373523 + }, + "key": "/apisix/ssls/1", + "modifiedIndex": 133, + "createdIndex": 133 + } + ], + "total": 1 + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "404": { + "$ref": "#/components/responses/NotFound" + } + }, + "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/SSLResponse" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/ssls/00000000000000000134", + "value": { + "type": "server", + "cert": "-----BEGIN CERTIFICATE-----\nMIIEojCCAwqgAwIBAgIJAK253pMhgCkxMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV\nBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0RvbmcxDzANBgNVBAcMBlpodUhhaTEPMA0G\nA1UECgwGaXJlc3R5MREwDwYDVQQDDAh0ZXN0LmNvbTAgFw0xOTA2MjQyMjE4MDVa\nGA8yMTE5MDUzMTIyMTgwNVowVjELMAkGA1UEBhMCQ04xEjAQBgNVBAgMCUd1YW5n\nRG9uZzEPMA0GA1UEBwwGWmh1SGFpMQ8wDQYDVQQKDAZpcmVzdHkxETAPBgNVBAMM\nCHRlc3QuY29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAyCM0rqJe\ncvgnCfOw4fATotPwk5Ba0gC2YvIrO+gSbQkyxXF5jhZB3W6BkWUWR4oNFLLSqcVb\nVDPitz/Mt46Mo8amuS6zTbQetGnBARzPLtmVhJfoeLj0efMiOepOSZflj9Ob4yKR\n2bGdEFOdHPjm+4ggXU9jMKeLqdVvxll/JiVFBW5smPtW1Oc/BV5terhscJdOgmRr\nabf9xiIis9/qVYfyGn52u9452V0owUuwP7nZ01jt6iMWEGeQU6mwPENgvj1olji2\nWjdG2UwpUVp3jp3l7j1ekQ6mI0F7yI+LeHzfUwiyVt1TmtMWn1ztk6FfLRqwJWR/\nEvm95vnfS3Le4S2ky3XAgn2UnCMyej3wDN6qHR1onpRVeXhrBajbCRDRBMwaNw/1\n/3Uvza8QKK10PzQR6OcQ0xo9psMkd9j9ts/dTuo2fzaqpIfyUbPST4GdqNG9NyIh\n/B9g26/0EWcjyO7mYVkaycrtLMaXm1u9jyRmcQQI1cGrGwyXbrieNp63AgMBAAGj\ncTBvMB0GA1UdDgQWBBSZtSvV8mBwl0bpkvFtgyiOUUcbszAfBgNVHSMEGDAWgBSZ\ntSvV8mBwl0bpkvFtgyiOUUcbszAMBgNVHRMEBTADAQH/MB8GA1UdEQQYMBaCCHRl\nc3QuY29tggoqLnRlc3QuY29tMA0GCSqGSIb3DQEBCwUAA4IBgQAHGEul/x7ViVgC\ntC8CbXEslYEkj1XVr2Y4hXZXAXKd3W7V3TC8rqWWBbr6L/tsSVFt126V5WyRmOaY\n1A5pju8VhnkhYxYfZALQxJN2tZPFVeME9iGJ9BE1wPtpMgITX8Rt9kbNlENfAgOl\nPYzrUZN1YUQjX+X8t8/1VkSmyZysr6ngJ46/M8F16gfYXc9zFj846Z9VST0zCKob\nrJs3GtHOkS9zGGldqKKCj+Awl0jvTstI4qtS1ED92tcnJh5j/SSXCAB5FgnpKZWy\nhme45nBQj86rJ8FhN+/aQ9H9/2Ib6Q4wbpaIvf4lQdLUEcWAeZGW6Rk0JURwEog1\n7/mMgkapDglgeFx9f/XztSTrkHTaX4Obr+nYrZ2V4KOB4llZnK5GeNjDrOOJDk2y\nIJFgBOZJWyS93dQfuKEj42hA79MuX64lMSCVQSjX+ipR289GQZqFrIhiJxLyA+Ve\nU/OOcSRr39Kuis/JJ+DkgHYa/PWHZhnJQBxcqXXk1bJGw9BNbhM=\n-----END CERTIFICATE-----\n", + "key": "-----BEGIN RSA PRIVATE KEY-----\n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\n-----END RSA 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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "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 body. If both URL and body IDs are supplied they must match.", + "tags": [ + "SSL Certificates" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "description": "`SSL` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SSLInput" + }, + { + "required": [ + "id" + ] + } + ] + }, + "example": { + "type": "server", + "sni": "api.example.com", + "cert": "-----BEGIN CERTIFICATE-----\nMIIEojCCAwqgAwIBAgIJAK253pMhgCkxMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV\nBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0RvbmcxDzANBgNVBAcMBlpodUhhaTEPMA0G\nA1UECgwGaXJlc3R5MREwDwYDVQQDDAh0ZXN0LmNvbTAgFw0xOTA2MjQyMjE4MDVa\nGA8yMTE5MDUzMTIyMTgwNVowVjELMAkGA1UEBhMCQ04xEjAQBgNVBAgMCUd1YW5n\nRG9uZzEPMA0GA1UEBwwGWmh1SGFpMQ8wDQYDVQQKDAZpcmVzdHkxETAPBgNVBAMM\nCHRlc3QuY29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAyCM0rqJe\ncvgnCfOw4fATotPwk5Ba0gC2YvIrO+gSbQkyxXF5jhZB3W6BkWUWR4oNFLLSqcVb\nVDPitz/Mt46Mo8amuS6zTbQetGnBARzPLtmVhJfoeLj0efMiOepOSZflj9Ob4yKR\n2bGdEFOdHPjm+4ggXU9jMKeLqdVvxll/JiVFBW5smPtW1Oc/BV5terhscJdOgmRr\nabf9xiIis9/qVYfyGn52u9452V0owUuwP7nZ01jt6iMWEGeQU6mwPENgvj1olji2\nWjdG2UwpUVp3jp3l7j1ekQ6mI0F7yI+LeHzfUwiyVt1TmtMWn1ztk6FfLRqwJWR/\nEvm95vnfS3Le4S2ky3XAgn2UnCMyej3wDN6qHR1onpRVeXhrBajbCRDRBMwaNw/1\n/3Uvza8QKK10PzQR6OcQ0xo9psMkd9j9ts/dTuo2fzaqpIfyUbPST4GdqNG9NyIh\n/B9g26/0EWcjyO7mYVkaycrtLMaXm1u9jyRmcQQI1cGrGwyXbrieNp63AgMBAAGj\ncTBvMB0GA1UdDgQWBBSZtSvV8mBwl0bpkvFtgyiOUUcbszAfBgNVHSMEGDAWgBSZ\ntSvV8mBwl0bpkvFtgyiOUUcbszAMBgNVHRMEBTADAQH/MB8GA1UdEQQYMBaCCHRl\nc3QuY29tggoqLnRlc3QuY29tMA0GCSqGSIb3DQEBCwUAA4IBgQAHGEul/x7ViVgC\ntC8CbXEslYEkj1XVr2Y4hXZXAXKd3W7V3TC8rqWWBbr6L/tsSVFt126V5WyRmOaY\n1A5pju8VhnkhYxYfZALQxJN2tZPFVeME9iGJ9BE1wPtpMgITX8Rt9kbNlENfAgOl\nPYzrUZN1YUQjX+X8t8/1VkSmyZysr6ngJ46/M8F16gfYXc9zFj846Z9VST0zCKob\nrJs3GtHOkS9zGGldqKKCj+Awl0jvTstI4qtS1ED92tcnJh5j/SSXCAB5FgnpKZWy\nhme45nBQj86rJ8FhN+/aQ9H9/2Ib6Q4wbpaIvf4lQdLUEcWAeZGW6Rk0JURwEog1\n7/mMgkapDglgeFx9f/XztSTrkHTaX4Obr+nYrZ2V4KOB4llZnK5GeNjDrOOJDk2y\nIJFgBOZJWyS93dQfuKEj42hA79MuX64lMSCVQSjX+ipR289GQZqFrIhiJxLyA+Ve\nU/OOcSRr39Kuis/JJ+DkgHYa/PWHZhnJQBxcqXXk1bJGw9BNbhM=\n-----END CERTIFICATE-----\n", + "key": "-----BEGIN RSA PRIVATE KEY-----\n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\n-----END RSA PRIVATE KEY-----\n", + "id": "1" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SSLResponse" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/ssls/1", + "value": { + "type": "server", + "key": "-----BEGIN RSA PRIVATE KEY-----\n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\n-----END RSA PRIVATE KEY-----\n", + "snis": [ + "test.com" + ], + "id": "1", + "create_time": 1684373523, + "status": 1, + "cert": "-----BEGIN CERTIFICATE-----\nMIIEojCCAwqgAwIBAgIJAK253pMhgCkxMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV\nBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0RvbmcxDzANBgNVBAcMBlpodUhhaTEPMA0G\nA1UECgwGaXJlc3R5MREwDwYDVQQDDAh0ZXN0LmNvbTAgFw0xOTA2MjQyMjE4MDVa\nGA8yMTE5MDUzMTIyMTgwNVowVjELMAkGA1UEBhMCQ04xEjAQBgNVBAgMCUd1YW5n\nRG9uZzEPMA0GA1UEBwwGWmh1SGFpMQ8wDQYDVQQKDAZpcmVzdHkxETAPBgNVBAMM\nCHRlc3QuY29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAyCM0rqJe\ncvgnCfOw4fATotPwk5Ba0gC2YvIrO+gSbQkyxXF5jhZB3W6BkWUWR4oNFLLSqcVb\nVDPitz/Mt46Mo8amuS6zTbQetGnBARzPLtmVhJfoeLj0efMiOepOSZflj9Ob4yKR\n2bGdEFOdHPjm+4ggXU9jMKeLqdVvxll/JiVFBW5smPtW1Oc/BV5terhscJdOgmRr\nabf9xiIis9/qVYfyGn52u9452V0owUuwP7nZ01jt6iMWEGeQU6mwPENgvj1olji2\nWjdG2UwpUVp3jp3l7j1ekQ6mI0F7yI+LeHzfUwiyVt1TmtMWn1ztk6FfLRqwJWR/\nEvm95vnfS3Le4S2ky3XAgn2UnCMyej3wDN6qHR1onpRVeXhrBajbCRDRBMwaNw/1\n/3Uvza8QKK10PzQR6OcQ0xo9psMkd9j9ts/dTuo2fzaqpIfyUbPST4GdqNG9NyIh\n/B9g26/0EWcjyO7mYVkaycrtLMaXm1u9jyRmcQQI1cGrGwyXbrieNp63AgMBAAGj\ncTBvMB0GA1UdDgQWBBSZtSvV8mBwl0bpkvFtgyiOUUcbszAfBgNVHSMEGDAWgBSZ\ntSvV8mBwl0bpkvFtgyiOUUcbszAMBgNVHRMEBTADAQH/MB8GA1UdEQQYMBaCCHRl\nc3QuY29tggoqLnRlc3QuY29tMA0GCSqGSIb3DQEBCwUAA4IBgQAHGEul/x7ViVgC\ntC8CbXEslYEkj1XVr2Y4hXZXAXKd3W7V3TC8rqWWBbr6L/tsSVFt126V5WyRmOaY\n1A5pju8VhnkhYxYfZALQxJN2tZPFVeME9iGJ9BE1wPtpMgITX8Rt9kbNlENfAgOl\nPYzrUZN1YUQjX+X8t8/1VkSmyZysr6ngJ46/M8F16gfYXc9zFj846Z9VST0zCKob\nrJs3GtHOkS9zGGldqKKCj+Awl0jvTstI4qtS1ED92tcnJh5j/SSXCAB5FgnpKZWy\nhme45nBQj86rJ8FhN+/aQ9H9/2Ib6Q4wbpaIvf4lQdLUEcWAeZGW6Rk0JURwEog1\n7/mMgkapDglgeFx9f/XztSTrkHTaX4Obr+nYrZ2V4KOB4llZnK5GeNjDrOOJDk2y\nIJFgBOZJWyS93dQfuKEj42hA79MuX64lMSCVQSjX+ipR289GQZqFrIhiJxLyA+Ve\nU/OOcSRr39Kuis/JJ+DkgHYa/PWHZhnJQBxcqXXk1bJGw9BNbhM=\n-----END CERTIFICATE-----\n", + "update_time": 1684373523 + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "200": { + "description": "Existing resource replaced.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SSLResponse" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/ssls/1", + "value": { + "type": "server", + "key": "-----BEGIN RSA PRIVATE KEY-----\n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\n-----END RSA PRIVATE KEY-----\n", + "snis": [ + "test.com" + ], + "id": "1", + "create_time": 1684373523, + "status": 1, + "cert": "-----BEGIN CERTIFICATE-----\nMIIEojCCAwqgAwIBAgIJAK253pMhgCkxMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV\nBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0RvbmcxDzANBgNVBAcMBlpodUhhaTEPMA0G\nA1UECgwGaXJlc3R5MREwDwYDVQQDDAh0ZXN0LmNvbTAgFw0xOTA2MjQyMjE4MDVa\nGA8yMTE5MDUzMTIyMTgwNVowVjELMAkGA1UEBhMCQ04xEjAQBgNVBAgMCUd1YW5n\nRG9uZzEPMA0GA1UEBwwGWmh1SGFpMQ8wDQYDVQQKDAZpcmVzdHkxETAPBgNVBAMM\nCHRlc3QuY29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAyCM0rqJe\ncvgnCfOw4fATotPwk5Ba0gC2YvIrO+gSbQkyxXF5jhZB3W6BkWUWR4oNFLLSqcVb\nVDPitz/Mt46Mo8amuS6zTbQetGnBARzPLtmVhJfoeLj0efMiOepOSZflj9Ob4yKR\n2bGdEFOdHPjm+4ggXU9jMKeLqdVvxll/JiVFBW5smPtW1Oc/BV5terhscJdOgmRr\nabf9xiIis9/qVYfyGn52u9452V0owUuwP7nZ01jt6iMWEGeQU6mwPENgvj1olji2\nWjdG2UwpUVp3jp3l7j1ekQ6mI0F7yI+LeHzfUwiyVt1TmtMWn1ztk6FfLRqwJWR/\nEvm95vnfS3Le4S2ky3XAgn2UnCMyej3wDN6qHR1onpRVeXhrBajbCRDRBMwaNw/1\n/3Uvza8QKK10PzQR6OcQ0xo9psMkd9j9ts/dTuo2fzaqpIfyUbPST4GdqNG9NyIh\n/B9g26/0EWcjyO7mYVkaycrtLMaXm1u9jyRmcQQI1cGrGwyXbrieNp63AgMBAAGj\ncTBvMB0GA1UdDgQWBBSZtSvV8mBwl0bpkvFtgyiOUUcbszAfBgNVHSMEGDAWgBSZ\ntSvV8mBwl0bpkvFtgyiOUUcbszAMBgNVHRMEBTADAQH/MB8GA1UdEQQYMBaCCHRl\nc3QuY29tggoqLnRlc3QuY29tMA0GCSqGSIb3DQEBCwUAA4IBgQAHGEul/x7ViVgC\ntC8CbXEslYEkj1XVr2Y4hXZXAXKd3W7V3TC8rqWWBbr6L/tsSVFt126V5WyRmOaY\n1A5pju8VhnkhYxYfZALQxJN2tZPFVeME9iGJ9BE1wPtpMgITX8Rt9kbNlENfAgOl\nPYzrUZN1YUQjX+X8t8/1VkSmyZysr6ngJ46/M8F16gfYXc9zFj846Z9VST0zCKob\nrJs3GtHOkS9zGGldqKKCj+Awl0jvTstI4qtS1ED92tcnJh5j/SSXCAB5FgnpKZWy\nhme45nBQj86rJ8FhN+/aQ9H9/2Ib6Q4wbpaIvf4lQdLUEcWAeZGW6Rk0JURwEog1\n7/mMgkapDglgeFx9f/XztSTrkHTaX4Obr+nYrZ2V4KOB4llZnK5GeNjDrOOJDk2y\nIJFgBOZJWyS93dQfuKEj42hA79MuX64lMSCVQSjX+ipR289GQZqFrIhiJxLyA+Ve\nU/OOcSRr39Kuis/JJ+DkgHYa/PWHZhnJQBxcqXXk1bJGw9BNbhM=\n-----END CERTIFICATE-----\n", + "update_time": 1684373523 + } + } + } + } + } + } + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "operationId": "createSSLWithBodyId" + } + }, + "/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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/SslUpsertPutId" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SSLResponse" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/ssls/1", + "value": { + "type": "server", + "key": "-----BEGIN RSA PRIVATE KEY-----\n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\n-----END RSA PRIVATE KEY-----\n", + "snis": [ + "test.com" + ], + "id": "1", + "create_time": 1684373523, + "status": 1, + "cert": "-----BEGIN CERTIFICATE-----\nMIIEojCCAwqgAwIBAgIJAK253pMhgCkxMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV\nBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0RvbmcxDzANBgNVBAcMBlpodUhhaTEPMA0G\nA1UECgwGaXJlc3R5MREwDwYDVQQDDAh0ZXN0LmNvbTAgFw0xOTA2MjQyMjE4MDVa\nGA8yMTE5MDUzMTIyMTgwNVowVjELMAkGA1UEBhMCQ04xEjAQBgNVBAgMCUd1YW5n\nRG9uZzEPMA0GA1UEBwwGWmh1SGFpMQ8wDQYDVQQKDAZpcmVzdHkxETAPBgNVBAMM\nCHRlc3QuY29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAyCM0rqJe\ncvgnCfOw4fATotPwk5Ba0gC2YvIrO+gSbQkyxXF5jhZB3W6BkWUWR4oNFLLSqcVb\nVDPitz/Mt46Mo8amuS6zTbQetGnBARzPLtmVhJfoeLj0efMiOepOSZflj9Ob4yKR\n2bGdEFOdHPjm+4ggXU9jMKeLqdVvxll/JiVFBW5smPtW1Oc/BV5terhscJdOgmRr\nabf9xiIis9/qVYfyGn52u9452V0owUuwP7nZ01jt6iMWEGeQU6mwPENgvj1olji2\nWjdG2UwpUVp3jp3l7j1ekQ6mI0F7yI+LeHzfUwiyVt1TmtMWn1ztk6FfLRqwJWR/\nEvm95vnfS3Le4S2ky3XAgn2UnCMyej3wDN6qHR1onpRVeXhrBajbCRDRBMwaNw/1\n/3Uvza8QKK10PzQR6OcQ0xo9psMkd9j9ts/dTuo2fzaqpIfyUbPST4GdqNG9NyIh\n/B9g26/0EWcjyO7mYVkaycrtLMaXm1u9jyRmcQQI1cGrGwyXbrieNp63AgMBAAGj\ncTBvMB0GA1UdDgQWBBSZtSvV8mBwl0bpkvFtgyiOUUcbszAfBgNVHSMEGDAWgBSZ\ntSvV8mBwl0bpkvFtgyiOUUcbszAMBgNVHRMEBTADAQH/MB8GA1UdEQQYMBaCCHRl\nc3QuY29tggoqLnRlc3QuY29tMA0GCSqGSIb3DQEBCwUAA4IBgQAHGEul/x7ViVgC\ntC8CbXEslYEkj1XVr2Y4hXZXAXKd3W7V3TC8rqWWBbr6L/tsSVFt126V5WyRmOaY\n1A5pju8VhnkhYxYfZALQxJN2tZPFVeME9iGJ9BE1wPtpMgITX8Rt9kbNlENfAgOl\nPYzrUZN1YUQjX+X8t8/1VkSmyZysr6ngJ46/M8F16gfYXc9zFj846Z9VST0zCKob\nrJs3GtHOkS9zGGldqKKCj+Awl0jvTstI4qtS1ED92tcnJh5j/SSXCAB5FgnpKZWy\nhme45nBQj86rJ8FhN+/aQ9H9/2Ib6Q4wbpaIvf4lQdLUEcWAeZGW6Rk0JURwEog1\n7/mMgkapDglgeFx9f/XztSTrkHTaX4Obr+nYrZ2V4KOB4llZnK5GeNjDrOOJDk2y\nIJFgBOZJWyS93dQfuKEj42hA79MuX64lMSCVQSjX+ipR289GQZqFrIhiJxLyA+Ve\nU/OOcSRr39Kuis/JJ+DkgHYa/PWHZhnJQBxcqXXk1bJGw9BNbhM=\n-----END CERTIFICATE-----\n", + "update_time": 1684373523 + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "200": { + "description": "Existing resource replaced.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SSLResponse" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "key": "/apisix/ssls/1", + "value": { + "type": "server", + "key": "-----BEGIN RSA PRIVATE KEY-----\n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\n-----END RSA PRIVATE KEY-----\n", + "snis": [ + "test.com" + ], + "id": "1", + "create_time": 1684373523, + "status": 1, + "cert": "-----BEGIN CERTIFICATE-----\nMIIEojCCAwqgAwIBAgIJAK253pMhgCkxMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV\nBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0RvbmcxDzANBgNVBAcMBlpodUhhaTEPMA0G\nA1UECgwGaXJlc3R5MREwDwYDVQQDDAh0ZXN0LmNvbTAgFw0xOTA2MjQyMjE4MDVa\nGA8yMTE5MDUzMTIyMTgwNVowVjELMAkGA1UEBhMCQ04xEjAQBgNVBAgMCUd1YW5n\nRG9uZzEPMA0GA1UEBwwGWmh1SGFpMQ8wDQYDVQQKDAZpcmVzdHkxETAPBgNVBAMM\nCHRlc3QuY29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAyCM0rqJe\ncvgnCfOw4fATotPwk5Ba0gC2YvIrO+gSbQkyxXF5jhZB3W6BkWUWR4oNFLLSqcVb\nVDPitz/Mt46Mo8amuS6zTbQetGnBARzPLtmVhJfoeLj0efMiOepOSZflj9Ob4yKR\n2bGdEFOdHPjm+4ggXU9jMKeLqdVvxll/JiVFBW5smPtW1Oc/BV5terhscJdOgmRr\nabf9xiIis9/qVYfyGn52u9452V0owUuwP7nZ01jt6iMWEGeQU6mwPENgvj1olji2\nWjdG2UwpUVp3jp3l7j1ekQ6mI0F7yI+LeHzfUwiyVt1TmtMWn1ztk6FfLRqwJWR/\nEvm95vnfS3Le4S2ky3XAgn2UnCMyej3wDN6qHR1onpRVeXhrBajbCRDRBMwaNw/1\n/3Uvza8QKK10PzQR6OcQ0xo9psMkd9j9ts/dTuo2fzaqpIfyUbPST4GdqNG9NyIh\n/B9g26/0EWcjyO7mYVkaycrtLMaXm1u9jyRmcQQI1cGrGwyXbrieNp63AgMBAAGj\ncTBvMB0GA1UdDgQWBBSZtSvV8mBwl0bpkvFtgyiOUUcbszAfBgNVHSMEGDAWgBSZ\ntSvV8mBwl0bpkvFtgyiOUUcbszAMBgNVHRMEBTADAQH/MB8GA1UdEQQYMBaCCHRl\nc3QuY29tggoqLnRlc3QuY29tMA0GCSqGSIb3DQEBCwUAA4IBgQAHGEul/x7ViVgC\ntC8CbXEslYEkj1XVr2Y4hXZXAXKd3W7V3TC8rqWWBbr6L/tsSVFt126V5WyRmOaY\n1A5pju8VhnkhYxYfZALQxJN2tZPFVeME9iGJ9BE1wPtpMgITX8Rt9kbNlENfAgOl\nPYzrUZN1YUQjX+X8t8/1VkSmyZysr6ngJ46/M8F16gfYXc9zFj846Z9VST0zCKob\nrJs3GtHOkS9zGGldqKKCj+Awl0jvTstI4qtS1ED92tcnJh5j/SSXCAB5FgnpKZWy\nhme45nBQj86rJ8FhN+/aQ9H9/2Ib6Q4wbpaIvf4lQdLUEcWAeZGW6Rk0JURwEog1\n7/mMgkapDglgeFx9f/XztSTrkHTaX4Obr+nYrZ2V4KOB4llZnK5GeNjDrOOJDk2y\nIJFgBOZJWyS93dQfuKEj42hA79MuX64lMSCVQSjX+ipR289GQZqFrIhiJxLyA+Ve\nU/OOcSRr39Kuis/JJ+DkgHYa/PWHZhnJQBxcqXXk1bJGw9BNbhM=\n-----END CERTIFICATE-----\n", + "update_time": 1684373523 + } + } + } + } + } + } + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SSLReadResponse" + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "value": { + "type": "server", + "snis": [ + "test.com" + ], + "id": "1", + "create_time": 1684373523, + "status": 1, + "cert": "-----BEGIN CERTIFICATE-----\nMIIEojCCAwqgAwIBAgIJAK253pMhgCkxMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV\nBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0RvbmcxDzANBgNVBAcMBlpodUhhaTEPMA0G\nA1UECgwGaXJlc3R5MREwDwYDVQQDDAh0ZXN0LmNvbTAgFw0xOTA2MjQyMjE4MDVa\nGA8yMTE5MDUzMTIyMTgwNVowVjELMAkGA1UEBhMCQ04xEjAQBgNVBAgMCUd1YW5n\nRG9uZzEPMA0GA1UEBwwGWmh1SGFpMQ8wDQYDVQQKDAZpcmVzdHkxETAPBgNVBAMM\nCHRlc3QuY29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAyCM0rqJe\ncvgnCfOw4fATotPwk5Ba0gC2YvIrO+gSbQkyxXF5jhZB3W6BkWUWR4oNFLLSqcVb\nVDPitz/Mt46Mo8amuS6zTbQetGnBARzPLtmVhJfoeLj0efMiOepOSZflj9Ob4yKR\n2bGdEFOdHPjm+4ggXU9jMKeLqdVvxll/JiVFBW5smPtW1Oc/BV5terhscJdOgmRr\nabf9xiIis9/qVYfyGn52u9452V0owUuwP7nZ01jt6iMWEGeQU6mwPENgvj1olji2\nWjdG2UwpUVp3jp3l7j1ekQ6mI0F7yI+LeHzfUwiyVt1TmtMWn1ztk6FfLRqwJWR/\nEvm95vnfS3Le4S2ky3XAgn2UnCMyej3wDN6qHR1onpRVeXhrBajbCRDRBMwaNw/1\n/3Uvza8QKK10PzQR6OcQ0xo9psMkd9j9ts/dTuo2fzaqpIfyUbPST4GdqNG9NyIh\n/B9g26/0EWcjyO7mYVkaycrtLMaXm1u9jyRmcQQI1cGrGwyXbrieNp63AgMBAAGj\ncTBvMB0GA1UdDgQWBBSZtSvV8mBwl0bpkvFtgyiOUUcbszAfBgNVHSMEGDAWgBSZ\ntSvV8mBwl0bpkvFtgyiOUUcbszAMBgNVHRMEBTADAQH/MB8GA1UdEQQYMBaCCHRl\nc3QuY29tggoqLnRlc3QuY29tMA0GCSqGSIb3DQEBCwUAA4IBgQAHGEul/x7ViVgC\ntC8CbXEslYEkj1XVr2Y4hXZXAXKd3W7V3TC8rqWWBbr6L/tsSVFt126V5WyRmOaY\n1A5pju8VhnkhYxYfZALQxJN2tZPFVeME9iGJ9BE1wPtpMgITX8Rt9kbNlENfAgOl\nPYzrUZN1YUQjX+X8t8/1VkSmyZysr6ngJ46/M8F16gfYXc9zFj846Z9VST0zCKob\nrJs3GtHOkS9zGGldqKKCj+Awl0jvTstI4qtS1ED92tcnJh5j/SSXCAB5FgnpKZWy\nhme45nBQj86rJ8FhN+/aQ9H9/2Ib6Q4wbpaIvf4lQdLUEcWAeZGW6Rk0JURwEog1\n7/mMgkapDglgeFx9f/XztSTrkHTaX4Obr+nYrZ2V4KOB4llZnK5GeNjDrOOJDk2y\nIJFgBOZJWyS93dQfuKEj42hA79MuX64lMSCVQSjX+ipR289GQZqFrIhiJxLyA+Ve\nU/OOcSRr39Kuis/JJ+DkgHYa/PWHZhnJQBxcqXXk1bJGw9BNbhM=\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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + } + }, + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/SslUpsertPatchId" + }, + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SSLResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Invalid configuration" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + }, + { + "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": { + "$ref": "#/components/schemas/DeleteResponse" + }, + "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" + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + } + }, + "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/GlobalRuleListResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "404": { + "$ref": "#/components/responses/NotFound" + } + }, + "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 body. If both URL and body IDs are supplied they must match.", + "tags": [ + "Global Rules" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "description": "`GlobalRule` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/GlobalRuleInput" + }, + { + "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/GlobalRuleResponse" + }, + "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" + }, + "200": { + "description": "Existing resource replaced.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GlobalRuleResponse" + }, + "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 + } + } + } + } + } + } + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "operationId": "createGlobalRuleWithBodyId" + } + }, + "/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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/GlobalRuleUpsertPutId" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GlobalRuleResponse" + }, + "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" + }, + "200": { + "description": "Existing resource replaced.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GlobalRuleResponse" + }, + "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 + } + } + } + } + } + } + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GlobalRuleResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + } + }, + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/GlobalRuleUpsertPatchId" + }, + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GlobalRuleResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + }, + { + "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": { + "$ref": "#/components/schemas/DeleteResponse" + }, + "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" + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + } + }, + "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/ConsumerGroupListResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "404": { + "$ref": "#/components/responses/NotFound" + } + }, + "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 body. If both URL and body IDs are supplied they must match.", + "tags": [ + "Consumer Groups" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "description": "`ConsumerGroup` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ConsumerGroupInput" + }, + { + "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/ConsumerGroupResponse" + }, + "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" + }, + "200": { + "description": "Existing resource replaced.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConsumerGroupResponse" + }, + "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 + } + } + } + } + } + } + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "operationId": "createConsumerGroupWithBodyId" + } + }, + "/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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/ConsumerGroupUpsertPutId" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConsumerGroupResponse" + }, + "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" + }, + "200": { + "description": "Existing resource replaced.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConsumerGroupResponse" + }, + "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 + } + } + } + } + } + } + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConsumerGroupResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + } + }, + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/ConsumerGroupUpsertPatchId" + }, + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConsumerGroupResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + }, + { + "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": { + "$ref": "#/components/schemas/DeleteResponse" + }, + "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" + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + } + }, + "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/PluginConfigListResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "404": { + "$ref": "#/components/responses/NotFound" + } + }, + "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 body. If both URL and body IDs are supplied they must match.", + "tags": [ + "Plugin Configs" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "description": "`PluginConfig` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/PluginConfigInput" + }, + { + "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/PluginConfigResponse" + }, + "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" + }, + "200": { + "description": "Existing resource replaced.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PluginConfigResponse" + }, + "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 + } + } + } + } + } + } + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "operationId": "createPluginConfigWithBodyId" + } + }, + "/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": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + }, + "example": "1" + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/PluginConfigUpsertPutId" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PluginConfigResponse" + }, + "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" + }, + "200": { + "description": "Existing resource replaced.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PluginConfigResponse" + }, + "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 + } + } + } + } + } + } + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PluginConfigResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + } + }, + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/PluginConfigUpsertPatchId" + }, + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PluginConfigResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + }, + { + "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": { + "$ref": "#/components/schemas/DeleteResponse" + }, + "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" + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + } + }, + "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" + } + }, + "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" + } + }, + "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" + } + }, + "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": { + "type": "string", + "description": "Number of deleted keys encoded as a 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" + } + }, + "operationId": "deletePluginMetadata" + } + }, + "/apisix/admin/plugins/list": { + "get": { + "summary": "List Plugin Names", + "description": "Get a list of all available plugin names, optionally filtered by subsystem (`http` or `stream`).", + "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": { + "1": { + "summary": "Successful Example", + "value": [ + "real-ip", + "ai", + "client-control", + "proxy-control", + "request-id", + "zipkin", + "ext-plugin-pre-req", + "fault-injection", + "mocking", + "serverless-pre-function", + "cors", + "ip-restriction", + "ua-restriction", + "referer-restriction", + "csrf", + "uri-blocker", + "request-validation", + "openid-connect", + "cas-auth", + "authz-casbin", + "authz-casdoor", + "wolf-rbac", + "ldap-auth", + "hmac-auth", + "basic-auth", + "jwt-auth", + "key-auth", + "consumer-restriction", + "forward-auth", + "opa", + "authz-keycloak", + "body-transformer", + "proxy-mirror", + "proxy-cache", + "proxy-rewrite", + "workflow", + "api-breaker", + "limit-conn", + "limit-count", + "limit-req", + "gzip", + "server-info", + "traffic-split", + "redirect", + "response-rewrite", + "degraphql", + "kafka-proxy", + "grpc-transcode", + "grpc-web", + "public-api", + "prometheus", + "datadog", + "elasticsearch-logger", + "echo", + "loggly", + "http-logger", + "splunk-hec-logging", + "skywalking-logger", + "google-cloud-logging", + "sls-logger", + "tcp-logger", + "kafka-logger", + "rocketmq-logger", + "syslog", + "udp-logger", + "file-logger", + "clickhouse-logger", + "tencent-cloud-cls", + "inspect", + "example-plugin", + "aws-lambda", + "azure-functions", + "openwhisk", + "openfunction", + "serverless-post-function", + "ext-plugin-post-req", + "ext-plugin-post-resp" + ] + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + } + }, + "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" + }, + { + "name": "schema_type", + "in": "query", + "schema": { + "type": "string" + }, + "description": "consumer selects consumer_schema; all other values select the route plugin schema.", + "example": "consumer" + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "if": { + "description": "Condition for when a specific policy or configuration is selected.", + "type": "object", + "properties": { + "properties": { + "description": "Properties to validate in the conditional check.", + "type": "object" + } + } + }, + "else": { + "description": "Alternative condition for other policies or configurations.", + "type": "object", + "properties": { + "then": { + "description": "Schema to apply when the alternative condition is met.", + "type": "object", + "properties": { + "required": { + "description": "Required fields for the configuration when this condition is met.", + "type": "array", + "items": { + "type": "string" + } + }, + "properties": { + "description": "Properties for the configuration when this condition is met.", + "type": "object" + } + } + }, + "if": { + "description": "Condition for when another specific policy or configuration is selected.", + "type": "object", + "properties": { + "properties": { + "description": "Properties to validate in this alternative condition.", + "type": "object" + } + } + } + } + }, + "required": { + "type": "array", + "description": "List of required property names for the plugin configuration.", + "items": { + "type": "string" + } + }, + "then": { + "description": "Schema to apply when the primary condition is met.", + "type": "object", + "properties": { + "required": { + "description": "Required fields for the configuration when this condition is met.", + "type": "array", + "items": { + "type": "string" + } + }, + "properties": { + "description": "Properties for the configuration when this condition is met.", + "type": "object" + } + } + }, + "type": { + "description": "JSON Schema type definition.", + "type": "string" + }, + "$comment": { + "description": "Comment metadata for the schema.", + "type": "string" + }, + "properties": { + "description": "Properties of the plugin.", + "type": "object" + } + } + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "$comment": "this is a mark for our injected plugin schema.", + "type": "object", + "required": [ + "count", + "time_window" + ], + "properties": { + "_meta": { + "description": "Metadata configuration for the plugin.", + "type": "object", + "additionalProperties": false, + "properties": { + "disable": { + "description": "If set to true, disables the plugin execution.", + "type": "boolean" + }, + "error_response": { + "description": "Custom error response when the plugin rejects a request.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + }, + "filter": { + "description": "Filter determines whether the plugin needs to be executed at runtime.", + "type": "array", + "items": {} + }, + "pre_function": { + "description": "Function to be executed before plugin execution with access to conf and ctx parameters.", + "type": "string" + }, + "priority": { + "description": "Priority of plugins by customized order.", + "type": "integer" + } + } + }, + "allow_degradation": { + "description": "If set to true, enables degradation when the rate limiting backend is unavailable.", + "type": "boolean", + "default": false + }, + "count": { + "description": "Maximum number of requests allowed within the time window.", + "type": "integer", + "exclusiveMinimum": 0 + }, + "group": { + "description": "Group identifier for the rate limiting configuration.", + "type": "string" + }, + "key": { + "description": "Key to use for rate limiting identification.", + "type": "string", + "default": "remote_addr" + }, + "key_type": { + "description": "Type of the key used for rate limiting.", + "type": "string", + "enum": [ + "var", + "var_combination", + "constant" + ], + "default": "var" + }, + "policy": { + "description": "Policy for rate limiting behavior.", + "type": "string", + "enum": [ + "local", + "redis", + "redis-cluster" + ], + "default": "local" + }, + "rejected_code": { + "description": "HTTP status code returned when requests exceed the limit.", + "type": "integer", + "default": 503, + "minimum": 200, + "maximum": 599 + }, + "rejected_msg": { + "description": "Custom message returned when requests exceed the limit.", + "type": "string", + "minLength": 1 + }, + "show_limit_quota_header": { + "description": "If set to true, adds X-RateLimit-Limit and X-RateLimit-Remaining headers to responses.", + "type": "boolean", + "default": true + }, + "time_window": { + "description": "Time window in seconds for rate limiting.", + "type": "integer", + "exclusiveMinimum": 0 + } + }, + "if": { + "properties": { + "policy": { + "enum": [ + "redis" + ] + } + } + }, + "then": { + "properties": { + "redis_database": { + "description": "Redis database index.", + "type": "integer", + "default": 0, + "minimum": 0 + }, + "redis_host": { + "description": "Redis server host address.", + "type": "string", + "minLength": 2 + }, + "redis_password": { + "description": "Password for Redis authentication.", + "type": "string", + "minLength": 0 + }, + "redis_port": { + "description": "Redis server port.", + "type": "integer", + "default": 6379, + "minimum": 1 + }, + "redis_ssl": { + "description": "If set to true, enables SSL encryption for Redis connections.", + "type": "boolean", + "default": false + }, + "redis_ssl_verify": { + "description": "If set to true, verifies SSL certificates for Redis connections.", + "type": "boolean", + "default": false + }, + "redis_timeout": { + "description": "Timeout in milliseconds for Redis operations.", + "type": "integer", + "default": 1000, + "minimum": 1 + }, + "redis_username": { + "description": "Username for Redis authentication.", + "type": "string", + "minLength": 1 + } + }, + "required": [ + "redis_host" + ] + }, + "else": { + "if": { + "properties": { + "policy": { + "enum": [ + "redis-cluster" + ] + } + } + }, + "then": { + "properties": { + "redis_cluster_name": { + "description": "Name of the Redis cluster.", + "type": "string" + }, + "redis_cluster_nodes": { + "description": "List of Redis cluster node addresses.", + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "minLength": 2, + "maxLength": 100 + } + }, + "redis_cluster_ssl": { + "description": "If set to true, enables SSL encryption for Redis cluster connections.", + "type": "boolean", + "default": false + }, + "redis_cluster_ssl_verify": { + "description": "If set to true, verifies SSL certificates for Redis cluster connections.", + "type": "boolean", + "default": false + }, + "redis_password": { + "description": "Password for Redis cluster authentication.", + "type": "string", + "minLength": 0 + }, + "redis_timeout": { + "description": "Timeout in milliseconds for Redis cluster operations.", + "type": "integer", + "default": 1000, + "minimum": 1 + } + }, + "required": [ + "redis_cluster_nodes", + "redis_cluster_name" + ] + } + } + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound", + "description": "Key not found" + } + }, + "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", + "properties": { + "plugin_name": { + "description": "Plugin configuration and metadata.", + "type": "object", + "properties": { + "version": { + "description": "Plugin schema version number.", + "type": "number" + }, + "schema": { + "description": "JSON Schema defining the plugin configuration structure.", + "type": "object", + "properties": { + "required": { + "description": "Required fields for the plugin.", + "type": "array", + "items": { + "type": "string" + } + }, + "type": { + "description": "JSON Schema type definition.", + "type": "string" + }, + "$comment": { + "description": "Comment metadata for the schema.", + "type": "string" + }, + "properties": { + "description": "Plugin properties.", + "type": "object" + } + } + }, + "priority": { + "description": "Execution priority of the plugin.", + "type": "integer" + } + } + } + } + }, + "examples": { + "1": { + "summary": "Successful Example", + "value": { + "limit-conn": { + "priority": 1003, + "version": 0.1, + "schema": { + "type": "object", + "$comment": "this is a mark for our injected plugin schema", + "required": [ + "conn", + "burst", + "default_conn_delay", + "key" + ], + "properties": { + "only_use_default_delay": { + "type": "boolean", + "default": false + }, + "conn": { + "type": "integer", + "exclusiveMinimum": 0 + }, + "burst": { + "type": "integer", + "minimum": 0 + }, + "key_type": { + "type": "string", + "enum": [ + "var", + "var_combination" + ], + "default": "var" + }, + "_meta": { + "type": "object", + "properties": { + "filter": { + "type": "array", + "description": "filter determines whether the plugin needs to be executed at runtime", + "items": {} + }, + "error_response": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + }, + "priority": { + "type": "integer", + "description": "priority of plugins by customized order" + }, + "disable": { + "type": "boolean" + } + } + }, + "key": { + "type": "string" + }, + "default_conn_delay": { + "type": "number", + "exclusiveMinimum": 0 + } + } + } + }, + "syslog": { + "schema": { + "type": "object", + "$comment": "this is a mark for our injected plugin schema", + "required": [ + "host", + "port" + ], + "properties": { + "_meta": { + "type": "object", + "properties": { + "filter": { + "type": "array", + "description": "filter determines whether the plugin needs to be executed at runtime", + "items": {} + }, + "error_response": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + }, + "priority": { + "type": "integer", + "description": "priority of plugins by customized order" + }, + "disable": { + "type": "boolean" + } + } + }, + "host": { + "type": "string" + }, + "max_retry_count": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "drop_limit": { + "type": "integer", + "default": 1048576 + }, + "flush_limit": { + "type": "integer", + "default": 4096, + "minimum": 1 + }, + "pool_size": { + "type": "integer", + "default": 5, + "minimum": 5 + }, + "sock_type": { + "type": "string", + "default": "tcp", + "enum": [ + "tcp", + "udp" + ] + }, + "buffer_duration": { + "type": "integer", + "default": 60, + "minimum": 1 + }, + "retry_delay": { + "type": "integer", + "default": 1, + "minimum": 0 + }, + "tls": { + "type": "boolean", + "default": false + }, + "name": { + "type": "string", + "default": "stream sys logger" + }, + "log_format": { + "type": "object" + }, + "batch_max_size": { + "type": "integer", + "default": 1000, + "minimum": 1 + }, + "timeout": { + "type": "integer", + "default": 3000, + "minimum": 1 + }, + "inactive_timeout": { + "type": "integer", + "default": 5, + "minimum": 1 + }, + "port": { + "type": "integer" + } + } + }, + "priority": 401, + "version": 0.1, + "metadata_schema": { + "type": "object", + "properties": { + "log_format": { + "type": "object", + "default": { + "@timestamp": "$time_iso8601", + "host": "$host", + "client_ip": "$remote_addr" + } + } + } + } + }, + "ip-restriction": { + "priority": 3000, + "version": 0.1, + "schema": { + "type": "object", + "oneOf": [ + { + "required": [ + "whitelist" + ] + }, + { + "required": [ + "blacklist" + ] + } + ], + "$comment": "this is a mark for our injected plugin schema", + "properties": { + "message": { + "type": "string", + "minLength": 1, + "maxLength": 1024, + "default": "Your IP address is not allowed" + }, + "_meta": { + "type": "object", + "properties": { + "filter": { + "type": "array", + "description": "filter determines whether the plugin needs to be executed at runtime", + "items": {} + }, + "error_response": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + }, + "priority": { + "type": "integer", + "description": "priority of plugins by customized order" + }, + "disable": { + "type": "boolean" + } + } + }, + "blacklist": { + "type": "array", + "minItems": 1, + "items": { + "anyOf": [ + { + "type": "string", + "format": "ipv4", + "title": "IPv4" + }, + { + "type": "string", + "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])$", + "title": "IPv4/CIDR" + }, + { + "type": "string", + "format": "ipv6", + "title": "IPv6" + }, + { + "type": "string", + "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}$", + "title": "IPv6/CIDR" + } + ] + } + }, + "whitelist": { + "type": "array", + "minItems": 1, + "items": { + "anyOf": [ + { + "type": "string", + "format": "ipv4", + "title": "IPv4" + }, + { + "type": "string", + "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])$", + "title": "IPv4/CIDR" + }, + { + "type": "string", + "format": "ipv6", + "title": "IPv6" + }, + { + "type": "string", + "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}$", + "title": "IPv6/CIDR" + } + ] + } + } + } + } + }, + "mqtt-proxy": { + "priority": 1000, + "version": 0.1, + "schema": { + "type": "object", + "$comment": "this is a mark for our injected plugin schema", + "required": [ + "protocol_name", + "protocol_level" + ], + "properties": { + "_meta": { + "type": "object", + "properties": { + "filter": { + "type": "array", + "description": "filter determines whether the plugin needs to be executed at runtime", + "items": {} + }, + "error_response": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + }, + "priority": { + "type": "integer", + "description": "priority of plugins by customized order" + }, + "disable": { + "type": "boolean" + } + } + }, + "protocol_name": { + "type": "string" + }, + "protocol_level": { + "type": "integer" + } + } + } + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest", + "description": "Not found plugin name" + }, + "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" + }, + "503": { + "description": "Failed to broadcast reload event.", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + }, + "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/StreamRouteListResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "404": { + "$ref": "#/components/responses/NotFound" + } + }, + "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/StreamRouteResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "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 body. If both URL and body IDs are supplied they must match.", + "tags": [ + "Stream Routes" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "description": "`StreamRoute` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/StreamRouteInput" + }, + { + "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/StreamRouteResponse" + }, + "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" + }, + "200": { + "description": "Existing resource replaced.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StreamRouteResponse" + }, + "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" + } + } + } + } + } + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "operationId": "createStreamRouteWithBodyId" + } + }, + "/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": { + "anyOf": [ + { + "type": "object", + "required": [ + "total", + "list" + ], + "properties": { + "total": { + "type": "integer" + }, + "list": { + "type": "array", + "items": { + "type": "object", + "required": [ + "key", + "value" + ], + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/Secret" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + } + } + } + } + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "node": { + "type": "object", + "properties": { + "nodes": { + "type": "array", + "items": { + "type": "object", + "required": [ + "key", + "value" + ], + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/Secret" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "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": { + "description": "Etcd operation failed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "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", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + }, + "example": "1" + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/ProtoUpsertPutId" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProtoResponse" + }, + "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" + }, + "200": { + "description": "Existing resource replaced.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProtoResponse" + }, + "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 }" + } + } + } + } + } + } + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "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", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + }, + "example": "1" + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProtoResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + } + }, + "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", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + }, + "example": "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": { + "$ref": "#/components/schemas/DeleteResponse" + }, + "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" + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + } + }, + "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/ProtoListResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "404": { + "$ref": "#/components/responses/NotFound" + } + }, + "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/ProtoResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "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 body. If both URL and body IDs are supplied they must match.", + "tags": [ + "Protos" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "description": "`Proto` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ProtoInput" + }, + { + "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/ProtoResponse" + }, + "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" + }, + "200": { + "description": "Existing resource replaced.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProtoResponse" + }, + "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 }" + } + } + } + } + } + } + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "operationId": "createProtoWithBodyId" + } + }, + "/apisix/admin/schema/validate/{resource}": { + "post": { + "summary": "Validate Resource Configuration", + "description": "Validate a resource configuration against APISIX's JSON Schema without creating the resource. Useful for dry-run validation in CI/CD pipelines.\n\nThe `{resource}` path parameter specifies the resource type (e.g., `routes`, `upstreams`, `services`, etc.).", + "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 + } + } + } + } + } + }, + "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", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + }, + "example": "1" + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StreamRouteResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "404": { + "$ref": "#/components/responses/NotFound" + } + }, + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/StreamRouteUpsertPutId" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StreamRouteResponse" + }, + "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" + }, + "200": { + "description": "Existing resource replaced.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StreamRouteResponse" + }, + "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" + } + } + } + } + } + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "operationId": "createStreamRouteById" + }, + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + }, + { + "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": { + "$ref": "#/components/schemas/DeleteResponse" + }, + "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" + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + } + }, + "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/CredentialListResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "404": { + "$ref": "#/components/responses/NotFound" + } + }, + "operationId": "listCredentials" + }, + "put": { + "summary": "Create or replace a credential using a body ID", + "description": "The credential 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": [ + { + "$ref": "#/components/schemas/CredentialInput" + }, + { + "required": [ + "id" + ] + } + ] + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CredentialResponse" + }, + "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" + }, + "200": { + "description": "Existing resource replaced.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CredentialResponse" + }, + "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 + } + } + } + } + } + } + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "operationId": "createCredentialWithBodyId" + } + }, + "/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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CredentialResponse" + }, + "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": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "404": { + "$ref": "#/components/responses/NotFound" + } + }, + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/CredentialUpsert" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CredentialResponse" + }, + "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" + }, + "200": { + "description": "Existing resource replaced.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CredentialResponse" + }, + "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 + } + } + } + } + } + } + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + }, + { + "$ref": "#/components/parameters/Force" + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeleteResponse" + }, + "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" + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + } + }, + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + } + ], + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "required": [ + "key", + "value" + ], + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/Secret" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + } + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "node": { + "type": "object", + "required": [ + "key", + "value" + ], + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/Secret" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "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": { + "description": "Etcd operation failed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "operationId": "getSecret" + }, + "put": { + "summary": "Create or Replace a Secret", + "description": "Configure a secret manager integration with a specified ID. secret_type selects the corresponding VaultSecret, AwsSecret, or GcpSecret schema; a payload for another backend is rejected.", + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/SecretUpsertPutCollection" + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "required": [ + "key", + "value" + ], + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/Secret" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + } + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "node": { + "type": "object", + "required": [ + "key", + "value" + ], + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/Secret" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + } + } + } + } + ] + }, + "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": { + "description": "Etcd operation failed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "200": { + "description": "Existing secret configuration replaced.", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "required": [ + "key", + "value" + ], + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/Secret" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + } + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "node": { + "type": "object", + "required": [ + "key", + "value" + ], + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/Secret" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + } + } + } + } + ] + }, + "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" + } + } + } + } + } + } + }, + "operationId": "createSecretById" + }, + "patch": { + "summary": "Update a Secret (Partial)", + "description": "Partially update a secret manager configuration. secret_type selects the corresponding VaultSecret, AwsSecret, or GcpSecret schema; a payload for another backend is rejected.", + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + }, + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/SecretUpsertPatchCollection" + }, + "responses": { + "200": { + "description": "Successful response.", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "required": [ + "key", + "value" + ], + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/Secret" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + } + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "node": { + "type": "object", + "required": [ + "key", + "value" + ], + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/Secret" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + } + } + } + } + ] + }, + "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": { + "description": "Etcd operation failed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "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": "1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + } + }, + { + "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": { + "$ref": "#/components/schemas/DeleteResponse" + }, + "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" + }, + "503": { + "description": "Etcd operation failed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "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 only when `deployment.config_provider` is `yaml`. Returns the entire current configuration document.", + "responses": { + "200": { + "description": "Current standalone configuration.", + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "application/yaml": { + "schema": { + "type": "string" + } + } + }, + "headers": { + "X-Digest": { + "description": "Configuration digest supplied by the client.", + "schema": { + "type": "string" + } + }, + "X-Last-Modified": { + "description": "Last configuration update, Unix timestamp.", + "schema": { + "type": "integer" + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "500": { + "description": "Unable to read, encode, store, or broadcast the configuration." + } + }, + "parameters": [] + }, + "put": { + "tags": [ + "Standalone" + ], + "operationId": "putStandaloneConfig", + "summary": "Replace the standalone configuration", + "description": "Replaces the entire current configuration. Available only when `deployment.config_provider` is `yaml`. X-Digest is required. Resource arrays omitted from a newer version are removed; equal per-resource *_conf_version values preserve the corresponding stored array. Versions must not decrease. Accepts JSON or application/yaml.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConfigValidationDocument" + } + }, + "application/yaml": { + "schema": { + "type": "string" + } + } + } + }, + "responses": { + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "500": { + "description": "Unable to read, encode, store, or broadcast the configuration." + }, + "202": { + "description": "Configuration accepted and broadcast to workers.", + "headers": { + "X-Digest": { + "description": "Configuration digest supplied by the client.", + "schema": { + "type": "string" + } + }, + "X-Last-Modified": { + "description": "Last configuration update, Unix timestamp.", + "schema": { + "type": "integer" + } + } + } + }, + "204": { + "description": "Digest unchanged; configuration was not updated." + } + }, + "parameters": [ + { + "name": "X-Digest", + "in": "header", + "required": true, + "schema": { + "type": "string" + }, + "description": "Client-supplied digest. An unchanged digest returns 204 without applying the body.", + "example": "configuration-v1" + } + ] + }, + "head": { + "tags": [ + "Standalone" + ], + "operationId": "headStandaloneConfig", + "summary": "Probe standalone configuration availability", + "description": "Returns `200 OK` if the deployment is in YAML provider mode and the configuration endpoint is reachable.", + "responses": { + "200": { + "description": "Standalone configuration is reachable.", + "headers": { + "X-Digest": { + "description": "Configuration digest supplied by the client.", + "schema": { + "type": "string" + } + }, + "X-Last-Modified": { + "description": "Last configuration update, Unix timestamp.", + "schema": { + "type": "integer" + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "500": { + "description": "Unable to read, encode, store, or broadcast the 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. sub_path may contain multiple slash-separated segments. OpenAPI path templates cannot express the router wildcard; substitute the full path manually when necessary.", + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": {}, + "example": 1 + } + } + }, + "responses": { + "200": { + "description": "Sub-field replaced.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RouteResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/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. sub_path may contain multiple slash-separated segments. OpenAPI path templates cannot express the router wildcard; substitute the full path manually when necessary.", + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": {}, + "example": true + } + } + }, + "responses": { + "200": { + "description": "Sub-field replaced.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServiceResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/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. sub_path may contain multiple slash-separated segments. OpenAPI path templates cannot express the router wildcard; substitute the full path manually when necessary.", + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": {}, + "example": 3 + } + } + }, + "responses": { + "200": { + "description": "Sub-field replaced.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpstreamResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/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. sub_path may contain multiple slash-separated segments. OpenAPI path templates cannot express the router wildcard; substitute the full path manually when necessary.", + "parameters": [ + { + "$ref": "#/components/parameters/Ttl" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": {}, + "example": 1 + } + } + }, + "responses": { + "200": { + "description": "Sub-field replaced.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SSLResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/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. sub_path may contain multiple slash-separated segments. OpenAPI path templates cannot express the router wildcard; substitute the full path manually when necessary.", + "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.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConsumerGroupResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/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. sub_path may contain multiple slash-separated segments. OpenAPI path templates cannot express the router wildcard; substitute the full path manually when necessary.", + "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.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GlobalRuleResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/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. sub_path may contain multiple slash-separated segments. OpenAPI path templates cannot express the router wildcard; substitute the full path manually when necessary.", + "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.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PluginConfigResponse" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "503": { + "description": "Etcd operation failed (including conflicting concurrent PATCH).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/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": [ + { + "$ref": "#/components/parameters/Page" + }, + { + "$ref": "#/components/parameters/PageSize" + } + ], + "responses": { + "200": { + "description": "Paginated list of secrets for this manager.", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "required": [ + "total", + "list" + ], + "properties": { + "total": { + "type": "integer" + }, + "list": { + "type": "array", + "items": { + "type": "object", + "required": [ + "key", + "value" + ], + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/Secret" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + } + } + } + } + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "node": { + "type": "object", + "properties": { + "nodes": { + "type": "array", + "items": { + "type": "object", + "required": [ + "key", + "value" + ], + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/Secret" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + } + } + } + } + } + } + } + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "503": { + "description": "Etcd operation failed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/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. sub_path may contain multiple slash-separated segments. OpenAPI path templates cannot express the router wildcard; substitute the full path manually when necessary. secret_type selects the corresponding VaultSecret, AwsSecret, or GcpSecret schema; a payload for another backend is rejected.", + "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": { + "description": "Etcd operation failed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "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" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound" + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/Subsystem" + }, + { + "name": "schema_type", + "in": "query", + "schema": { + "type": "string" + }, + "description": "consumer selects consumer_schema; all other values select the route plugin schema.", + "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": { + "anyOf": [ + { + "type": "object", + "required": [ + "message" + ], + "properties": { + "message": { + "type": "string" + } + } + }, + { + "$ref": "#/components/schemas/ErrorResponse" + } + ], + "description": "Etcd missing keys return message; handler dispatch and plugin errors return error_msg." + }, + "ResourceId": { + "anyOf": [ + { + "maxLength": 64, + "type": "string", + "pattern": "^[a-zA-Z0-9-_.]+$", + "minLength": 1 + }, + { + "minimum": 1, + "type": "integer" + } + ], + "description": "Unique identifier for the resource. Can be a string (1–64 characters, alphanumeric with `-`, `_`, `.`) or a positive integer." + }, + "Timeout": { + "type": "object", + "required": [ + "connect", + "send", + "read" + ], + "properties": { + "send": { + "exclusiveMinimum": 0, + "type": "number", + "description": "Timeout in seconds for sending data to the upstream.", + "examples": [ + 6 + ] + }, + "read": { + "exclusiveMinimum": 0, + "type": "number", + "description": "Timeout in seconds for reading the response from the upstream.", + "examples": [ + 6 + ] + }, + "connect": { + "exclusiveMinimum": 0, + "type": "number", + "description": "Timeout in seconds for establishing a connection to the upstream.", + "examples": [ + 6 + ] + } + }, + "description": "Timeout settings in seconds for connecting to, sending data to, and reading data from the upstream.", + "examples": [ + { + "connect": 6, + "send": 6, + "read": 6 + } + ] + }, + "Labels": { + "type": "object", + "description": "Key-value pairs for categorizing and filtering resources. Values must be non-empty strings (max 256 characters).", + "patternProperties": { + ".*": { + "type": "string", + "description": "value of label", + "minLength": 1, + "pattern": "^\\S+$", + "maxLength": 256 + } + }, + "examples": [ + { + "env": "production", + "team": "platform", + "version": "v2" + } + ] + }, + "HealthCheckActiveHealthy": { + "properties": { + "interval": { + "minimum": 1, + "type": "integer", + "default": 1, + "description": "Interval in seconds between active health check probes for healthy targets." + }, + "http_statuses": { + "items": { + "minimum": 200, + "type": "integer", + "maximum": 599 + }, + "type": "array", + "default": [ + 200, + 302 + ], + "minItems": 1, + "uniqueItems": true, + "description": "HTTP status codes that indicate a healthy target." + }, + "successes": { + "minimum": 1, + "type": "integer", + "default": 2, + "maximum": 254, + "description": "Number of consecutive successful probes to consider a target healthy." + } + }, + "type": "object", + "description": "Thresholds for considering a target healthy during active checks." + }, + "HealthCheckActiveUnhealthy": { + "properties": { + "timeouts": { + "minimum": 1, + "type": "integer", + "default": 3, + "maximum": 254, + "description": "Number of probe timeouts to consider a target unhealthy." + }, + "http_failures": { + "minimum": 1, + "type": "integer", + "default": 5, + "maximum": 254, + "description": "Number of HTTP failures to consider a target unhealthy." + }, + "interval": { + "minimum": 1, + "type": "integer", + "default": 1, + "description": "Interval in seconds between active health check probes for unhealthy targets." + }, + "http_statuses": { + "items": { + "minimum": 200, + "type": "integer", + "maximum": 599 + }, + "type": "array", + "default": [ + 429, + 404, + 500, + 501, + 502, + 503, + 504, + 505 + ], + "minItems": 1, + "uniqueItems": true, + "description": "HTTP status codes that indicate an unhealthy target." + }, + "tcp_failures": { + "minimum": 1, + "type": "integer", + "default": 2, + "maximum": 254, + "description": "Number of TCP connection failures to consider a target unhealthy." + } + }, + "type": "object", + "description": "Thresholds for considering a target unhealthy during active checks." + }, + "HealthCheckActive": { + "properties": { + "type": { + "type": "string", + "default": "http", + "enum": [ + "http", + "https", + "tcp" + ], + "description": "Protocol type for health check probes." + }, + "timeout": { + "default": 1, + "type": "number", + "description": "Timeout in seconds for each health check probe." + }, + "req_headers": { + "items": { + "uniqueItems": true, + "type": "string" + }, + "type": "array", + "minItems": 1, + "description": "Additional HTTP headers to include in health check probe requests." + }, + "http_path": { + "default": "/", + "type": "string", + "description": "HTTP request path for health check probes." + }, + "concurrency": { + "default": 10, + "type": "integer", + "description": "Number of targets to check concurrently during active health checks." + }, + "host": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string", + "description": "Hostname to use in the health check probe `Host` header. Defaults to the upstream node's host." + }, + "port": { + "minimum": 1, + "type": "integer", + "maximum": 65535, + "description": "Port to use for health check probes. Defaults to the upstream node's port." + }, + "unhealthy": { + "$ref": "#/components/schemas/HealthCheckActiveUnhealthy" + }, + "healthy": { + "$ref": "#/components/schemas/HealthCheckActiveHealthy" + }, + "https_verify_certificate": { + "default": true, + "type": "boolean", + "description": "Whether to verify the upstream's TLS certificate during HTTPS health checks." + } + }, + "type": "object", + "description": "Active health check configuration. APISIX periodically sends probes to upstream nodes to determine their health status." + }, + "HealthCheckPassiveHealthy": { + "properties": { + "http_statuses": { + "items": { + "minimum": 200, + "type": "integer", + "maximum": 599 + }, + "type": "array", + "default": [ + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 226, + 300, + 301, + 302, + 303, + 304, + 305, + 306, + 307, + 308 + ], + "minItems": 1, + "uniqueItems": true, + "description": "HTTP status codes from real traffic that indicate a healthy target." + }, + "successes": { + "minimum": 0, + "type": "integer", + "default": 5, + "maximum": 254, + "description": "Number of consecutive successful requests to restore a target to healthy status." + } + }, + "type": "object", + "description": "Thresholds for considering a target healthy based on real traffic responses." + }, + "HealthCheckPassiveUnhealthy": { + "properties": { + "http_failures": { + "minimum": 0, + "type": "integer", + "default": 5, + "maximum": 254, + "description": "Number of HTTP failures from real traffic to consider a target unhealthy." + }, + "timeouts": { + "minimum": 0, + "type": "integer", + "default": 7, + "maximum": 254, + "description": "Number of timeouts from real traffic to consider a target unhealthy." + }, + "http_statuses": { + "items": { + "minimum": 200, + "type": "integer", + "maximum": 599 + }, + "type": "array", + "default": [ + 429, + 500, + 503 + ], + "minItems": 1, + "uniqueItems": true, + "description": "HTTP status codes from real traffic that indicate an unhealthy target." + }, + "tcp_failures": { + "minimum": 0, + "type": "integer", + "default": 2, + "maximum": 254, + "description": "Number of TCP failures from real traffic to consider a target unhealthy." + } + }, + "type": "object", + "description": "Thresholds for considering a target unhealthy based on real traffic responses." + }, + "HealthCheckPassive": { + "properties": { + "unhealthy": { + "$ref": "#/components/schemas/HealthCheckPassiveUnhealthy" + }, + "type": { + "type": "string", + "default": "http", + "enum": [ + "http", + "https", + "tcp" + ], + "description": "Protocol type for passive health monitoring." + }, + "healthy": { + "$ref": "#/components/schemas/HealthCheckPassiveHealthy" + } + }, + "type": "object", + "description": "Passive health check configuration. APISIX monitors real traffic responses to determine upstream node health — no extra probes are sent." + }, + "HealthCheck": { + "type": "object", + "properties": { + "passive": { + "$ref": "#/components/schemas/HealthCheckPassive" + }, + "active": { + "$ref": "#/components/schemas/HealthCheckActive" + } + }, + "anyOf": [ + { + "required": [ + "active" + ] + }, + { + "required": [ + "active", + "passive" + ] + } + ], + "description": "Health check configuration for monitoring upstream node availability. Active checks require at least the `active` field." + }, + "UpstreamNodesMap": { + "patternProperties": { + ".*": { + "minimum": 0, + "type": "integer", + "description": "weight of node" + } + }, + "type": "object", + "description": "Upstream nodes as a map of `\"host:port\": weight` pairs. This is the shorthand format.", + "examples": [ + { + "httpbin.org:80": 1, + "10.0.0.1:8080": 2 + } + ] + }, + "UpstreamNodeItem": { + "type": "object", + "required": [ + "host", + "weight" + ], + "properties": { + "host": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string", + "description": "Hostname or IP address of the upstream node.", + "examples": [ + "10.0.0.1" + ] + }, + "port": { + "minimum": 1, + "type": "integer", + "description": "Port of the upstream node.", + "maximum": 65535, + "examples": [ + 8080 + ] + }, + "weight": { + "minimum": 0, + "type": "integer", + "description": "Weight for load balancing. Higher values mean more traffic. `0` means no traffic.", + "examples": [ + 1 + ] + }, + "priority": { + "type": "integer", + "description": "Priority of the node. Nodes with higher priority are used first. Lower-priority nodes serve as fallbacks.", + "default": 0 + }, + "metadata": { + "$ref": "#/components/schemas/Plugins" + } + }, + "description": "An upstream node with explicit host, port, weight, and optional priority." + }, + "UpstreamNodes": { + "anyOf": [ + { + "patternProperties": { + ".*": { + "minimum": 0, + "type": "integer", + "description": "weight of node" + } + }, + "type": "object" + }, + { + "items": { + "$ref": "#/components/schemas/UpstreamNodeItem" + }, + "type": "array" + } + ], + "description": "Upstream backend nodes. Supports two formats:\n\n**Map format** (shorthand): `{\"host:port\": weight}`\n\n**Array format** (full control): `[{\"host\": \"...\", \"port\": ..., \"weight\": ...}]`" + }, + "UpstreamTLS": { + "type": "object", + "properties": { + "verify": { + "type": "boolean", + "description": "Whether to verify the upstream server's TLS certificate. Currently only supported for Kafka upstreams.", + "default": false + }, + "client_key": { + "type": "string", + "maxLength": 65536, + "minLength": 64, + "description": "PEM-encoded client private key for mTLS with the upstream." + }, + "client_cert_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "client_cert": { + "type": "string", + "maxLength": 65536, + "minLength": 128, + "description": "PEM-encoded client certificate for mTLS with the upstream." + } + }, + "dependentSchemas": { + "client_key": { + "required": [ + "client_cert" + ] + }, + "client_cert_id": { + "not": { + "required": [ + "client_cert", + "client_key" + ] + } + }, + "client_cert": { + "required": [ + "client_key" + ] + } + }, + "description": "TLS configuration for connecting to upstream nodes over HTTPS/gRPCS." + }, + "KeepalivePool": { + "properties": { + "idle_timeout": { + "minimum": 0, + "type": "number", + "default": 60, + "description": "Time in seconds after which an idle keepalive connection is closed. `0` disables the timeout." + }, + "requests": { + "minimum": 1, + "type": "integer", + "default": 1000, + "description": "Maximum number of requests that can be sent over a single keepalive connection before it is closed." + }, + "size": { + "minimum": 1, + "type": "integer", + "default": 320, + "description": "Maximum number of keepalive connections cached in the connection pool." + } + }, + "type": "object", + "description": "Connection pool configuration for keepalive connections to upstream nodes." + }, + "Upstream": { + "oneOf": [ + { + "required": [ + "nodes" + ] + }, + { + "required": [ + "service_name", + "discovery_type" + ] + } + ], + "type": "object", + "additionalProperties": false, + "properties": { + "tls": { + "$ref": "#/components/schemas/UpstreamTLS" + }, + "update_time": { + "type": "integer", + "description": "Unix timestamp when the upstream was last updated. Read-only, set automatically." + }, + "retries": { + "minimum": 0, + "type": "integer", + "description": "Number of retry attempts when a request to the upstream fails." + }, + "key": { + "description": "The hash key used when `type` is `chash`. Required when `hash_on` is `header`, `cookie`, `vars`, or `vars_combinations`. Not needed when `hash_on` is `consumer`.", + "type": "string" + }, + "pass_host": { + "default": "pass", + "type": "string", + "description": "How to set the `Host` header when proxying to the upstream.", + "enum": [ + "pass", + "node", + "rewrite" + ] + }, + "service_name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Service name for service discovery. Required when `discovery_type` is set." + }, + "discovery_args": { + "properties": { + "namespace_id": { + "description": "Namespace ID for Nacos service discovery.", + "type": "string" + }, + "group_name": { + "description": "Group name for service discovery.", + "type": "string" + } + }, + "type": "object", + "description": "Additional arguments for service discovery." + }, + "type": { + "type": "string", + "description": "Load balancing algorithm.", + "default": "roundrobin" + }, + "timeout": { + "$ref": "#/components/schemas/Timeout" + }, + "checks": { + "$ref": "#/components/schemas/HealthCheck" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "upstream_host": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string", + "description": "Custom `Host` header value. Only effective when `pass_host` is `rewrite`." + }, + "discovery_type": { + "description": "Service discovery type (e.g., `dns`, `consul`, `nacos`, `eureka`). Required when using service discovery instead of static `nodes`.", + "type": "string" + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description of the upstream." + }, + "scheme": { + "enum": [ + "grpc", + "grpcs", + "http", + "https", + "tcp", + "tls", + "udp", + "kafka" + ], + "default": "http", + "description": "Protocol for communicating with upstream nodes." + }, + "create_time": { + "type": "integer", + "description": "Unix timestamp when the upstream was created. Read-only, set automatically." + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Human-readable name for the upstream." + }, + "hash_on": { + "type": "string", + "default": "vars", + "enum": [ + "vars", + "header", + "cookie", + "consumer", + "vars_combinations" + ], + "description": "What to hash on when `type` is `chash`." + }, + "nodes": { + "$ref": "#/components/schemas/UpstreamNodes" + }, + "keepalive_pool": { + "$ref": "#/components/schemas/KeepalivePool" + }, + "retry_timeout": { + "minimum": 0, + "type": "number", + "description": "Maximum time in seconds for retry attempts. `0` disables the retry timeout." + }, + "labels": { + "$ref": "#/components/schemas/Labels" + } + }, + "description": "Upstream configuration defining backend service nodes and load balancing behavior.\n\nAn upstream must specify either `nodes` (static list) or `service_name` + `discovery_type` (service discovery)." + }, + "Plugins": { + "type": "object", + "description": "Plugin configuration map. Each configured plugin is additionally validated using its subsystem schema and check_schema callback.", + "examples": [ + { + "limit-count": { + "count": 100, + "time_window": 60, + "rejected_code": 429, + "key_type": "var", + "key": "remote_addr" + } + } + ] + }, + "Route": { + "type": "object", + "additionalProperties": false, + "not": { + "anyOf": [ + { + "required": [ + "script", + "plugins" + ] + }, + { + "required": [ + "script", + "plugin_config_id" + ] + } + ] + }, + "properties": { + "service_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "update_time": { + "type": "integer", + "description": "Unix timestamp of last update. Read-only." + }, + "uris": { + "items": { + "description": "HTTP uri", + "type": "string" + }, + "type": "array", + "minItems": 1, + "uniqueItems": true, + "description": "Multiple URI patterns. Mutually exclusive with `uri`." + }, + "hosts": { + "items": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "type": "array", + "minItems": 1, + "uniqueItems": true, + "description": "Multiple hostnames to match. Mutually exclusive with `host`." + }, + "remote_addrs": { + "items": { + "type": "string", + "description": "client IP", + "anyOf": [ + { + "type": "string", + "title": "IPv4", + "format": "ipv4" + }, + { + "type": "string", + "title": "IPv4/CIDR", + "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": "IPv6", + "format": "ipv6" + }, + { + "type": "string", + "title": "IPv6/CIDR", + "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": "array", + "minItems": 1, + "uniqueItems": true, + "description": "Multiple client IP addresses or CIDR ranges to match. Mutually exclusive with `remote_addr`." + }, + "host": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string", + "description": "Single hostname to match against the request's `Host` header. Supports wildcard prefix (`*.example.com`). Mutually exclusive with `hosts`.", + "examples": [ + "api.example.com" + ] + }, + "remote_addr": { + "type": "string", + "description": "Single client IP address or CIDR range to match. Mutually exclusive with `remote_addrs`.", + "anyOf": [ + { + "type": "string", + "title": "IPv4", + "format": "ipv4" + }, + { + "type": "string", + "title": "IPv4/CIDR", + "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": "IPv6", + "format": "ipv6" + }, + { + "type": "string", + "title": "IPv6/CIDR", + "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}$" + } + ], + "examples": [ + "192.168.1.0/24" + ] + }, + "script_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "upstream_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "plugins": { + "$ref": "#/components/schemas/Plugins" + }, + "priority": { + "default": 0, + "type": "integer", + "description": "Route priority. When multiple routes match the same request, the one with the highest priority wins." + }, + "uri": { + "type": "string", + "maxLength": 4096, + "minLength": 1, + "description": "URI path pattern to match. Supports exact match (`/api/v1/users`) and prefix match (`/api/*`). Mutually exclusive with `uris`.", + "examples": [ + "/api/v1/users/*" + ] + }, + "methods": { + "items": { + "type": "string", + "description": "HTTP method", + "enum": [ + "GET", + "POST", + "PUT", + "DELETE", + "PATCH", + "HEAD", + "OPTIONS", + "CONNECT", + "TRACE", + "PURGE" + ] + }, + "type": "array", + "uniqueItems": true, + "description": "HTTP methods allowed on this route. If empty or not set, all methods are allowed.", + "examples": [ + [ + "GET", + "POST" + ] + ] + }, + "timeout": { + "$ref": "#/components/schemas/Timeout" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "status": { + "default": 1, + "type": "integer", + "description": "Route status.", + "enum": [ + 1, + 0 + ] + }, + "enable_websocket": { + "description": "Enable WebSocket proxying on this route.", + "type": "boolean" + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description of what this route does." + }, + "upstream": { + "$ref": "#/components/schemas/Upstream" + }, + "create_time": { + "type": "integer", + "description": "Unix timestamp of creation. Read-only." + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Human-readable name for the route." + }, + "plugin_config_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "script": { + "type": "string", + "maxLength": 102400, + "minLength": 10, + "description": "Lua script for plugin orchestration. Cannot be used together with `plugins` or `plugin_config_id`." + }, + "filter_func": { + "type": "string", + "pattern": "^function", + "minLength": 10, + "description": "Custom Lua function for advanced request matching. Must start with `function`." + }, + "vars": { + "type": "array", + "description": "Custom matching conditions using [APISIX expressions](https://apisix.apache.org/docs/apisix/3.17/router-radixtree/#how-to-filter-route-by-nginx-built-in-variable). Each condition is an array of `[variable, operator, value]`.", + "examples": [ + [ + [ + "http_user_agent", + "~*", + "curl" + ] + ] + ] + }, + "labels": { + "$ref": "#/components/schemas/Labels" + } + }, + "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" + ] + } + ] + } + ], + "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" + ] + } + ], + "description": "Route configuration. A route matches incoming requests by URI, host, methods, and other conditions, then forwards them to an upstream.\n\nA route must have at least a `uri` (or `uris`) and one of: `upstream`, `upstream_id`, `service_id`, or `plugins`." + }, + "Service": { + "type": "object", + "additionalProperties": false, + "properties": { + "update_time": { + "type": "integer", + "description": "Unix timestamp of last update. Read-only." + }, + "upstream": { + "$ref": "#/components/schemas/Upstream" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "hosts": { + "items": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "type": "array", + "minItems": 1, + "uniqueItems": true, + "description": "Hostnames associated with this service." + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description of this service." + }, + "create_time": { + "type": "integer", + "description": "Unix timestamp of creation. Read-only." + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Human-readable name." + }, + "upstream_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "plugins": { + "$ref": "#/components/schemas/Plugins" + }, + "enable_websocket": { + "description": "Enable WebSocket proxying for routes using this service.", + "type": "boolean" + }, + "script": { + "type": "string", + "maxLength": 102400, + "minLength": 10, + "description": "Lua script for plugin orchestration." + }, + "labels": { + "$ref": "#/components/schemas/Labels" + } + }, + "description": "Service configuration. A service groups an upstream and plugins so multiple routes can share the same backend configuration." + }, + "Consumer": { + "additionalProperties": false, + "type": "object", + "required": [ + "username" + ], + "properties": { + "username": { + "maxLength": 256, + "type": "string", + "pattern": "^[a-zA-Z0-9_\\-]+$", + "minLength": 1, + "description": "Unique username for the consumer. Only alphanumeric characters, hyphens, and underscores are allowed.", + "examples": [ + "my-app" + ] + }, + "create_time": { + "type": "integer", + "description": "Unix timestamp of creation. Read-only." + }, + "update_time": { + "type": "integer", + "description": "Unix timestamp of last update. Read-only." + }, + "plugins": { + "$ref": "#/components/schemas/Plugins" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "group_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description of this consumer." + } + }, + "description": "Consumer configuration. Represents an API user or application with authentication credentials and plugin policies." + }, + "Credential": { + "type": "object", + "additionalProperties": false, + "properties": { + "create_time": { + "type": "integer", + "description": "Unix timestamp of creation. Read-only." + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Human-readable name." + }, + "plugins": { + "maxProperties": 1, + "type": "object", + "description": "At most one authentication plugin configuration (for example, `key-auth`, `basic-auth`, or `jwt-auth`)." + }, + "id": { + "oneOf": [ + { + "$ref": "#/components/schemas/ResourceId" + }, + { + "maxLength": 128, + "type": "string", + "pattern": "^[a-zA-Z0-9-_]+/credentials/[a-zA-Z0-9-_.]+$", + "minLength": 15 + } + ], + "description": "Unique identifier for the resource. Can be a string (1–64 characters, alphanumeric with `-`, `_`, `.`) or a positive integer." + }, + "update_time": { + "type": "integer", + "description": "Unix timestamp of last update. Read-only." + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description of this credential." + } + }, + "description": "Credential configuration for a consumer. A credential can store at most one authentication plugin." + }, + "ConsumerGroup": { + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "plugins" + ], + "properties": { + "create_time": { + "type": "integer", + "description": "Unix timestamp of creation. Read-only." + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Human-readable name." + }, + "plugins": { + "$ref": "#/components/schemas/Plugins" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "update_time": { + "type": "integer", + "description": "Unix timestamp of last update. Read-only." + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description of this group." + } + }, + "description": "Consumer Group configuration. Defines shared plugin configurations for multiple consumers." + }, + "SSL": { + "type": "object", + "additionalProperties": false, + "then": { + "oneOf": [ + { + "required": [ + "sni", + "key", + "cert" + ] + }, + { + "required": [ + "snis", + "key", + "cert" + ] + } + ] + }, + "properties": { + "type": { + "enum": [ + "server", + "client" + ], + "type": "string", + "description": "Certificate type.", + "default": "server" + }, + "update_time": { + "type": "integer", + "description": "Unix timestamp of last update. Read-only." + }, + "snis": { + "items": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "type": "array", + "minItems": 1, + "description": "Multiple SNI hostnames. Mutually exclusive with `sni`." + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "status": { + "default": 1, + "type": "integer", + "description": "SSL status.", + "enum": [ + 1, + 0 + ] + }, + "cert": { + "type": "string", + "maxLength": 65536, + "minLength": 128, + "description": "PEM-encoded server certificate. Can also be a secret reference (`$secret://...`)." + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description." + }, + "client": { + "type": "object", + "required": [ + "ca" + ], + "properties": { + "ca": { + "type": "string", + "maxLength": 65536, + "minLength": 128, + "description": "PEM-encoded CA certificate for client verification." + }, + "depth": { + "minimum": 0, + "type": "integer", + "default": 1, + "description": "Maximum certificate chain depth." + }, + "skip_mtls_uri_regex": { + "items": { + "description": "uri regular expression to skip mtls", + "type": "string" + }, + "type": "array", + "minItems": 1, + "uniqueItems": true, + "description": "URI regex patterns to skip mTLS verification." + } + }, + "description": "mTLS client verification settings." + }, + "create_time": { + "type": "integer", + "description": "Unix timestamp of creation. Read-only." + }, + "key": { + "type": "string", + "maxLength": 65536, + "minLength": 64, + "description": "PEM-encoded private key. Can also be a secret reference (`$secret://...`). **Not returned in GET responses for security.**" + }, + "ssl_protocols": { + "items": { + "enum": [ + "TLSv1.1", + "TLSv1.2", + "TLSv1.3" + ] + }, + "type": "array", + "description": "Allowed TLS protocol versions.", + "maxItems": 3, + "uniqueItems": true + }, + "keys": { + "items": { + "type": "string", + "maxLength": 65536, + "minLength": 64 + }, + "type": "array", + "description": "Additional private keys corresponding to `certs`." + }, + "certs": { + "items": { + "type": "string", + "maxLength": 65536, + "minLength": 128 + }, + "type": "array", + "description": "Additional certificates for certificate chain or alternative key types." + }, + "sni": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string", + "description": "Server Name Indication (SNI) to match. Supports wildcard prefix (`*.example.com`). Mutually exclusive with `snis`.", + "examples": [ + "api.example.com" + ] + }, + "labels": { + "$ref": "#/components/schemas/Labels" + } + }, + "if": { + "properties": { + "type": { + "enum": [ + "server" + ] + } + } + }, + "else": { + "required": [ + "key", + "cert" + ], + "title": "SNI not required" + }, + "description": "SSL certificate configuration for TLS termination or mTLS." + }, + "GlobalRule": { + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "plugins" + ], + "properties": { + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "create_time": { + "type": "integer", + "description": "Unix timestamp of creation. Read-only." + }, + "update_time": { + "type": "integer", + "description": "Unix timestamp of last update. Read-only." + }, + "plugins": { + "$ref": "#/components/schemas/Plugins" + } + }, + "description": "Global Rule configuration. Global plugins apply independently of locally bound plugins. For HTTP requests, execution is interleaved by phase: global `rewrite`, local `rewrite`, global `access`, then local `access`. Priorities order plugins within each phase and scope, and an early response skips handlers scheduled later." + }, + "PluginConfig": { + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "plugins" + ], + "properties": { + "create_time": { + "type": "integer", + "description": "Unix timestamp of creation. Read-only." + }, + "name": { + "type": "string", + "description": "Human-readable name." + }, + "plugins": { + "$ref": "#/components/schemas/Plugins" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "update_time": { + "type": "integer", + "description": "Unix timestamp of last update. Read-only." + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description." + } + }, + "description": "Plugin Config — a reusable set of plugin configurations that can be referenced by multiple routes." + }, + "StreamRoute": { + "type": "object", + "additionalProperties": false, + "properties": { + "service_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "update_time": { + "type": "integer", + "description": "Unix timestamp of last update. Read-only." + }, + "upstream": { + "$ref": "#/components/schemas/Upstream" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "protocol": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "logger": { + "items": { + "properties": { + "conf": { + "$ref": "#/components/schemas/Plugins" + }, + "name": { + "type": "string" + }, + "filter": { + "description": "logger filter rules", + "type": "array" + } + }, + "additionalProperties": false, + "dependentRequired": { + "name": [ + "conf" + ] + } + }, + "type": "array" + }, + "superior_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "name": { + "type": "string" + }, + "conf": { + "$ref": "#/components/schemas/Plugins" + } + } + }, + "server_port": { + "minimum": 1, + "type": "integer", + "description": "Server port to match.", + "maximum": 65535 + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description." + }, + "upstream_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "create_time": { + "type": "integer", + "description": "Unix timestamp of creation. Read-only." + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Human-readable name." + }, + "server_addr": { + "type": "string", + "description": "Server IP address to match.", + "anyOf": [ + { + "type": "string", + "title": "IPv4", + "format": "ipv4" + }, + { + "type": "string", + "title": "IPv4/CIDR", + "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": "IPv6", + "format": "ipv6" + }, + { + "type": "string", + "title": "IPv6/CIDR", + "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}$" + } + ] + }, + "plugins": { + "$ref": "#/components/schemas/Plugins" + }, + "sni": { + "type": "string", + "description": "SNI hostname to match (for TLS connections).", + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$" + }, + "remote_addr": { + "type": "string", + "description": "Client IP address or CIDR range to match.", + "anyOf": [ + { + "type": "string", + "title": "IPv4", + "format": "ipv4" + }, + { + "type": "string", + "title": "IPv4/CIDR", + "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": "IPv6", + "format": "ipv6" + }, + { + "type": "string", + "title": "IPv6/CIDR", + "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}$" + } + ] + }, + "labels": { + "$ref": "#/components/schemas/Labels" + } + }, + "description": "Stream Route for TCP/UDP (L4) proxying. Matches transport-layer connections by client IP, server address/port, or SNI." + }, + "Proto": { + "additionalProperties": false, + "type": "object", + "required": [ + "content" + ], + "properties": { + "create_time": { + "type": "integer", + "description": "Unix timestamp of creation. Read-only." + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Human-readable name." + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "update_time": { + "type": "integer", + "description": "Unix timestamp of last update. Read-only." + }, + "content": { + "type": "string", + "maxLength": 1048576, + "minLength": 1, + "description": "Protobuf `.proto` file content as a string." + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description." + } + }, + "description": "Protocol buffer definition for gRPC transcoding." + }, + "SSLRead": { + "type": "object", + "additionalProperties": false, + "then": { + "oneOf": [ + { + "required": [ + "sni", + "cert" + ] + }, + { + "required": [ + "snis", + "cert" + ] + } + ] + }, + "properties": { + "type": { + "enum": [ + "server", + "client" + ], + "type": "string", + "description": "Certificate type.", + "default": "server" + }, + "update_time": { + "type": "integer", + "description": "Unix timestamp of last update. Read-only." + }, + "snis": { + "items": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "type": "array", + "minItems": 1, + "description": "Multiple SNI hostnames. Mutually exclusive with `sni`." + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "status": { + "default": 1, + "type": "integer", + "description": "SSL status.", + "enum": [ + 1, + 0 + ] + }, + "cert": { + "type": "string", + "maxLength": 65536, + "minLength": 128, + "description": "PEM-encoded server certificate. Can also be a secret reference (`$secret://...`)." + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description." + }, + "client": { + "type": "object", + "required": [ + "ca" + ], + "properties": { + "ca": { + "type": "string", + "maxLength": 65536, + "minLength": 128, + "description": "PEM-encoded CA certificate for client verification." + }, + "depth": { + "minimum": 0, + "type": "integer", + "default": 1, + "description": "Maximum certificate chain depth." + }, + "skip_mtls_uri_regex": { + "items": { + "description": "uri regular expression to skip mtls", + "type": "string" + }, + "type": "array", + "minItems": 1, + "uniqueItems": true, + "description": "URI regex patterns to skip mTLS verification." + } + }, + "description": "mTLS client verification settings." + }, + "create_time": { + "type": "integer", + "description": "Unix timestamp of creation. Read-only." + }, + "ssl_protocols": { + "items": { + "enum": [ + "TLSv1.1", + "TLSv1.2", + "TLSv1.3" + ] + }, + "type": "array", + "description": "Allowed TLS protocol versions.", + "maxItems": 3, + "uniqueItems": true + }, + "certs": { + "items": { + "type": "string", + "maxLength": 65536, + "minLength": 128 + }, + "type": "array", + "description": "Additional certificates for certificate chain or alternative key types." + }, + "sni": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string", + "description": "Server Name Indication (SNI) to match. Supports wildcard prefix (`*.example.com`). Mutually exclusive with `snis`.", + "examples": [ + "api.example.com" + ] + }, + "labels": { + "$ref": "#/components/schemas/Labels" + } + }, + "if": { + "properties": { + "type": { + "enum": [ + "server" + ] + } + } + }, + "else": { + "required": [ + "cert" + ], + "title": "SNI not required" + }, + "description": "SSL resource returned by GET; private keys are omitted." + }, + "CredentialEnvelope": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/Credential" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "required": [ + "key", + "value" + ] + }, + "CredentialListEnvelope": { + "type": "object", + "properties": { + "total": { + "type": "integer" + }, + "list": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CredentialEnvelope" + } + } + }, + "required": [ + "total", + "list" + ] + }, + "RouteEnvelope": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/Route" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "required": [ + "key", + "value" + ] + }, + "RouteListEnvelope": { + "type": "object", + "properties": { + "total": { + "type": "integer" + }, + "list": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RouteEnvelope" + } + } + }, + "required": [ + "total", + "list" + ] + }, + "ServiceEnvelope": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/Service" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "required": [ + "key", + "value" + ] + }, + "ServiceListEnvelope": { + "type": "object", + "properties": { + "total": { + "type": "integer" + }, + "list": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ServiceEnvelope" + } + } + }, + "required": [ + "total", + "list" + ] + }, + "UpstreamEnvelope": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/Upstream" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "required": [ + "key", + "value" + ] + }, + "UpstreamListEnvelope": { + "type": "object", + "properties": { + "total": { + "type": "integer" + }, + "list": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UpstreamEnvelope" + } + } + }, + "required": [ + "total", + "list" + ] + }, + "ConsumerEnvelope": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/Consumer" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "required": [ + "key", + "value" + ] + }, + "ConsumerListEnvelope": { + "type": "object", + "properties": { + "total": { + "type": "integer" + }, + "list": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ConsumerEnvelope" + } + } + }, + "required": [ + "total", + "list" + ] + }, + "ConsumerGroupEnvelope": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/ConsumerGroup" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "required": [ + "key", + "value" + ] + }, + "ConsumerGroupListEnvelope": { + "type": "object", + "properties": { + "total": { + "type": "integer" + }, + "list": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ConsumerGroupEnvelope" + } + } + }, + "required": [ + "total", + "list" + ] + }, + "SSLEnvelope": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/SSL" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "required": [ + "key", + "value" + ] + }, + "SSLListEnvelope": { + "type": "object", + "properties": { + "total": { + "type": "integer" + }, + "list": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SSLEnvelope" + } + } + }, + "required": [ + "total", + "list" + ] + }, + "GlobalRuleEnvelope": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/GlobalRule" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "required": [ + "key", + "value" + ] + }, + "GlobalRuleListEnvelope": { + "type": "object", + "properties": { + "total": { + "type": "integer" + }, + "list": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GlobalRuleEnvelope" + } + } + }, + "required": [ + "total", + "list" + ] + }, + "PluginConfigEnvelope": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/PluginConfig" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "required": [ + "key", + "value" + ] + }, + "PluginConfigListEnvelope": { + "type": "object", + "properties": { + "total": { + "type": "integer" + }, + "list": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PluginConfigEnvelope" + } + } + }, + "required": [ + "total", + "list" + ] + }, + "StreamRouteEnvelope": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/StreamRoute" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "required": [ + "key", + "value" + ] + }, + "StreamRouteListEnvelope": { + "type": "object", + "properties": { + "total": { + "type": "integer" + }, + "list": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StreamRouteEnvelope" + } + } + }, + "required": [ + "total", + "list" + ] + }, + "ProtoEnvelope": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/Proto" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "required": [ + "key", + "value" + ] + }, + "ProtoListEnvelope": { + "type": "object", + "properties": { + "total": { + "type": "integer" + }, + "list": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProtoEnvelope" + } + } + }, + "required": [ + "total", + "list" + ] + }, + "RoutePatch": { + "type": "object", + "additionalProperties": false, + "properties": { + "service_id": { + "anyOf": [ + { + "anyOf": [ + { + "maxLength": 64, + "type": "string", + "pattern": "^[a-zA-Z0-9-_.]+$", + "minLength": 1 + }, + { + "minimum": 1, + "type": "integer" + } + ], + "description": "Unique identifier for the resource. Can be a string (1–64 characters, alphanumeric with `-`, `_`, `.`) or a positive integer." + }, + { + "type": "null" + } + ] + }, + "uris": { + "anyOf": [ + { + "items": { + "description": "HTTP uri", + "type": "string" + }, + "type": "array", + "minItems": 1, + "uniqueItems": true, + "description": "Multiple URI patterns. Mutually exclusive with `uri`." + }, + { + "type": "null" + } + ] + }, + "hosts": { + "anyOf": [ + { + "items": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "type": "array", + "minItems": 1, + "uniqueItems": true, + "description": "Multiple hostnames to match. Mutually exclusive with `host`." + }, + { + "type": "null" + } + ] + }, + "remote_addrs": { + "anyOf": [ + { + "items": { + "type": "string", + "description": "client IP", + "anyOf": [ + { + "type": "string", + "title": "IPv4", + "format": "ipv4" + }, + { + "type": "string", + "title": "IPv4/CIDR", + "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": "IPv6", + "format": "ipv6" + }, + { + "type": "string", + "title": "IPv6/CIDR", + "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": "array", + "minItems": 1, + "uniqueItems": true, + "description": "Multiple client IP addresses or CIDR ranges to match. Mutually exclusive with `remote_addr`." + }, + { + "type": "null" + } + ] + }, + "host": { + "anyOf": [ + { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string", + "description": "Single hostname to match against the request's `Host` header. Supports wildcard prefix (`*.example.com`). Mutually exclusive with `hosts`.", + "examples": [ + "api.example.com" + ] + }, + { + "type": "null" + } + ] + }, + "remote_addr": { + "anyOf": [ + { + "type": "string", + "description": "Single client IP address or CIDR range to match. Mutually exclusive with `remote_addrs`.", + "anyOf": [ + { + "type": "string", + "title": "IPv4", + "format": "ipv4" + }, + { + "type": "string", + "title": "IPv4/CIDR", + "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": "IPv6", + "format": "ipv6" + }, + { + "type": "string", + "title": "IPv6/CIDR", + "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}$" + } + ], + "examples": [ + "192.168.1.0/24" + ] + }, + { + "type": "null" + } + ] + }, + "script_id": { + "anyOf": [ + { + "anyOf": [ + { + "maxLength": 64, + "type": "string", + "pattern": "^[a-zA-Z0-9-_.]+$", + "minLength": 1 + }, + { + "minimum": 1, + "type": "integer" + } + ], + "description": "Unique identifier for the resource. Can be a string (1–64 characters, alphanumeric with `-`, `_`, `.`) or a positive integer." + }, + { + "type": "null" + } + ] + }, + "upstream_id": { + "anyOf": [ + { + "anyOf": [ + { + "maxLength": 64, + "type": "string", + "pattern": "^[a-zA-Z0-9-_.]+$", + "minLength": 1 + }, + { + "minimum": 1, + "type": "integer" + } + ], + "description": "Unique identifier for the resource. Can be a string (1–64 characters, alphanumeric with `-`, `_`, `.`) or a positive integer." + }, + { + "type": "null" + } + ] + }, + "plugins": { + "anyOf": [ + { + "type": "object", + "description": "Plugin configurations as a JSON object. Each key is a plugin name, and the value is that plugin's configuration object.\n\nExample:\n```json\n{\n \"limit-count\": {\n \"count\": 100,\n \"time_window\": 60\n },\n \"key-auth\": {}\n}\n```\n\nSee the [Plugin Hub](https://apisix.apache.org/plugins/) for available plugins and their schemas.", + "examples": [ + { + "limit-count": { + "count": 100, + "time_window": 60, + "rejected_code": 429, + "key_type": "var", + "key": "remote_addr" + } + } + ] + }, + { + "type": "null" + } + ] + }, + "priority": { + "anyOf": [ + { + "default": 0, + "type": "integer", + "description": "Route priority. When multiple routes match the same request, the one with the highest priority wins." + }, + { + "type": "null" + } + ] + }, + "uri": { + "anyOf": [ + { + "type": "string", + "maxLength": 4096, + "minLength": 1, + "description": "URI path pattern to match. Supports exact match (`/api/v1/users`) and prefix match (`/api/*`). Mutually exclusive with `uris`.", + "examples": [ + "/api/v1/users/*" + ] + }, + { + "type": "null" + } + ] + }, + "methods": { + "anyOf": [ + { + "items": { + "type": "string", + "description": "HTTP method", + "enum": [ + "GET", + "POST", + "PUT", + "DELETE", + "PATCH", + "HEAD", + "OPTIONS", + "CONNECT", + "TRACE", + "PURGE" + ] + }, + "type": "array", + "uniqueItems": true, + "description": "HTTP methods allowed on this route. If empty or not set, all methods are allowed.", + "examples": [ + [ + "GET", + "POST" + ] + ] + }, + { + "type": "null" + } + ] + }, + "timeout": { + "anyOf": [ + { + "type": "object", + "properties": { + "send": { + "anyOf": [ + { + "exclusiveMinimum": 0, + "type": "number", + "description": "Timeout in seconds for sending data to the upstream.", + "examples": [ + 6 + ] + }, + { + "type": "null" + } + ] + }, + "read": { + "anyOf": [ + { + "exclusiveMinimum": 0, + "type": "number", + "description": "Timeout in seconds for reading the response from the upstream.", + "examples": [ + 6 + ] + }, + { + "type": "null" + } + ] + }, + "connect": { + "anyOf": [ + { + "exclusiveMinimum": 0, + "type": "number", + "description": "Timeout in seconds for establishing a connection to the upstream.", + "examples": [ + 6 + ] + }, + { + "type": "null" + } + ] + } + }, + "description": "Timeout settings in seconds for connecting to, sending data to, and reading data from the upstream.", + "examples": [ + { + "connect": 6, + "send": 6, + "read": 6 + } + ] + }, + { + "type": "null" + } + ] + }, + "id": { + "anyOf": [ + { + "anyOf": [ + { + "maxLength": 64, + "type": "string", + "pattern": "^[a-zA-Z0-9-_.]+$", + "minLength": 1 + }, + { + "minimum": 1, + "type": "integer" + } + ], + "description": "Unique identifier for the resource. Can be a string (1–64 characters, alphanumeric with `-`, `_`, `.`) or a positive integer." + }, + { + "type": "null" + } + ] + }, + "status": { + "anyOf": [ + { + "default": 1, + "type": "integer", + "description": "Route status.", + "enum": [ + 1, + 0 + ] + }, + { + "type": "null" + } + ] + }, + "enable_websocket": { + "anyOf": [ + { + "description": "Enable WebSocket proxying on this route.", + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "desc": { + "anyOf": [ + { + "maxLength": 256, + "type": "string", + "description": "Description of what this route does." + }, + { + "type": "null" + } + ] + }, + "upstream": { + "anyOf": [ + { + "$ref": "#/components/schemas/UpstreamPatch" + }, + { + "type": "null" + } + ] + }, + "name": { + "anyOf": [ + { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Human-readable name for the route." + }, + { + "type": "null" + } + ] + }, + "plugin_config_id": { + "anyOf": [ + { + "anyOf": [ + { + "maxLength": 64, + "type": "string", + "pattern": "^[a-zA-Z0-9-_.]+$", + "minLength": 1 + }, + { + "minimum": 1, + "type": "integer" + } + ], + "description": "Unique identifier for the resource. Can be a string (1–64 characters, alphanumeric with `-`, `_`, `.`) or a positive integer." + }, + { + "type": "null" + } + ] + }, + "script": { + "anyOf": [ + { + "type": "string", + "maxLength": 102400, + "minLength": 10, + "description": "Lua script for plugin orchestration. Cannot be used together with `plugins` or `plugin_config_id`." + }, + { + "type": "null" + } + ] + }, + "filter_func": { + "anyOf": [ + { + "type": "string", + "pattern": "^function", + "minLength": 10, + "description": "Custom Lua function for advanced request matching. Must start with `function`." + }, + { + "type": "null" + } + ] + }, + "vars": { + "anyOf": [ + { + "type": "array", + "description": "Custom matching conditions using [APISIX expressions](https://apisix.apache.org/docs/apisix/3.17/router-radixtree/#how-to-filter-route-by-nginx-built-in-variable). Each condition is an array of `[variable, operator, value]`.", + "examples": [ + [ + [ + "http_user_agent", + "~*", + "curl" + ] + ] + ] + }, + { + "type": "null" + } + ] + }, + "labels": { + "anyOf": [ + { + "type": "object", + "description": "Key-value pairs for categorizing and filtering resources. Values must be non-empty strings (max 256 characters).", + "patternProperties": { + ".*": { + "anyOf": [ + { + "type": "string", + "description": "value of label", + "minLength": 1, + "pattern": "^\\S+$", + "maxLength": 256 + }, + { + "type": "null" + } + ] + } + }, + "examples": [ + { + "env": "production", + "team": "platform", + "version": "v2" + } + ] + }, + { + "type": "null" + } + ] + } + }, + "description": "Partial object merged recursively into the stored resource. Null removes a field. Arrays replace existing arrays. The complete resulting resource is validated." + }, + "ServicePatch": { + "type": "object", + "additionalProperties": false, + "properties": { + "upstream": { + "anyOf": [ + { + "$ref": "#/components/schemas/UpstreamPatch" + }, + { + "type": "null" + } + ] + }, + "id": { + "anyOf": [ + { + "anyOf": [ + { + "maxLength": 64, + "type": "string", + "pattern": "^[a-zA-Z0-9-_.]+$", + "minLength": 1 + }, + { + "minimum": 1, + "type": "integer" + } + ], + "description": "Unique identifier for the resource. Can be a string (1–64 characters, alphanumeric with `-`, `_`, `.`) or a positive integer." + }, + { + "type": "null" + } + ] + }, + "hosts": { + "anyOf": [ + { + "items": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "type": "array", + "minItems": 1, + "uniqueItems": true, + "description": "Hostnames associated with this service." + }, + { + "type": "null" + } + ] + }, + "desc": { + "anyOf": [ + { + "maxLength": 256, + "type": "string", + "description": "Description of this service." + }, + { + "type": "null" + } + ] + }, + "name": { + "anyOf": [ + { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Human-readable name." + }, + { + "type": "null" + } + ] + }, + "upstream_id": { + "anyOf": [ + { + "anyOf": [ + { + "maxLength": 64, + "type": "string", + "pattern": "^[a-zA-Z0-9-_.]+$", + "minLength": 1 + }, + { + "minimum": 1, + "type": "integer" + } + ], + "description": "Unique identifier for the resource. Can be a string (1–64 characters, alphanumeric with `-`, `_`, `.`) or a positive integer." + }, + { + "type": "null" + } + ] + }, + "plugins": { + "anyOf": [ + { + "type": "object", + "description": "Plugin configurations as a JSON object. Each key is a plugin name, and the value is that plugin's configuration object.\n\nExample:\n```json\n{\n \"limit-count\": {\n \"count\": 100,\n \"time_window\": 60\n },\n \"key-auth\": {}\n}\n```\n\nSee the [Plugin Hub](https://apisix.apache.org/plugins/) for available plugins and their schemas.", + "examples": [ + { + "limit-count": { + "count": 100, + "time_window": 60, + "rejected_code": 429, + "key_type": "var", + "key": "remote_addr" + } + } + ] + }, + { + "type": "null" + } + ] + }, + "enable_websocket": { + "anyOf": [ + { + "description": "Enable WebSocket proxying for routes using this service.", + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "script": { + "anyOf": [ + { + "type": "string", + "maxLength": 102400, + "minLength": 10, + "description": "Lua script for plugin orchestration." + }, + { + "type": "null" + } + ] + }, + "labels": { + "anyOf": [ + { + "type": "object", + "description": "Key-value pairs for categorizing and filtering resources. Values must be non-empty strings (max 256 characters).", + "patternProperties": { + ".*": { + "anyOf": [ + { + "type": "string", + "description": "value of label", + "minLength": 1, + "pattern": "^\\S+$", + "maxLength": 256 + }, + { + "type": "null" + } + ] + } + }, + "examples": [ + { + "env": "production", + "team": "platform", + "version": "v2" + } + ] + }, + { + "type": "null" + } + ] + } + }, + "description": "Partial object merged recursively into the stored resource. Null removes a field. Arrays replace existing arrays. The complete resulting resource is validated." + }, + "UpstreamPatch": { + "type": "object", + "additionalProperties": false, + "properties": { + "tls": { + "anyOf": [ + { + "type": "object", + "properties": { + "verify": { + "anyOf": [ + { + "type": "boolean", + "description": "Whether to verify the upstream server's TLS certificate. Currently only supported for Kafka upstreams.", + "default": false + }, + { + "type": "null" + } + ] + }, + "client_key": { + "anyOf": [ + { + "type": "string", + "maxLength": 65536, + "minLength": 64, + "description": "PEM-encoded client private key for mTLS with the upstream." + }, + { + "type": "null" + } + ] + }, + "client_cert_id": { + "anyOf": [ + { + "anyOf": [ + { + "maxLength": 64, + "type": "string", + "pattern": "^[a-zA-Z0-9-_.]+$", + "minLength": 1 + }, + { + "minimum": 1, + "type": "integer" + } + ], + "description": "Unique identifier for the resource. Can be a string (1–64 characters, alphanumeric with `-`, `_`, `.`) or a positive integer." + }, + { + "type": "null" + } + ] + }, + "client_cert": { + "anyOf": [ + { + "type": "string", + "maxLength": 65536, + "minLength": 128, + "description": "PEM-encoded client certificate for mTLS with the upstream." + }, + { + "type": "null" + } + ] + } + }, + "description": "TLS configuration for connecting to upstream nodes over HTTPS/gRPCS." + }, + { + "type": "null" + } + ] + }, + "retries": { + "anyOf": [ + { + "minimum": 0, + "type": "integer", + "description": "Number of retry attempts when a request to the upstream fails." + }, + { + "type": "null" + } + ] + }, + "key": { + "anyOf": [ + { + "description": "The hash key used when `type` is `chash`. Required when `hash_on` is `header`, `cookie`, `vars`, or `vars_combinations`. Not needed when `hash_on` is `consumer`.", + "type": "string" + }, + { + "type": "null" + } + ] + }, + "pass_host": { + "anyOf": [ + { + "default": "pass", + "type": "string", + "description": "How to set the `Host` header when proxying to the upstream.", + "enum": [ + "pass", + "node", + "rewrite" + ] + }, + { + "type": "null" + } + ] + }, + "service_name": { + "anyOf": [ + { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Service name for service discovery. Required when `discovery_type` is set." + }, + { + "type": "null" + } + ] + }, + "discovery_args": { + "anyOf": [ + { + "properties": { + "namespace_id": { + "anyOf": [ + { + "description": "Namespace ID for Nacos service discovery.", + "type": "string" + }, + { + "type": "null" + } + ] + }, + "group_name": { + "anyOf": [ + { + "description": "Group name for service discovery.", + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "type": "object", + "description": "Additional arguments for service discovery." + }, + { + "type": "null" + } + ] + }, + "type": { + "anyOf": [ + { + "type": "string", + "description": "Load balancing algorithm.", + "default": "roundrobin" + }, + { + "type": "null" + } + ] + }, + "timeout": { + "anyOf": [ + { + "type": "object", + "properties": { + "send": { + "anyOf": [ + { + "exclusiveMinimum": 0, + "type": "number", + "description": "Timeout in seconds for sending data to the upstream.", + "examples": [ + 6 + ] + }, + { + "type": "null" + } + ] + }, + "read": { + "anyOf": [ + { + "exclusiveMinimum": 0, + "type": "number", + "description": "Timeout in seconds for reading the response from the upstream.", + "examples": [ + 6 + ] + }, + { + "type": "null" + } + ] + }, + "connect": { + "anyOf": [ + { + "exclusiveMinimum": 0, + "type": "number", + "description": "Timeout in seconds for establishing a connection to the upstream.", + "examples": [ + 6 + ] + }, + { + "type": "null" + } + ] + } + }, + "description": "Timeout settings in seconds for connecting to, sending data to, and reading data from the upstream.", + "examples": [ + { + "connect": 6, + "send": 6, + "read": 6 + } + ] + }, + { + "type": "null" + } + ] + }, + "checks": { + "anyOf": [ + { + "type": "object", + "properties": { + "passive": { + "anyOf": [ + { + "properties": { + "unhealthy": { + "anyOf": [ + { + "properties": { + "http_failures": { + "anyOf": [ + { + "minimum": 0, + "type": "integer", + "default": 5, + "maximum": 254, + "description": "Number of HTTP failures from real traffic to consider a target unhealthy." + }, + { + "type": "null" + } + ] + }, + "timeouts": { + "anyOf": [ + { + "minimum": 0, + "type": "integer", + "default": 7, + "maximum": 254, + "description": "Number of timeouts from real traffic to consider a target unhealthy." + }, + { + "type": "null" + } + ] + }, + "http_statuses": { + "anyOf": [ + { + "items": { + "minimum": 200, + "type": "integer", + "maximum": 599 + }, + "type": "array", + "default": [ + 429, + 500, + 503 + ], + "minItems": 1, + "uniqueItems": true, + "description": "HTTP status codes from real traffic that indicate an unhealthy target." + }, + { + "type": "null" + } + ] + }, + "tcp_failures": { + "anyOf": [ + { + "minimum": 0, + "type": "integer", + "default": 2, + "maximum": 254, + "description": "Number of TCP failures from real traffic to consider a target unhealthy." + }, + { + "type": "null" + } + ] + } + }, + "type": "object", + "description": "Thresholds for considering a target unhealthy based on real traffic responses." + }, + { + "type": "null" + } + ] + }, + "type": { + "anyOf": [ + { + "type": "string", + "default": "http", + "enum": [ + "http", + "https", + "tcp" + ], + "description": "Protocol type for passive health monitoring." + }, + { + "type": "null" + } + ] + }, + "healthy": { + "anyOf": [ + { + "properties": { + "http_statuses": { + "anyOf": [ + { + "items": { + "minimum": 200, + "type": "integer", + "maximum": 599 + }, + "type": "array", + "default": [ + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 226, + 300, + 301, + 302, + 303, + 304, + 305, + 306, + 307, + 308 + ], + "minItems": 1, + "uniqueItems": true, + "description": "HTTP status codes from real traffic that indicate a healthy target." + }, + { + "type": "null" + } + ] + }, + "successes": { + "anyOf": [ + { + "minimum": 0, + "type": "integer", + "default": 5, + "maximum": 254, + "description": "Number of consecutive successful requests to restore a target to healthy status." + }, + { + "type": "null" + } + ] + } + }, + "type": "object", + "description": "Thresholds for considering a target healthy based on real traffic responses." + }, + { + "type": "null" + } + ] + } + }, + "type": "object", + "description": "Passive health check configuration. APISIX monitors real traffic responses to determine upstream node health — no extra probes are sent." + }, + { + "type": "null" + } + ] + }, + "active": { + "anyOf": [ + { + "properties": { + "type": { + "anyOf": [ + { + "type": "string", + "default": "http", + "enum": [ + "http", + "https", + "tcp" + ], + "description": "Protocol type for health check probes." + }, + { + "type": "null" + } + ] + }, + "timeout": { + "anyOf": [ + { + "default": 1, + "type": "number", + "description": "Timeout in seconds for each health check probe." + }, + { + "type": "null" + } + ] + }, + "req_headers": { + "anyOf": [ + { + "items": { + "uniqueItems": true, + "type": "string" + }, + "type": "array", + "minItems": 1, + "description": "Additional HTTP headers to include in health check probe requests." + }, + { + "type": "null" + } + ] + }, + "http_path": { + "anyOf": [ + { + "default": "/", + "type": "string", + "description": "HTTP request path for health check probes." + }, + { + "type": "null" + } + ] + }, + "concurrency": { + "anyOf": [ + { + "default": 10, + "type": "integer", + "description": "Number of targets to check concurrently during active health checks." + }, + { + "type": "null" + } + ] + }, + "host": { + "anyOf": [ + { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string", + "description": "Hostname to use in the health check probe `Host` header. Defaults to the upstream node's host." + }, + { + "type": "null" + } + ] + }, + "port": { + "anyOf": [ + { + "minimum": 1, + "type": "integer", + "maximum": 65535, + "description": "Port to use for health check probes. Defaults to the upstream node's port." + }, + { + "type": "null" + } + ] + }, + "unhealthy": { + "anyOf": [ + { + "properties": { + "timeouts": { + "anyOf": [ + { + "minimum": 1, + "type": "integer", + "default": 3, + "maximum": 254, + "description": "Number of probe timeouts to consider a target unhealthy." + }, + { + "type": "null" + } + ] + }, + "http_failures": { + "anyOf": [ + { + "minimum": 1, + "type": "integer", + "default": 5, + "maximum": 254, + "description": "Number of HTTP failures to consider a target unhealthy." + }, + { + "type": "null" + } + ] + }, + "interval": { + "anyOf": [ + { + "minimum": 1, + "type": "integer", + "default": 1, + "description": "Interval in seconds between active health check probes for unhealthy targets." + }, + { + "type": "null" + } + ] + }, + "http_statuses": { + "anyOf": [ + { + "items": { + "minimum": 200, + "type": "integer", + "maximum": 599 + }, + "type": "array", + "default": [ + 429, + 404, + 500, + 501, + 502, + 503, + 504, + 505 + ], + "minItems": 1, + "uniqueItems": true, + "description": "HTTP status codes that indicate an unhealthy target." + }, + { + "type": "null" + } + ] + }, + "tcp_failures": { + "anyOf": [ + { + "minimum": 1, + "type": "integer", + "default": 2, + "maximum": 254, + "description": "Number of TCP connection failures to consider a target unhealthy." + }, + { + "type": "null" + } + ] + } + }, + "type": "object", + "description": "Thresholds for considering a target unhealthy during active checks." + }, + { + "type": "null" + } + ] + }, + "healthy": { + "anyOf": [ + { + "properties": { + "interval": { + "anyOf": [ + { + "minimum": 1, + "type": "integer", + "default": 1, + "description": "Interval in seconds between active health check probes for healthy targets." + }, + { + "type": "null" + } + ] + }, + "http_statuses": { + "anyOf": [ + { + "items": { + "minimum": 200, + "type": "integer", + "maximum": 599 + }, + "type": "array", + "default": [ + 200, + 302 + ], + "minItems": 1, + "uniqueItems": true, + "description": "HTTP status codes that indicate a healthy target." + }, + { + "type": "null" + } + ] + }, + "successes": { + "anyOf": [ + { + "minimum": 1, + "type": "integer", + "default": 2, + "maximum": 254, + "description": "Number of consecutive successful probes to consider a target healthy." + }, + { + "type": "null" + } + ] + } + }, + "type": "object", + "description": "Thresholds for considering a target healthy during active checks." + }, + { + "type": "null" + } + ] + }, + "https_verify_certificate": { + "anyOf": [ + { + "default": true, + "type": "boolean", + "description": "Whether to verify the upstream's TLS certificate during HTTPS health checks." + }, + { + "type": "null" + } + ] + } + }, + "type": "object", + "description": "Active health check configuration. APISIX periodically sends probes to upstream nodes to determine their health status." + }, + { + "type": "null" + } + ] + } + }, + "description": "Health check configuration for monitoring upstream node availability. Active checks require at least the `active` field." + }, + { + "type": "null" + } + ] + }, + "id": { + "anyOf": [ + { + "anyOf": [ + { + "maxLength": 64, + "type": "string", + "pattern": "^[a-zA-Z0-9-_.]+$", + "minLength": 1 + }, + { + "minimum": 1, + "type": "integer" + } + ], + "description": "Unique identifier for the resource. Can be a string (1–64 characters, alphanumeric with `-`, `_`, `.`) or a positive integer." + }, + { + "type": "null" + } + ] + }, + "upstream_host": { + "anyOf": [ + { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string", + "description": "Custom `Host` header value. Only effective when `pass_host` is `rewrite`." + }, + { + "type": "null" + } + ] + }, + "discovery_type": { + "anyOf": [ + { + "description": "Service discovery type (e.g., `dns`, `consul`, `nacos`, `eureka`). Required when using service discovery instead of static `nodes`.", + "type": "string" + }, + { + "type": "null" + } + ] + }, + "desc": { + "anyOf": [ + { + "maxLength": 256, + "type": "string", + "description": "Description of the upstream." + }, + { + "type": "null" + } + ] + }, + "scheme": { + "anyOf": [ + { + "enum": [ + "grpc", + "grpcs", + "http", + "https", + "tcp", + "tls", + "udp", + "kafka" + ], + "default": "http", + "description": "Protocol for communicating with upstream nodes." + }, + { + "type": "null" + } + ] + }, + "name": { + "anyOf": [ + { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Human-readable name for the upstream." + }, + { + "type": "null" + } + ] + }, + "hash_on": { + "anyOf": [ + { + "type": "string", + "default": "vars", + "enum": [ + "vars", + "header", + "cookie", + "consumer", + "vars_combinations" + ], + "description": "What to hash on when `type` is `chash`." + }, + { + "type": "null" + } + ] + }, + "nodes": { + "anyOf": [ + { + "anyOf": [ + { + "patternProperties": { + ".*": { + "anyOf": [ + { + "minimum": 0, + "type": "integer", + "description": "weight of node" + }, + { + "type": "null" + } + ] + } + }, + "type": "object" + }, + { + "items": { + "$ref": "#/components/schemas/UpstreamNodeItem" + }, + "type": "array" + } + ], + "description": "The upstream endpoints." + }, + { + "type": "null" + } + ] + }, + "keepalive_pool": { + "anyOf": [ + { + "properties": { + "idle_timeout": { + "anyOf": [ + { + "minimum": 0, + "type": "number", + "default": 60, + "description": "Time in seconds after which an idle keepalive connection is closed. `0` disables the timeout." + }, + { + "type": "null" + } + ] + }, + "requests": { + "anyOf": [ + { + "minimum": 1, + "type": "integer", + "default": 1000, + "description": "Maximum number of requests that can be sent over a single keepalive connection before it is closed." + }, + { + "type": "null" + } + ] + }, + "size": { + "anyOf": [ + { + "minimum": 1, + "type": "integer", + "default": 320, + "description": "Maximum number of keepalive connections cached in the connection pool." + }, + { + "type": "null" + } + ] + } + }, + "type": "object", + "description": "Connection pool configuration for keepalive connections to upstream nodes." + }, + { + "type": "null" + } + ] + }, + "retry_timeout": { + "anyOf": [ + { + "minimum": 0, + "type": "number", + "description": "Maximum time in seconds for retry attempts. `0` disables the retry timeout." + }, + { + "type": "null" + } + ] + }, + "labels": { + "anyOf": [ + { + "type": "object", + "description": "Key-value pairs for categorizing and filtering resources. Values must be non-empty strings (max 256 characters).", + "patternProperties": { + ".*": { + "anyOf": [ + { + "type": "string", + "description": "value of label", + "minLength": 1, + "pattern": "^\\S+$", + "maxLength": 256 + }, + { + "type": "null" + } + ] + } + }, + "examples": [ + { + "env": "production", + "team": "platform", + "version": "v2" + } + ] + }, + { + "type": "null" + } + ] + } + }, + "description": "Partial object merged recursively into the stored resource. Null removes a field. Arrays replace existing arrays. The complete resulting resource is validated." + }, + "GlobalRulePatch": { + "additionalProperties": false, + "type": "object", + "properties": { + "id": { + "anyOf": [ + { + "anyOf": [ + { + "maxLength": 64, + "type": "string", + "pattern": "^[a-zA-Z0-9-_.]+$", + "minLength": 1 + }, + { + "minimum": 1, + "type": "integer" + } + ], + "description": "Unique identifier for the resource. Can be a string (1–64 characters, alphanumeric with `-`, `_`, `.`) or a positive integer." + }, + { + "type": "null" + } + ] + }, + "plugins": { + "anyOf": [ + { + "type": "object", + "description": "Plugin configurations as a JSON object. Each key is a plugin name, and the value is that plugin's configuration object.\n\nExample:\n```json\n{\n \"limit-count\": {\n \"count\": 100,\n \"time_window\": 60\n },\n \"key-auth\": {}\n}\n```\n\nSee the [Plugin Hub](https://apisix.apache.org/plugins/) for available plugins and their schemas.", + "examples": [ + { + "limit-count": { + "count": 100, + "time_window": 60, + "rejected_code": 429, + "key_type": "var", + "key": "remote_addr" + } + } + ] + }, + { + "type": "null" + } + ] + } + }, + "description": "Partial object merged recursively into the stored resource. Null removes a field. Arrays replace existing arrays. The complete resulting resource is validated." + }, + "ConsumerGroupPatch": { + "additionalProperties": false, + "type": "object", + "properties": { + "name": { + "anyOf": [ + { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Human-readable name." + }, + { + "type": "null" + } + ] + }, + "plugins": { + "anyOf": [ + { + "type": "object", + "description": "Plugin configurations as a JSON object. Each key is a plugin name, and the value is that plugin's configuration object.\n\nExample:\n```json\n{\n \"limit-count\": {\n \"count\": 100,\n \"time_window\": 60\n },\n \"key-auth\": {}\n}\n```\n\nSee the [Plugin Hub](https://apisix.apache.org/plugins/) for available plugins and their schemas.", + "examples": [ + { + "limit-count": { + "count": 100, + "time_window": 60, + "rejected_code": 429, + "key_type": "var", + "key": "remote_addr" + } + } + ] + }, + { + "type": "null" + } + ] + }, + "id": { + "anyOf": [ + { + "anyOf": [ + { + "maxLength": 64, + "type": "string", + "pattern": "^[a-zA-Z0-9-_.]+$", + "minLength": 1 + }, + { + "minimum": 1, + "type": "integer" + } + ], + "description": "Unique identifier for the resource. Can be a string (1–64 characters, alphanumeric with `-`, `_`, `.`) or a positive integer." + }, + { + "type": "null" + } + ] + }, + "labels": { + "anyOf": [ + { + "type": "object", + "description": "Key-value pairs for categorizing and filtering resources. Values must be non-empty strings (max 256 characters).", + "patternProperties": { + ".*": { + "anyOf": [ + { + "type": "string", + "description": "value of label", + "minLength": 1, + "pattern": "^\\S+$", + "maxLength": 256 + }, + { + "type": "null" + } + ] + } + }, + "examples": [ + { + "env": "production", + "team": "platform", + "version": "v2" + } + ] + }, + { + "type": "null" + } + ] + }, + "desc": { + "anyOf": [ + { + "maxLength": 256, + "type": "string", + "description": "Description of this group." + }, + { + "type": "null" + } + ] + } + }, + "description": "Partial object merged recursively into the stored resource. Null removes a field. Arrays replace existing arrays. The complete resulting resource is validated." + }, + "PluginConfigPatch": { + "additionalProperties": false, + "type": "object", + "properties": { + "name": { + "anyOf": [ + { + "type": "string", + "description": "Human-readable name." + }, + { + "type": "null" + } + ] + }, + "plugins": { + "anyOf": [ + { + "type": "object", + "description": "Plugin configurations as a JSON object. Each key is a plugin name, and the value is that plugin's configuration object.\n\nExample:\n```json\n{\n \"limit-count\": {\n \"count\": 100,\n \"time_window\": 60\n },\n \"key-auth\": {}\n}\n```\n\nSee the [Plugin Hub](https://apisix.apache.org/plugins/) for available plugins and their schemas.", + "examples": [ + { + "limit-count": { + "count": 100, + "time_window": 60, + "rejected_code": 429, + "key_type": "var", + "key": "remote_addr" + } + } + ] + }, + { + "type": "null" + } + ] + }, + "id": { + "anyOf": [ + { + "anyOf": [ + { + "maxLength": 64, + "type": "string", + "pattern": "^[a-zA-Z0-9-_.]+$", + "minLength": 1 + }, + { + "minimum": 1, + "type": "integer" + } + ], + "description": "Unique identifier for the resource. Can be a string (1–64 characters, alphanumeric with `-`, `_`, `.`) or a positive integer." + }, + { + "type": "null" + } + ] + }, + "labels": { + "anyOf": [ + { + "type": "object", + "description": "Key-value pairs for categorizing and filtering resources. Values must be non-empty strings (max 256 characters).", + "patternProperties": { + ".*": { + "anyOf": [ + { + "type": "string", + "description": "value of label", + "minLength": 1, + "pattern": "^\\S+$", + "maxLength": 256 + }, + { + "type": "null" + } + ] + } + }, + "examples": [ + { + "env": "production", + "team": "platform", + "version": "v2" + } + ] + }, + { + "type": "null" + } + ] + }, + "desc": { + "anyOf": [ + { + "maxLength": 256, + "type": "string", + "description": "Description." + }, + { + "type": "null" + } + ] + } + }, + "description": "Partial object merged recursively into the stored resource. Null removes a field. Arrays replace existing arrays. The complete resulting resource is validated." + }, + "VaultSecret": { + "type": "object", + "required": [ + "uri", + "prefix", + "token" + ], + "properties": { + "namespace": { + "type": "string", + "description": "Vault Enterprise or HCP Vault namespace." + }, + "token": { + "type": "string", + "description": "Vault authentication token or environment-variable URI." + }, + "uri": { + "pattern": "^[^\\/]+:\\/\\/([\\da-zA-Z.-]+|\\[[\\da-fA-F:]+\\])(:\\d+)?", + "type": "string", + "description": "Vault server URI." + }, + "prefix": { + "type": "string", + "description": "Path prefix for secrets in Vault." + } + }, + "description": "HashiCorp Vault secret-manager configuration." + }, + "AwsSecret": { + "type": "object", + "required": [ + "access_key_id", + "secret_access_key" + ], + "properties": { + "session_token": { + "type": "string", + "description": "Optional AWS session token or environment-variable URI." + }, + "endpoint_url": { + "pattern": "^[^\\/]+:\\/\\/([\\da-zA-Z.-]+|\\[[\\da-fA-F:]+\\])(:\\d+)?", + "type": "string", + "description": "Custom AWS Secrets Manager endpoint." + }, + "region": { + "default": "us-east-1", + "type": "string", + "description": "AWS region." + }, + "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." + } + }, + "description": "AWS Secrets Manager configuration." + }, + "GcpSecret": { + "oneOf": [ + { + "required": [ + "auth_config" + ] + }, + { + "required": [ + "auth_file" + ] + } + ], + "type": "object", + "properties": { + "ssl_verify": { + "default": true, + "type": "boolean" + }, + "auth_config": { + "type": "object", + "required": [ + "client_email", + "private_key", + "project_id" + ], + "properties": { + "client_email": { + "type": "string" + }, + "entries_uri": { + "default": "https://secretmanager.googleapis.com/v1", + "type": "string" + }, + "scope": { + "items": { + "type": "string" + }, + "type": "array", + "default": [ + "https://www.googleapis.com/auth/cloud-platform" + ] + }, + "project_id": { + "type": "string" + }, + "token_uri": { + "default": "https://oauth2.googleapis.com/token", + "type": "string" + }, + "private_key": { + "type": "string" + } + } + }, + "auth_file": { + "type": "string", + "description": "Path to a Google service-account JSON file." + } + }, + "description": "Google Cloud Secret Manager configuration." + }, + "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", + "additionalProperties": false, + "properties": { + "type": { + "anyOf": [ + { + "enum": [ + "server", + "client" + ], + "type": "string", + "description": "Certificate type.", + "default": "server" + }, + { + "type": "null" + } + ] + }, + "snis": { + "anyOf": [ + { + "items": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "type": "array", + "minItems": 1, + "description": "Multiple SNI hostnames. Mutually exclusive with `sni`." + }, + { + "type": "null" + } + ] + }, + "id": { + "anyOf": [ + { + "anyOf": [ + { + "maxLength": 64, + "type": "string", + "pattern": "^[a-zA-Z0-9-_.]+$", + "minLength": 1 + }, + { + "minimum": 1, + "type": "integer" + } + ], + "description": "Unique identifier for the resource. Can be a string (1–64 characters, alphanumeric with `-`, `_`, `.`) or a positive integer." + }, + { + "type": "null" + } + ] + }, + "status": { + "anyOf": [ + { + "default": 1, + "type": "integer", + "description": "SSL status.", + "enum": [ + 1, + 0 + ] + }, + { + "type": "null" + } + ] + }, + "cert": { + "anyOf": [ + { + "type": "string", + "maxLength": 65536, + "minLength": 128, + "description": "PEM-encoded server certificate. Can also be a secret reference (`$secret://...`)." + }, + { + "type": "null" + } + ] + }, + "desc": { + "anyOf": [ + { + "maxLength": 256, + "type": "string", + "description": "Description." + }, + { + "type": "null" + } + ] + }, + "client": { + "anyOf": [ + { + "type": "object", + "properties": { + "ca": { + "anyOf": [ + { + "type": "string", + "maxLength": 65536, + "minLength": 128, + "description": "PEM-encoded CA certificate for client verification." + }, + { + "type": "null" + } + ] + }, + "depth": { + "anyOf": [ + { + "minimum": 0, + "type": "integer", + "default": 1, + "description": "Maximum certificate chain depth." + }, + { + "type": "null" + } + ] + }, + "skip_mtls_uri_regex": { + "anyOf": [ + { + "items": { + "description": "uri regular expression to skip mtls", + "type": "string" + }, + "type": "array", + "minItems": 1, + "uniqueItems": true, + "description": "URI regex patterns to skip mTLS verification." + }, + { + "type": "null" + } + ] + } + }, + "description": "mTLS client verification settings." + }, + { + "type": "null" + } + ] + }, + "key": { + "anyOf": [ + { + "type": "string", + "maxLength": 65536, + "minLength": 64, + "description": "PEM-encoded private key. Can also be a secret reference (`$secret://...`). **Not returned in GET responses for security.**" + }, + { + "type": "null" + } + ] + }, + "ssl_protocols": { + "anyOf": [ + { + "items": { + "enum": [ + "TLSv1.1", + "TLSv1.2", + "TLSv1.3" + ] + }, + "type": "array", + "description": "Allowed TLS protocol versions.", + "maxItems": 3, + "uniqueItems": true + }, + { + "type": "null" + } + ] + }, + "keys": { + "anyOf": [ + { + "items": { + "type": "string", + "maxLength": 65536, + "minLength": 64 + }, + "type": "array", + "description": "Additional private keys corresponding to `certs`." + }, + { + "type": "null" + } + ] + }, + "certs": { + "anyOf": [ + { + "items": { + "type": "string", + "maxLength": 65536, + "minLength": 128 + }, + "type": "array", + "description": "Additional certificates for certificate chain or alternative key types." + }, + { + "type": "null" + } + ] + }, + "sni": { + "anyOf": [ + { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string", + "description": "Server Name Indication (SNI) to match. Supports wildcard prefix (`*.example.com`). Mutually exclusive with `snis`.", + "examples": [ + "api.example.com" + ] + }, + { + "type": "null" + } + ] + }, + "labels": { + "anyOf": [ + { + "type": "object", + "description": "Key-value pairs for categorizing and filtering resources. Values must be non-empty strings (max 256 characters).", + "patternProperties": { + ".*": { + "anyOf": [ + { + "type": "string", + "description": "value of label", + "minLength": 1, + "pattern": "^\\S+$", + "maxLength": 256 + }, + { + "type": "null" + } + ] + } + }, + "examples": [ + { + "env": "production", + "team": "platform", + "version": "v2" + } + ] + }, + { + "type": "null" + } + ] + } + }, + "description": "Partial object merged recursively into the stored resource. Null removes a field. Arrays replace existing arrays. The complete resulting resource is validated." + }, + "SecretPatch": { + "type": "object", + "description": "Partial backend-specific secret configuration; null removes a field. The complete merged configuration is validated by the selected secret backend." + }, + "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": { + "type": "object", + "additionalProperties": false, + "properties": { + "create_time": { + "type": "integer", + "description": "Unix timestamp of creation. Read-only." + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Human-readable name." + }, + "plugins": { + "maxProperties": 1, + "type": "object", + "description": "At most one authentication plugin configuration (for example, `key-auth`, `basic-auth`, or `jwt-auth`)." + }, + "id": { + "type": "string", + "minLength": 15, + "maxLength": 128, + "pattern": "^[a-zA-Z0-9-_]+/credentials/[a-zA-Z0-9-_.]+$" + }, + "update_time": { + "type": "integer", + "description": "Unix timestamp of last update. Read-only." + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description of this credential." + }, + "modifiedIndex": { + "type": "integer", + "description": "Accepted only with the YAML config provider; see standalone.lua." + } + }, + "description": "Declarative resource. modifiedIndex and composite credential IDs require the YAML config provider; etcd-mode validation uses the unpatched core schema." + }, + "ConfigValidationRoute": { + "type": "object", + "additionalProperties": false, + "not": { + "anyOf": [ + { + "required": [ + "script", + "plugins" + ] + }, + { + "required": [ + "script", + "plugin_config_id" + ] + } + ] + }, + "properties": { + "service_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "update_time": { + "type": "integer", + "description": "Unix timestamp of last update. Read-only." + }, + "uris": { + "items": { + "description": "HTTP uri", + "type": "string" + }, + "type": "array", + "minItems": 1, + "uniqueItems": true, + "description": "Multiple URI patterns. Mutually exclusive with `uri`." + }, + "hosts": { + "items": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "type": "array", + "minItems": 1, + "uniqueItems": true, + "description": "Multiple hostnames to match. Mutually exclusive with `host`." + }, + "remote_addrs": { + "items": { + "type": "string", + "description": "client IP", + "anyOf": [ + { + "type": "string", + "title": "IPv4", + "format": "ipv4" + }, + { + "type": "string", + "title": "IPv4/CIDR", + "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": "IPv6", + "format": "ipv6" + }, + { + "type": "string", + "title": "IPv6/CIDR", + "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": "array", + "minItems": 1, + "uniqueItems": true, + "description": "Multiple client IP addresses or CIDR ranges to match. Mutually exclusive with `remote_addr`." + }, + "host": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string", + "description": "Single hostname to match against the request's `Host` header. Supports wildcard prefix (`*.example.com`). Mutually exclusive with `hosts`.", + "examples": [ + "api.example.com" + ] + }, + "remote_addr": { + "type": "string", + "description": "Single client IP address or CIDR range to match. Mutually exclusive with `remote_addrs`.", + "anyOf": [ + { + "type": "string", + "title": "IPv4", + "format": "ipv4" + }, + { + "type": "string", + "title": "IPv4/CIDR", + "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": "IPv6", + "format": "ipv6" + }, + { + "type": "string", + "title": "IPv6/CIDR", + "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}$" + } + ], + "examples": [ + "192.168.1.0/24" + ] + }, + "script_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "upstream_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "plugins": { + "$ref": "#/components/schemas/Plugins" + }, + "priority": { + "default": 0, + "type": "integer", + "description": "Route priority. When multiple routes match the same request, the one with the highest priority wins." + }, + "uri": { + "type": "string", + "maxLength": 4096, + "minLength": 1, + "description": "URI path pattern to match. Supports exact match (`/api/v1/users`) and prefix match (`/api/*`). Mutually exclusive with `uris`.", + "examples": [ + "/api/v1/users/*" + ] + }, + "methods": { + "items": { + "type": "string", + "description": "HTTP method", + "enum": [ + "GET", + "POST", + "PUT", + "DELETE", + "PATCH", + "HEAD", + "OPTIONS", + "CONNECT", + "TRACE", + "PURGE" + ] + }, + "type": "array", + "uniqueItems": true, + "description": "HTTP methods allowed on this route. If empty or not set, all methods are allowed.", + "examples": [ + [ + "GET", + "POST" + ] + ] + }, + "timeout": { + "$ref": "#/components/schemas/Timeout" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "status": { + "default": 1, + "type": "integer", + "description": "Route status.", + "enum": [ + 1, + 0 + ] + }, + "enable_websocket": { + "description": "Enable WebSocket proxying on this route.", + "type": "boolean" + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description of what this route does." + }, + "upstream": { + "$ref": "#/components/schemas/Upstream" + }, + "create_time": { + "type": "integer", + "description": "Unix timestamp of creation. Read-only." + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Human-readable name for the route." + }, + "plugin_config_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "script": { + "type": "string", + "maxLength": 102400, + "minLength": 10, + "description": "Lua script for plugin orchestration. Cannot be used together with `plugins` or `plugin_config_id`." + }, + "filter_func": { + "type": "string", + "pattern": "^function", + "minLength": 10, + "description": "Custom Lua function for advanced request matching. Must start with `function`." + }, + "vars": { + "type": "array", + "description": "Custom matching conditions using [APISIX expressions](https://apisix.apache.org/docs/apisix/3.17/router-radixtree/#how-to-filter-route-by-nginx-built-in-variable). Each condition is an array of `[variable, operator, value]`.", + "examples": [ + [ + [ + "http_user_agent", + "~*", + "curl" + ] + ] + ] + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "modifiedIndex": { + "type": "integer", + "description": "Accepted only with the YAML config provider; see standalone.lua." + } + }, + "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" + ] + } + ] + } + ], + "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" + ] + } + ], + "description": "Declarative resource. modifiedIndex and composite credential IDs require the YAML config provider; etcd-mode validation uses the unpatched core schema." + }, + "ConfigValidationService": { + "type": "object", + "additionalProperties": false, + "properties": { + "update_time": { + "type": "integer", + "description": "Unix timestamp of last update. Read-only." + }, + "upstream": { + "$ref": "#/components/schemas/Upstream" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "hosts": { + "items": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "type": "array", + "minItems": 1, + "uniqueItems": true, + "description": "Hostnames associated with this service." + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description of this service." + }, + "create_time": { + "type": "integer", + "description": "Unix timestamp of creation. Read-only." + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Human-readable name." + }, + "upstream_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "plugins": { + "$ref": "#/components/schemas/Plugins" + }, + "enable_websocket": { + "description": "Enable WebSocket proxying for routes using this service.", + "type": "boolean" + }, + "script": { + "type": "string", + "maxLength": 102400, + "minLength": 10, + "description": "Lua script for plugin orchestration." + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "modifiedIndex": { + "type": "integer", + "description": "Accepted only with the YAML config provider; see standalone.lua." + } + }, + "description": "Declarative resource. modifiedIndex and composite credential IDs require the YAML config provider; etcd-mode validation uses the unpatched core schema." + }, + "ConfigValidationUpstream": { + "oneOf": [ + { + "required": [ + "nodes" + ] + }, + { + "required": [ + "service_name", + "discovery_type" + ] + } + ], + "type": "object", + "additionalProperties": false, + "properties": { + "tls": { + "$ref": "#/components/schemas/UpstreamTLS" + }, + "update_time": { + "type": "integer", + "description": "Unix timestamp when the upstream was last updated. Read-only, set automatically." + }, + "retries": { + "minimum": 0, + "type": "integer", + "description": "Number of retry attempts when a request to the upstream fails." + }, + "key": { + "description": "The hash key used when `type` is `chash`. Required when `hash_on` is `header`, `cookie`, `vars`, or `vars_combinations`. Not needed when `hash_on` is `consumer`.", + "type": "string" + }, + "pass_host": { + "default": "pass", + "type": "string", + "description": "How to set the `Host` header when proxying to the upstream.", + "enum": [ + "pass", + "node", + "rewrite" + ] + }, + "service_name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Service name for service discovery. Required when `discovery_type` is set." + }, + "discovery_args": { + "properties": { + "namespace_id": { + "description": "Namespace ID for Nacos service discovery.", + "type": "string" + }, + "group_name": { + "description": "Group name for service discovery.", + "type": "string" + } + }, + "type": "object", + "description": "Additional arguments for service discovery." + }, + "type": { + "type": "string", + "description": "Load balancing algorithm.", + "default": "roundrobin" + }, + "timeout": { + "$ref": "#/components/schemas/Timeout" + }, + "checks": { + "$ref": "#/components/schemas/HealthCheck" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "upstream_host": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string", + "description": "Custom `Host` header value. Only effective when `pass_host` is `rewrite`." + }, + "discovery_type": { + "description": "Service discovery type (e.g., `dns`, `consul`, `nacos`, `eureka`). Required when using service discovery instead of static `nodes`.", + "type": "string" + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description of the upstream." + }, + "scheme": { + "enum": [ + "grpc", + "grpcs", + "http", + "https", + "tcp", + "tls", + "udp", + "kafka" + ], + "default": "http", + "description": "Protocol for communicating with upstream nodes." + }, + "create_time": { + "type": "integer", + "description": "Unix timestamp when the upstream was created. Read-only, set automatically." + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Human-readable name for the upstream." + }, + "hash_on": { + "type": "string", + "default": "vars", + "enum": [ + "vars", + "header", + "cookie", + "consumer", + "vars_combinations" + ], + "description": "What to hash on when `type` is `chash`." + }, + "nodes": { + "$ref": "#/components/schemas/UpstreamNodes" + }, + "keepalive_pool": { + "$ref": "#/components/schemas/KeepalivePool" + }, + "retry_timeout": { + "minimum": 0, + "type": "number", + "description": "Maximum time in seconds for retry attempts. `0` disables the retry timeout." + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "modifiedIndex": { + "type": "integer", + "description": "Accepted only with the YAML config provider; see standalone.lua." + } + }, + "description": "Declarative resource. modifiedIndex and composite credential IDs require the YAML config provider; etcd-mode validation uses the unpatched core schema." + }, + "ConfigValidationConsumer": { + "additionalProperties": false, + "type": "object", + "required": [ + "username" + ], + "properties": { + "username": { + "maxLength": 256, + "type": "string", + "pattern": "^[a-zA-Z0-9_\\-]+$", + "minLength": 1, + "description": "Unique username for the consumer. Only alphanumeric characters, hyphens, and underscores are allowed.", + "examples": [ + "my-app" + ] + }, + "create_time": { + "type": "integer", + "description": "Unix timestamp of creation. Read-only." + }, + "update_time": { + "type": "integer", + "description": "Unix timestamp of last update. Read-only." + }, + "plugins": { + "$ref": "#/components/schemas/Plugins" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "group_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description of this consumer." + }, + "modifiedIndex": { + "type": "integer", + "description": "Accepted only with the YAML config provider; see standalone.lua." + } + }, + "description": "Declarative resource. modifiedIndex and composite credential IDs require the YAML config provider; etcd-mode validation uses the unpatched core schema." + }, + "ConfigValidationSSL": { + "type": "object", + "additionalProperties": false, + "then": { + "oneOf": [ + { + "required": [ + "sni", + "key", + "cert" + ] + }, + { + "required": [ + "snis", + "key", + "cert" + ] + } + ] + }, + "properties": { + "type": { + "enum": [ + "server", + "client" + ], + "type": "string", + "description": "Certificate type.", + "default": "server" + }, + "update_time": { + "type": "integer", + "description": "Unix timestamp of last update. Read-only." + }, + "snis": { + "items": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "type": "array", + "minItems": 1, + "description": "Multiple SNI hostnames. Mutually exclusive with `sni`." + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "status": { + "default": 1, + "type": "integer", + "description": "SSL status.", + "enum": [ + 1, + 0 + ] + }, + "cert": { + "type": "string", + "maxLength": 65536, + "minLength": 128, + "description": "PEM-encoded server certificate. Can also be a secret reference (`$secret://...`)." + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description." + }, + "client": { + "type": "object", + "required": [ + "ca" + ], + "properties": { + "ca": { + "type": "string", + "maxLength": 65536, + "minLength": 128, + "description": "PEM-encoded CA certificate for client verification." + }, + "depth": { + "minimum": 0, + "type": "integer", + "default": 1, + "description": "Maximum certificate chain depth." + }, + "skip_mtls_uri_regex": { + "items": { + "description": "uri regular expression to skip mtls", + "type": "string" + }, + "type": "array", + "minItems": 1, + "uniqueItems": true, + "description": "URI regex patterns to skip mTLS verification." + } + }, + "description": "mTLS client verification settings." + }, + "create_time": { + "type": "integer", + "description": "Unix timestamp of creation. Read-only." + }, + "key": { + "type": "string", + "maxLength": 65536, + "minLength": 64, + "description": "PEM-encoded private key. Can also be a secret reference (`$secret://...`). **Not returned in GET responses for security.**" + }, + "ssl_protocols": { + "items": { + "enum": [ + "TLSv1.1", + "TLSv1.2", + "TLSv1.3" + ] + }, + "type": "array", + "description": "Allowed TLS protocol versions.", + "maxItems": 3, + "uniqueItems": true + }, + "keys": { + "items": { + "type": "string", + "maxLength": 65536, + "minLength": 64 + }, + "type": "array", + "description": "Additional private keys corresponding to `certs`." + }, + "certs": { + "items": { + "type": "string", + "maxLength": 65536, + "minLength": 128 + }, + "type": "array", + "description": "Additional certificates for certificate chain or alternative key types." + }, + "sni": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string", + "description": "Server Name Indication (SNI) to match. Supports wildcard prefix (`*.example.com`). Mutually exclusive with `snis`.", + "examples": [ + "api.example.com" + ] + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "modifiedIndex": { + "type": "integer", + "description": "Accepted only with the YAML config provider; see standalone.lua." + } + }, + "if": { + "properties": { + "type": { + "enum": [ + "server" + ] + } + } + }, + "else": { + "required": [ + "key", + "cert" + ], + "title": "SNI not required" + }, + "description": "Declarative resource. modifiedIndex and composite credential IDs require the YAML config provider; etcd-mode validation uses the unpatched core schema." + }, + "ConfigValidationStreamRoute": { + "type": "object", + "additionalProperties": false, + "properties": { + "service_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "update_time": { + "type": "integer", + "description": "Unix timestamp of last update. Read-only." + }, + "upstream": { + "$ref": "#/components/schemas/Upstream" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "protocol": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "logger": { + "items": { + "properties": { + "conf": { + "$ref": "#/components/schemas/Plugins" + }, + "name": { + "type": "string" + }, + "filter": { + "description": "logger filter rules", + "type": "array" + } + }, + "additionalProperties": false, + "dependentRequired": { + "name": [ + "conf" + ] + } + }, + "type": "array" + }, + "superior_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "name": { + "type": "string" + }, + "conf": { + "$ref": "#/components/schemas/Plugins" + } + } + }, + "server_port": { + "minimum": 1, + "type": "integer", + "description": "Server port to match.", + "maximum": 65535 + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description." + }, + "upstream_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "create_time": { + "type": "integer", + "description": "Unix timestamp of creation. Read-only." + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Human-readable name." + }, + "server_addr": { + "type": "string", + "description": "Server IP address to match.", + "anyOf": [ + { + "type": "string", + "title": "IPv4", + "format": "ipv4" + }, + { + "type": "string", + "title": "IPv4/CIDR", + "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": "IPv6", + "format": "ipv6" + }, + { + "type": "string", + "title": "IPv6/CIDR", + "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}$" + } + ] + }, + "plugins": { + "$ref": "#/components/schemas/Plugins" + }, + "sni": { + "type": "string", + "description": "SNI hostname to match (for TLS connections).", + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$" + }, + "remote_addr": { + "type": "string", + "description": "Client IP address or CIDR range to match.", + "anyOf": [ + { + "type": "string", + "title": "IPv4", + "format": "ipv4" + }, + { + "type": "string", + "title": "IPv4/CIDR", + "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": "IPv6", + "format": "ipv6" + }, + { + "type": "string", + "title": "IPv6/CIDR", + "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}$" + } + ] + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "modifiedIndex": { + "type": "integer", + "description": "Accepted only with the YAML config provider; see standalone.lua." + } + }, + "description": "Declarative resource. modifiedIndex and composite credential IDs require the YAML config provider; etcd-mode validation uses the unpatched core schema." + }, + "ConfigValidationPluginConfig": { + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "plugins" + ], + "properties": { + "create_time": { + "type": "integer", + "description": "Unix timestamp of creation. Read-only." + }, + "name": { + "type": "string", + "description": "Human-readable name." + }, + "plugins": { + "$ref": "#/components/schemas/Plugins" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "update_time": { + "type": "integer", + "description": "Unix timestamp of last update. Read-only." + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description." + }, + "modifiedIndex": { + "type": "integer", + "description": "Accepted only with the YAML config provider; see standalone.lua." + } + }, + "description": "Declarative resource. modifiedIndex and composite credential IDs require the YAML config provider; etcd-mode validation uses the unpatched core schema." + }, + "ConfigValidationConsumerGroup": { + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "plugins" + ], + "properties": { + "create_time": { + "type": "integer", + "description": "Unix timestamp of creation. Read-only." + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Human-readable name." + }, + "plugins": { + "$ref": "#/components/schemas/Plugins" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "update_time": { + "type": "integer", + "description": "Unix timestamp of last update. Read-only." + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description of this group." + }, + "modifiedIndex": { + "type": "integer", + "description": "Accepted only with the YAML config provider; see standalone.lua." + } + }, + "description": "Declarative resource. modifiedIndex and composite credential IDs require the YAML config provider; etcd-mode validation uses the unpatched core schema." + }, + "ConfigValidationGlobalRule": { + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "plugins" + ], + "properties": { + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "create_time": { + "type": "integer", + "description": "Unix timestamp of creation. Read-only." + }, + "update_time": { + "type": "integer", + "description": "Unix timestamp of last update. Read-only." + }, + "plugins": { + "$ref": "#/components/schemas/Plugins" + }, + "modifiedIndex": { + "type": "integer", + "description": "Accepted only with the YAML config provider; see standalone.lua." + } + }, + "description": "Declarative resource. modifiedIndex and composite credential IDs require the YAML config provider; etcd-mode validation uses the unpatched core schema." + }, + "ConfigValidationProto": { + "additionalProperties": false, + "type": "object", + "required": [ + "content" + ], + "properties": { + "create_time": { + "type": "integer", + "description": "Unix timestamp of creation. Read-only." + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Human-readable name." + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "update_time": { + "type": "integer", + "description": "Unix timestamp of last update. Read-only." + }, + "content": { + "type": "string", + "maxLength": 1048576, + "minLength": 1, + "description": "Protobuf `.proto` file content as a string." + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description." + }, + "modifiedIndex": { + "type": "integer", + "description": "Accepted only with the YAML config provider; see standalone.lua." + } + }, + "description": "Declarative resource. modifiedIndex and composite credential IDs require the YAML config provider; etcd-mode validation uses the unpatched core schema." + }, + "RouteInput": { + "type": "object", + "additionalProperties": false, + "not": { + "anyOf": [ + { + "required": [ + "script", + "plugins" + ] + }, + { + "required": [ + "script", + "plugin_config_id" + ] + } + ] + }, + "properties": { + "service_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "uris": { + "items": { + "description": "HTTP uri", + "type": "string" + }, + "type": "array", + "minItems": 1, + "uniqueItems": true, + "description": "Multiple URI patterns. Mutually exclusive with `uri`." + }, + "hosts": { + "items": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "type": "array", + "minItems": 1, + "uniqueItems": true, + "description": "Multiple hostnames to match. Mutually exclusive with `host`." + }, + "remote_addrs": { + "items": { + "type": "string", + "description": "client IP", + "anyOf": [ + { + "type": "string", + "title": "IPv4", + "format": "ipv4" + }, + { + "type": "string", + "title": "IPv4/CIDR", + "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": "IPv6", + "format": "ipv6" + }, + { + "type": "string", + "title": "IPv6/CIDR", + "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": "array", + "minItems": 1, + "uniqueItems": true, + "description": "Multiple client IP addresses or CIDR ranges to match. Mutually exclusive with `remote_addr`." + }, + "host": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string", + "description": "Single hostname to match against the request's `Host` header. Supports wildcard prefix (`*.example.com`). Mutually exclusive with `hosts`.", + "examples": [ + "api.example.com" + ] + }, + "remote_addr": { + "type": "string", + "description": "Single client IP address or CIDR range to match. Mutually exclusive with `remote_addrs`.", + "anyOf": [ + { + "type": "string", + "title": "IPv4", + "format": "ipv4" + }, + { + "type": "string", + "title": "IPv4/CIDR", + "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": "IPv6", + "format": "ipv6" + }, + { + "type": "string", + "title": "IPv6/CIDR", + "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}$" + } + ], + "examples": [ + "192.168.1.0/24" + ] + }, + "script_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "upstream_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "plugins": { + "$ref": "#/components/schemas/Plugins" + }, + "priority": { + "default": 0, + "type": "integer", + "description": "Route priority. When multiple routes match the same request, the one with the highest priority wins." + }, + "uri": { + "type": "string", + "maxLength": 4096, + "minLength": 1, + "description": "URI path pattern to match. Supports exact match (`/api/v1/users`) and prefix match (`/api/*`). Mutually exclusive with `uris`.", + "examples": [ + "/api/v1/users/*" + ] + }, + "methods": { + "items": { + "type": "string", + "description": "HTTP method", + "enum": [ + "GET", + "POST", + "PUT", + "DELETE", + "PATCH", + "HEAD", + "OPTIONS", + "CONNECT", + "TRACE", + "PURGE" + ] + }, + "type": "array", + "uniqueItems": true, + "description": "HTTP methods allowed on this route. If empty or not set, all methods are allowed.", + "examples": [ + [ + "GET", + "POST" + ] + ] + }, + "timeout": { + "$ref": "#/components/schemas/Timeout" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "status": { + "default": 1, + "type": "integer", + "description": "Route status.", + "enum": [ + 1, + 0 + ] + }, + "enable_websocket": { + "description": "Enable WebSocket proxying on this route.", + "type": "boolean" + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description of what this route does." + }, + "upstream": { + "$ref": "#/components/schemas/UpstreamInput" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Human-readable name for the route." + }, + "plugin_config_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "script": { + "type": "string", + "maxLength": 102400, + "minLength": 10, + "description": "Lua script for plugin orchestration. Cannot be used together with `plugins` or `plugin_config_id`." + }, + "filter_func": { + "type": "string", + "pattern": "^function", + "minLength": 10, + "description": "Custom Lua function for advanced request matching. Must start with `function`." + }, + "vars": { + "type": "array", + "description": "Custom matching conditions using [APISIX expressions](https://apisix.apache.org/docs/apisix/3.17/router-radixtree/#how-to-filter-route-by-nginx-built-in-variable). Each condition is an array of `[variable, operator, value]`.", + "examples": [ + [ + [ + "http_user_agent", + "~*", + "curl" + ] + ] + ] + }, + "labels": { + "$ref": "#/components/schemas/Labels" + } + }, + "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" + ] + } + ] + } + ], + "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" + ] + } + ], + "description": "Route configuration. A route matches incoming requests by URI, host, methods, and other conditions, then forwards them to an upstream.\n\nA route must have at least a `uri` (or `uris`) and one of: `upstream`, `upstream_id`, `service_id`, or `plugins`." + }, + "UpstreamInput": { + "oneOf": [ + { + "required": [ + "nodes" + ] + }, + { + "required": [ + "service_name", + "discovery_type" + ] + } + ], + "type": "object", + "additionalProperties": false, + "properties": { + "tls": { + "$ref": "#/components/schemas/UpstreamTLS" + }, + "retries": { + "minimum": 0, + "type": "integer", + "description": "Number of retry attempts when a request to the upstream fails." + }, + "key": { + "description": "The hash key used when `type` is `chash`. Required when `hash_on` is `header`, `cookie`, `vars`, or `vars_combinations`. Not needed when `hash_on` is `consumer`.", + "type": "string" + }, + "pass_host": { + "default": "pass", + "type": "string", + "description": "How to set the `Host` header when proxying to the upstream.", + "enum": [ + "pass", + "node", + "rewrite" + ] + }, + "service_name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Service name for service discovery. Required when `discovery_type` is set." + }, + "discovery_args": { + "properties": { + "namespace_id": { + "description": "Namespace ID for Nacos service discovery.", + "type": "string" + }, + "group_name": { + "description": "Group name for service discovery.", + "type": "string" + } + }, + "type": "object", + "description": "Additional arguments for service discovery." + }, + "type": { + "type": "string", + "description": "Load balancing algorithm.", + "default": "roundrobin" + }, + "timeout": { + "$ref": "#/components/schemas/Timeout" + }, + "checks": { + "$ref": "#/components/schemas/HealthCheck" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "upstream_host": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string", + "description": "Custom `Host` header value. Only effective when `pass_host` is `rewrite`." + }, + "discovery_type": { + "description": "Service discovery type (e.g., `dns`, `consul`, `nacos`, `eureka`). Required when using service discovery instead of static `nodes`.", + "type": "string" + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description of the upstream." + }, + "scheme": { + "enum": [ + "grpc", + "grpcs", + "http", + "https", + "tcp", + "tls", + "udp", + "kafka" + ], + "default": "http", + "description": "Protocol for communicating with upstream nodes." + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Human-readable name for the upstream." + }, + "hash_on": { + "type": "string", + "default": "vars", + "enum": [ + "vars", + "header", + "cookie", + "consumer", + "vars_combinations" + ], + "description": "What to hash on when `type` is `chash`." + }, + "nodes": { + "$ref": "#/components/schemas/UpstreamNodes" + }, + "keepalive_pool": { + "$ref": "#/components/schemas/KeepalivePool" + }, + "retry_timeout": { + "minimum": 0, + "type": "number", + "description": "Maximum time in seconds for retry attempts. `0` disables the retry timeout." + }, + "labels": { + "$ref": "#/components/schemas/Labels" + } + }, + "description": "Upstream configuration defining backend service nodes and load balancing behavior.\n\nAn upstream must specify either `nodes` (static list) or `service_name` + `discovery_type` (service discovery)." + }, + "ServiceInput": { + "type": "object", + "additionalProperties": false, + "properties": { + "upstream": { + "$ref": "#/components/schemas/UpstreamInput" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "hosts": { + "items": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "type": "array", + "minItems": 1, + "uniqueItems": true, + "description": "Hostnames associated with this service." + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description of this service." + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Human-readable name." + }, + "upstream_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "plugins": { + "$ref": "#/components/schemas/Plugins" + }, + "enable_websocket": { + "description": "Enable WebSocket proxying for routes using this service.", + "type": "boolean" + }, + "script": { + "type": "string", + "maxLength": 102400, + "minLength": 10, + "description": "Lua script for plugin orchestration." + }, + "labels": { + "$ref": "#/components/schemas/Labels" + } + }, + "description": "Service configuration. A service groups an upstream and plugins so multiple routes can share the same backend configuration." + }, + "ConsumerInput": { + "additionalProperties": false, + "type": "object", + "required": [ + "username" + ], + "properties": { + "username": { + "maxLength": 256, + "type": "string", + "pattern": "^[a-zA-Z0-9_\\-]+$", + "minLength": 1, + "description": "Unique username for the consumer. Only alphanumeric characters, hyphens, and underscores are allowed.", + "examples": [ + "my-app" + ] + }, + "plugins": { + "$ref": "#/components/schemas/Plugins" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "group_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description of this consumer." + } + }, + "description": "Consumer configuration. Represents an API user or application with authentication credentials and plugin policies." + }, + "CredentialInput": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Human-readable name." + }, + "plugins": { + "maxProperties": 1, + "type": "object", + "description": "At most one authentication plugin configuration (for example, `key-auth`, `basic-auth`, or `jwt-auth`)." + }, + "id": { + "oneOf": [ + { + "$ref": "#/components/schemas/ResourceId" + }, + { + "maxLength": 128, + "type": "string", + "pattern": "^[a-zA-Z0-9-_]+/credentials/[a-zA-Z0-9-_.]+$", + "minLength": 15 + } + ], + "description": "Unique identifier for the resource. Can be a string (1–64 characters, alphanumeric with `-`, `_`, `.`) or a positive integer." + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description of this credential." + } + }, + "description": "Credential configuration for a consumer. A credential can store at most one authentication plugin." + }, + "SSLInput": { + "type": "object", + "additionalProperties": false, + "then": { + "oneOf": [ + { + "required": [ + "sni", + "key", + "cert" + ] + }, + { + "required": [ + "snis", + "key", + "cert" + ] + } + ] + }, + "properties": { + "type": { + "enum": [ + "server", + "client" + ], + "type": "string", + "description": "Certificate type.", + "default": "server" + }, + "snis": { + "items": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string" + }, + "type": "array", + "minItems": 1, + "description": "Multiple SNI hostnames. Mutually exclusive with `sni`." + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "status": { + "default": 1, + "type": "integer", + "description": "SSL status.", + "enum": [ + 1, + 0 + ] + }, + "cert": { + "type": "string", + "maxLength": 65536, + "minLength": 128, + "description": "PEM-encoded server certificate. Can also be a secret reference (`$secret://...`)." + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description." + }, + "client": { + "type": "object", + "required": [ + "ca" + ], + "properties": { + "ca": { + "type": "string", + "maxLength": 65536, + "minLength": 128, + "description": "PEM-encoded CA certificate for client verification." + }, + "depth": { + "minimum": 0, + "type": "integer", + "default": 1, + "description": "Maximum certificate chain depth." + }, + "skip_mtls_uri_regex": { + "items": { + "description": "uri regular expression to skip mtls", + "type": "string" + }, + "type": "array", + "minItems": 1, + "uniqueItems": true, + "description": "URI regex patterns to skip mTLS verification." + } + }, + "description": "mTLS client verification settings." + }, + "key": { + "type": "string", + "maxLength": 65536, + "minLength": 64, + "description": "PEM-encoded private key. Can also be a secret reference (`$secret://...`). **Not returned in GET responses for security.**" + }, + "ssl_protocols": { + "items": { + "enum": [ + "TLSv1.1", + "TLSv1.2", + "TLSv1.3" + ] + }, + "type": "array", + "description": "Allowed TLS protocol versions.", + "maxItems": 3, + "uniqueItems": true + }, + "keys": { + "items": { + "type": "string", + "maxLength": 65536, + "minLength": 64 + }, + "type": "array", + "description": "Additional private keys corresponding to `certs`." + }, + "certs": { + "items": { + "type": "string", + "maxLength": 65536, + "minLength": 128 + }, + "type": "array", + "description": "Additional certificates for certificate chain or alternative key types." + }, + "sni": { + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$", + "type": "string", + "description": "Server Name Indication (SNI) to match. Supports wildcard prefix (`*.example.com`). Mutually exclusive with `snis`.", + "examples": [ + "api.example.com" + ] + }, + "labels": { + "$ref": "#/components/schemas/Labels" + } + }, + "if": { + "properties": { + "type": { + "enum": [ + "server" + ] + } + } + }, + "else": { + "required": [ + "key", + "cert" + ], + "title": "SNI not required" + }, + "description": "SSL certificate configuration for TLS termination or mTLS." + }, + "StreamRouteInput": { + "type": "object", + "additionalProperties": false, + "properties": { + "service_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "upstream": { + "$ref": "#/components/schemas/UpstreamInput" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "protocol": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "logger": { + "items": { + "properties": { + "conf": { + "$ref": "#/components/schemas/Plugins" + }, + "name": { + "type": "string" + }, + "filter": { + "description": "logger filter rules", + "type": "array" + } + }, + "additionalProperties": false, + "dependentRequired": { + "name": [ + "conf" + ] + } + }, + "type": "array" + }, + "superior_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "name": { + "type": "string" + }, + "conf": { + "$ref": "#/components/schemas/Plugins" + } + } + }, + "server_port": { + "minimum": 1, + "type": "integer", + "description": "Server port to match.", + "maximum": 65535 + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description." + }, + "upstream_id": { + "$ref": "#/components/schemas/ResourceId" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Human-readable name." + }, + "server_addr": { + "type": "string", + "description": "Server IP address to match.", + "anyOf": [ + { + "type": "string", + "title": "IPv4", + "format": "ipv4" + }, + { + "type": "string", + "title": "IPv4/CIDR", + "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": "IPv6", + "format": "ipv6" + }, + { + "type": "string", + "title": "IPv6/CIDR", + "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}$" + } + ] + }, + "plugins": { + "$ref": "#/components/schemas/Plugins" + }, + "sni": { + "type": "string", + "description": "SNI hostname to match (for TLS connections).", + "pattern": "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$" + }, + "remote_addr": { + "type": "string", + "description": "Client IP address or CIDR range to match.", + "anyOf": [ + { + "type": "string", + "title": "IPv4", + "format": "ipv4" + }, + { + "type": "string", + "title": "IPv4/CIDR", + "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": "IPv6", + "format": "ipv6" + }, + { + "type": "string", + "title": "IPv6/CIDR", + "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}$" + } + ] + }, + "labels": { + "$ref": "#/components/schemas/Labels" + } + }, + "description": "Stream Route for TCP/UDP (L4) proxying. Matches transport-layer connections by client IP, server address/port, or SNI." + }, + "GlobalRuleInput": { + "additionalProperties": false, + "type": "object", + "required": [ + "plugins" + ], + "properties": { + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "plugins": { + "$ref": "#/components/schemas/Plugins" + } + }, + "description": "Global Rule configuration. Global plugins apply independently of locally bound plugins. For HTTP requests, execution is interleaved by phase: global `rewrite`, local `rewrite`, global `access`, then local `access`. Priorities order plugins within each phase and scope, and an early response skips handlers scheduled later." + }, + "ConsumerGroupInput": { + "additionalProperties": false, + "type": "object", + "required": [ + "plugins" + ], + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Human-readable name." + }, + "plugins": { + "$ref": "#/components/schemas/Plugins" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description of this group." + } + }, + "description": "Consumer Group configuration. Defines shared plugin configurations for multiple consumers." + }, + "PluginConfigInput": { + "additionalProperties": false, + "type": "object", + "required": [ + "plugins" + ], + "properties": { + "name": { + "type": "string", + "description": "Human-readable name." + }, + "plugins": { + "$ref": "#/components/schemas/Plugins" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description." + } + }, + "description": "Plugin Config — a reusable set of plugin configurations that can be referenced by multiple routes." + }, + "ProtoInput": { + "additionalProperties": false, + "type": "object", + "required": [ + "content" + ], + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Human-readable name." + }, + "labels": { + "$ref": "#/components/schemas/Labels" + }, + "id": { + "$ref": "#/components/schemas/ResourceId" + }, + "content": { + "type": "string", + "maxLength": 1048576, + "minLength": 1, + "description": "Protobuf `.proto` file content as a string." + }, + "desc": { + "maxLength": 256, + "type": "string", + "description": "Description." + } + }, + "description": "Protocol buffer definition for gRPC transcoding." + }, + "RouteResponse": { + "anyOf": [ + { + "$ref": "#/components/schemas/RouteEnvelope" + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "action": { + "type": "string" + }, + "node": { + "$ref": "#/components/schemas/RouteEnvelope" + } + } + } + ] + }, + "RouteListResponse": { + "anyOf": [ + { + "$ref": "#/components/schemas/RouteListEnvelope" + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "action": { + "type": "string" + }, + "count": { + "type": "integer" + }, + "node": { + "type": "object", + "properties": { + "dir": { + "type": "boolean" + }, + "nodes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RouteEnvelope" + } + } + } + } + } + } + ] + }, + "UpstreamResponse": { + "anyOf": [ + { + "$ref": "#/components/schemas/UpstreamEnvelope" + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "action": { + "type": "string" + }, + "node": { + "$ref": "#/components/schemas/UpstreamEnvelope" + } + } + } + ] + }, + "UpstreamListResponse": { + "anyOf": [ + { + "$ref": "#/components/schemas/UpstreamListEnvelope" + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "action": { + "type": "string" + }, + "count": { + "type": "integer" + }, + "node": { + "type": "object", + "properties": { + "dir": { + "type": "boolean" + }, + "nodes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UpstreamEnvelope" + } + } + } + } + } + } + ] + }, + "ServiceResponse": { + "anyOf": [ + { + "$ref": "#/components/schemas/ServiceEnvelope" + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "action": { + "type": "string" + }, + "node": { + "$ref": "#/components/schemas/ServiceEnvelope" + } + } + } + ] + }, + "ServiceListResponse": { + "anyOf": [ + { + "$ref": "#/components/schemas/ServiceListEnvelope" + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "action": { + "type": "string" + }, + "count": { + "type": "integer" + }, + "node": { + "type": "object", + "properties": { + "dir": { + "type": "boolean" + }, + "nodes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ServiceEnvelope" + } + } + } + } + } + } + ] + }, + "ConsumerResponse": { + "anyOf": [ + { + "$ref": "#/components/schemas/ConsumerEnvelope" + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "action": { + "type": "string" + }, + "node": { + "$ref": "#/components/schemas/ConsumerEnvelope" + } + } + } + ] + }, + "ConsumerListResponse": { + "anyOf": [ + { + "$ref": "#/components/schemas/ConsumerListEnvelope" + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "action": { + "type": "string" + }, + "count": { + "type": "integer" + }, + "node": { + "type": "object", + "properties": { + "dir": { + "type": "boolean" + }, + "nodes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ConsumerEnvelope" + } + } + } + } + } + } + ] + }, + "CredentialResponse": { + "anyOf": [ + { + "$ref": "#/components/schemas/CredentialEnvelope" + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "action": { + "type": "string" + }, + "node": { + "$ref": "#/components/schemas/CredentialEnvelope" + } + } + } + ] + }, + "CredentialListResponse": { + "anyOf": [ + { + "$ref": "#/components/schemas/CredentialListEnvelope" + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "action": { + "type": "string" + }, + "count": { + "type": "integer" + }, + "node": { + "type": "object", + "properties": { + "dir": { + "type": "boolean" + }, + "nodes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CredentialEnvelope" + } + } + } + } + } + } + ] + }, + "SSLResponse": { + "anyOf": [ + { + "$ref": "#/components/schemas/SSLEnvelope" + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "action": { + "type": "string" + }, + "node": { + "$ref": "#/components/schemas/SSLEnvelope" + } + } + } + ] + }, + "SSLListResponse": { + "anyOf": [ + { + "$ref": "#/components/schemas/SSLListEnvelope" + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "action": { + "type": "string" + }, + "count": { + "type": "integer" + }, + "node": { + "type": "object", + "properties": { + "dir": { + "type": "boolean" + }, + "nodes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SSLEnvelope" + } + } + } + } + } + } + ] + }, + "StreamRouteResponse": { + "anyOf": [ + { + "$ref": "#/components/schemas/StreamRouteEnvelope" + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "action": { + "type": "string" + }, + "node": { + "$ref": "#/components/schemas/StreamRouteEnvelope" + } + } + } + ] + }, + "StreamRouteListResponse": { + "anyOf": [ + { + "$ref": "#/components/schemas/StreamRouteListEnvelope" + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "action": { + "type": "string" + }, + "count": { + "type": "integer" + }, + "node": { + "type": "object", + "properties": { + "dir": { + "type": "boolean" + }, + "nodes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StreamRouteEnvelope" + } + } + } + } + } + } + ] + }, + "GlobalRuleResponse": { + "anyOf": [ + { + "$ref": "#/components/schemas/GlobalRuleEnvelope" + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "action": { + "type": "string" + }, + "node": { + "$ref": "#/components/schemas/GlobalRuleEnvelope" + } + } + } + ] + }, + "GlobalRuleListResponse": { + "anyOf": [ + { + "$ref": "#/components/schemas/GlobalRuleListEnvelope" + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "action": { + "type": "string" + }, + "count": { + "type": "integer" + }, + "node": { + "type": "object", + "properties": { + "dir": { + "type": "boolean" + }, + "nodes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GlobalRuleEnvelope" + } + } + } + } + } + } + ] + }, + "ConsumerGroupResponse": { + "anyOf": [ + { + "$ref": "#/components/schemas/ConsumerGroupEnvelope" + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "action": { + "type": "string" + }, + "node": { + "$ref": "#/components/schemas/ConsumerGroupEnvelope" + } + } + } + ] + }, + "ConsumerGroupListResponse": { + "anyOf": [ + { + "$ref": "#/components/schemas/ConsumerGroupListEnvelope" + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "action": { + "type": "string" + }, + "count": { + "type": "integer" + }, + "node": { + "type": "object", + "properties": { + "dir": { + "type": "boolean" + }, + "nodes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ConsumerGroupEnvelope" + } + } + } + } + } + } + ] + }, + "PluginConfigResponse": { + "anyOf": [ + { + "$ref": "#/components/schemas/PluginConfigEnvelope" + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "action": { + "type": "string" + }, + "node": { + "$ref": "#/components/schemas/PluginConfigEnvelope" + } + } + } + ] + }, + "PluginConfigListResponse": { + "anyOf": [ + { + "$ref": "#/components/schemas/PluginConfigListEnvelope" + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "action": { + "type": "string" + }, + "count": { + "type": "integer" + }, + "node": { + "type": "object", + "properties": { + "dir": { + "type": "boolean" + }, + "nodes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PluginConfigEnvelope" + } + } + } + } + } + } + ] + }, + "ProtoResponse": { + "anyOf": [ + { + "$ref": "#/components/schemas/ProtoEnvelope" + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "action": { + "type": "string" + }, + "node": { + "$ref": "#/components/schemas/ProtoEnvelope" + } + } + } + ] + }, + "ProtoListResponse": { + "anyOf": [ + { + "$ref": "#/components/schemas/ProtoListEnvelope" + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "action": { + "type": "string" + }, + "count": { + "type": "integer" + }, + "node": { + "type": "object", + "properties": { + "dir": { + "type": "boolean" + }, + "nodes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProtoEnvelope" + } + } + } + } + } + } + ] + }, + "SSLReadEnvelope": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/SSLRead" + }, + "createdIndex": { + "type": "integer" + }, + "modifiedIndex": { + "type": "integer" + } + }, + "required": [ + "key", + "value" + ] + }, + "SSLReadListEnvelope": { + "type": "object", + "properties": { + "total": { + "type": "integer" + }, + "list": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SSLReadEnvelope" + } + } + }, + "required": [ + "total", + "list" + ] + }, + "SSLReadResponse": { + "anyOf": [ + { + "$ref": "#/components/schemas/SSLReadEnvelope" + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "action": { + "type": "string" + }, + "node": { + "$ref": "#/components/schemas/SSLReadEnvelope" + } + } + } + ] + }, + "SSLReadListResponse": { + "anyOf": [ + { + "$ref": "#/components/schemas/SSLReadListEnvelope" + }, + { + "type": "object", + "required": [ + "node" + ], + "properties": { + "action": { + "type": "string" + }, + "count": { + "type": "integer" + }, + "node": { + "type": "object", + "properties": { + "dir": { + "type": "boolean" + }, + "nodes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SSLReadEnvelope" + } + } + } + } + } + } + ] + }, + "DeleteResponse": { + "type": "object", + "required": [ + "deleted", + "key" + ], + "properties": { + "key": { + "type": "string" + }, + "deleted": { + "type": "string", + "description": "Number of deleted keys encoded as a string." + }, + "action": { + "type": "string" + }, + "node": { + "type": "object" + } + } + } + }, + "responses": { + "BadRequest": { + "description": "**Bad Request** — The request body is invalid or missing required fields.", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ErrorResponse" + }, + { + "type": "string" + } + ] + }, + "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": { + "message": "Key not found" + } + } + } + } + }, + "parameters": { + "Ttl": { + "name": "ttl", + "in": "query", + "required": false, + "schema": { + "type": "number" + }, + "description": "Lease time in seconds, passed to etcd. The Admin handler accepts numeric values; etcd can reject invalid lease durations.", + "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": "Pagination is applied only in v3 when both page and page_size are numeric. A page less than 1 is treated as 1.", + "example": 1 + }, + "PageSize": { + "name": "page_size", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 10, + "maximum": 500 + }, + "description": "V3 only. Both page and page_size must be supplied; otherwise the full list is returned. Range 10–500.", + "example": 20 + }, + "IdInPath": { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-zA-Z0-9-_.]+$" + }, + "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": "V3 only. Filter list by `name` (regex match)." + }, + "LabelFilter": { + "name": "label", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "V3 only. Filter list to resources with the given label key.", + "example": "env" + }, + "UriFilter": { + "name": "uri", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "V3 only. Filter routes by URI (regex match against `uri` or any `uris[]`)." + }, + "FilterExpr": { + "name": "filter", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "V3 only. URL-encoded query string. Supported fields: routes service_id/upstream_id; stream_routes upstream_id. Unsupported filter keys are ignored." + }, + "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, + "schema": { + "type": "string", + "enum": [ + "route", + "service", + "upstream", + "consumer", + "credential", + "ssl", + "proto", + "global_rule", + "stream_route", + "plugin_config", + "consumer_group", + "plugins" + ] + }, + "description": "Exported core.schema name (singular), for example route or upstream.", + "example": "route" + } + }, + "requestBodies": { + "CredentialUpsert": { + "description": "`Credential` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CredentialInput" + }, + "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": { + "type": "object", + "additionalProperties": true + } + } + } + }, + "RouteUpsertPostCollection": { + "description": "`Route` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RouteInput" + }, + "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": { + "$ref": "#/components/schemas/RouteInput" + }, + "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": { + "$ref": "#/components/schemas/ServiceInput" + }, + "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": { + "$ref": "#/components/schemas/ServiceInput" + }, + "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": { + "$ref": "#/components/schemas/UpstreamInput" + }, + "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": { + "$ref": "#/components/schemas/UpstreamInput" + }, + "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": { + "$ref": "#/components/schemas/ConsumerInput" + }, + "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": { + "$ref": "#/components/schemas/ConsumerGroupInput" + }, + "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": { + "$ref": "#/components/schemas/SSLInput" + }, + "example": { + "type": "server", + "sni": "test.com", + "cert": "-----BEGIN CERTIFICATE-----\nMIIEojCCAwqgAwIBAgIJAK253pMhgCkxMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV\nBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0RvbmcxDzANBgNVBAcMBlpodUhhaTEPMA0G\nA1UECgwGaXJlc3R5MREwDwYDVQQDDAh0ZXN0LmNvbTAgFw0xOTA2MjQyMjE4MDVa\nGA8yMTE5MDUzMTIyMTgwNVowVjELMAkGA1UEBhMCQ04xEjAQBgNVBAgMCUd1YW5n\nRG9uZzEPMA0GA1UEBwwGWmh1SGFpMQ8wDQYDVQQKDAZpcmVzdHkxETAPBgNVBAMM\nCHRlc3QuY29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAyCM0rqJe\ncvgnCfOw4fATotPwk5Ba0gC2YvIrO+gSbQkyxXF5jhZB3W6BkWUWR4oNFLLSqcVb\nVDPitz/Mt46Mo8amuS6zTbQetGnBARzPLtmVhJfoeLj0efMiOepOSZflj9Ob4yKR\n2bGdEFOdHPjm+4ggXU9jMKeLqdVvxll/JiVFBW5smPtW1Oc/BV5terhscJdOgmRr\nabf9xiIis9/qVYfyGn52u9452V0owUuwP7nZ01jt6iMWEGeQU6mwPENgvj1olji2\nWjdG2UwpUVp3jp3l7j1ekQ6mI0F7yI+LeHzfUwiyVt1TmtMWn1ztk6FfLRqwJWR/\nEvm95vnfS3Le4S2ky3XAgn2UnCMyej3wDN6qHR1onpRVeXhrBajbCRDRBMwaNw/1\n/3Uvza8QKK10PzQR6OcQ0xo9psMkd9j9ts/dTuo2fzaqpIfyUbPST4GdqNG9NyIh\n/B9g26/0EWcjyO7mYVkaycrtLMaXm1u9jyRmcQQI1cGrGwyXbrieNp63AgMBAAGj\ncTBvMB0GA1UdDgQWBBSZtSvV8mBwl0bpkvFtgyiOUUcbszAfBgNVHSMEGDAWgBSZ\ntSvV8mBwl0bpkvFtgyiOUUcbszAMBgNVHRMEBTADAQH/MB8GA1UdEQQYMBaCCHRl\nc3QuY29tggoqLnRlc3QuY29tMA0GCSqGSIb3DQEBCwUAA4IBgQAHGEul/x7ViVgC\ntC8CbXEslYEkj1XVr2Y4hXZXAXKd3W7V3TC8rqWWBbr6L/tsSVFt126V5WyRmOaY\n1A5pju8VhnkhYxYfZALQxJN2tZPFVeME9iGJ9BE1wPtpMgITX8Rt9kbNlENfAgOl\nPYzrUZN1YUQjX+X8t8/1VkSmyZysr6ngJ46/M8F16gfYXc9zFj846Z9VST0zCKob\nrJs3GtHOkS9zGGldqKKCj+Awl0jvTstI4qtS1ED92tcnJh5j/SSXCAB5FgnpKZWy\nhme45nBQj86rJ8FhN+/aQ9H9/2Ib6Q4wbpaIvf4lQdLUEcWAeZGW6Rk0JURwEog1\n7/mMgkapDglgeFx9f/XztSTrkHTaX4Obr+nYrZ2V4KOB4llZnK5GeNjDrOOJDk2y\nIJFgBOZJWyS93dQfuKEj42hA79MuX64lMSCVQSjX+ipR289GQZqFrIhiJxLyA+Ve\nU/OOcSRr39Kuis/JJ+DkgHYa/PWHZhnJQBxcqXXk1bJGw9BNbhM=\n-----END CERTIFICATE-----\n", + "key": "-----BEGIN RSA PRIVATE KEY-----\n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\n-----END RSA PRIVATE KEY-----\n" + } + } + } + }, + "SslUpsertPutId": { + "description": "`SSL` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SSLInput" + }, + "example": { + "type": "server", + "sni": "api.example.com", + "cert": "-----BEGIN CERTIFICATE-----\nMIIEojCCAwqgAwIBAgIJAK253pMhgCkxMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV\nBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0RvbmcxDzANBgNVBAcMBlpodUhhaTEPMA0G\nA1UECgwGaXJlc3R5MREwDwYDVQQDDAh0ZXN0LmNvbTAgFw0xOTA2MjQyMjE4MDVa\nGA8yMTE5MDUzMTIyMTgwNVowVjELMAkGA1UEBhMCQ04xEjAQBgNVBAgMCUd1YW5n\nRG9uZzEPMA0GA1UEBwwGWmh1SGFpMQ8wDQYDVQQKDAZpcmVzdHkxETAPBgNVBAMM\nCHRlc3QuY29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAyCM0rqJe\ncvgnCfOw4fATotPwk5Ba0gC2YvIrO+gSbQkyxXF5jhZB3W6BkWUWR4oNFLLSqcVb\nVDPitz/Mt46Mo8amuS6zTbQetGnBARzPLtmVhJfoeLj0efMiOepOSZflj9Ob4yKR\n2bGdEFOdHPjm+4ggXU9jMKeLqdVvxll/JiVFBW5smPtW1Oc/BV5terhscJdOgmRr\nabf9xiIis9/qVYfyGn52u9452V0owUuwP7nZ01jt6iMWEGeQU6mwPENgvj1olji2\nWjdG2UwpUVp3jp3l7j1ekQ6mI0F7yI+LeHzfUwiyVt1TmtMWn1ztk6FfLRqwJWR/\nEvm95vnfS3Le4S2ky3XAgn2UnCMyej3wDN6qHR1onpRVeXhrBajbCRDRBMwaNw/1\n/3Uvza8QKK10PzQR6OcQ0xo9psMkd9j9ts/dTuo2fzaqpIfyUbPST4GdqNG9NyIh\n/B9g26/0EWcjyO7mYVkaycrtLMaXm1u9jyRmcQQI1cGrGwyXbrieNp63AgMBAAGj\ncTBvMB0GA1UdDgQWBBSZtSvV8mBwl0bpkvFtgyiOUUcbszAfBgNVHSMEGDAWgBSZ\ntSvV8mBwl0bpkvFtgyiOUUcbszAMBgNVHRMEBTADAQH/MB8GA1UdEQQYMBaCCHRl\nc3QuY29tggoqLnRlc3QuY29tMA0GCSqGSIb3DQEBCwUAA4IBgQAHGEul/x7ViVgC\ntC8CbXEslYEkj1XVr2Y4hXZXAXKd3W7V3TC8rqWWBbr6L/tsSVFt126V5WyRmOaY\n1A5pju8VhnkhYxYfZALQxJN2tZPFVeME9iGJ9BE1wPtpMgITX8Rt9kbNlENfAgOl\nPYzrUZN1YUQjX+X8t8/1VkSmyZysr6ngJ46/M8F16gfYXc9zFj846Z9VST0zCKob\nrJs3GtHOkS9zGGldqKKCj+Awl0jvTstI4qtS1ED92tcnJh5j/SSXCAB5FgnpKZWy\nhme45nBQj86rJ8FhN+/aQ9H9/2Ib6Q4wbpaIvf4lQdLUEcWAeZGW6Rk0JURwEog1\n7/mMgkapDglgeFx9f/XztSTrkHTaX4Obr+nYrZ2V4KOB4llZnK5GeNjDrOOJDk2y\nIJFgBOZJWyS93dQfuKEj42hA79MuX64lMSCVQSjX+ipR289GQZqFrIhiJxLyA+Ve\nU/OOcSRr39Kuis/JJ+DkgHYa/PWHZhnJQBxcqXXk1bJGw9BNbhM=\n-----END CERTIFICATE-----\n", + "key": "-----BEGIN RSA PRIVATE KEY-----\n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\n-----END RSA PRIVATE KEY-----\n" + } + } + } + }, + "GlobalRuleUpsertPutId": { + "description": "`GlobalRule` configuration body for create / replace / patch.", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GlobalRuleInput" + }, + "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": { + "$ref": "#/components/schemas/PluginConfigInput" + }, + "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": { + "$ref": "#/components/schemas/StreamRouteInput" + }, + "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": { + "$ref": "#/components/schemas/StreamRouteInput" + }, + "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": { + "$ref": "#/components/schemas/ProtoInput" + }, + "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": { + "$ref": "#/components/schemas/ProtoInput" + }, + "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": { + "$ref": "#/components/schemas/Secret" + }, + "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-apisix-source": { + "branch": "release/3.17", + "commit": "0e783c9c8e7940d3ecdbe3e983b2ca58583edec9", + "files": [ + "apisix/admin/init.lua", + "apisix/admin/resource.lua", + "apisix/schema_def.lua", + "apisix/admin/v3_adapter.lua", + "apisix/admin/standalone.lua", + "apisix/admin/config_validate.lua", + "apisix/secret/vault.lua", + "apisix/secret/aws.lua", + "apisix/secret/gcp.lua" + ] + } +} diff --git a/utils/admin-api-openapi-sources.json b/utils/admin-api-openapi-sources.json new file mode 100644 index 000000000000..a34a8ba94400 --- /dev/null +++ b/utils/admin-api-openapi-sources.json @@ -0,0 +1,32 @@ +{ + "apisix/admin/config_validate.lua": "6b356158624eb87a45fe921b89988aa8a19d174927e92552c86d88f1a1971f8f", + "apisix/admin/consumer_group.lua": "62fd700bf4e703b757da0788e9987afc6ddc73e9b613564c42d7b2c5ed90f41e", + "apisix/admin/consumers.lua": "1330ee34e539453ec3da36129f79f71545a9cb7cf80b449387b941dd547799ee", + "apisix/admin/credentials.lua": "00dcbbd7b95f68ea9819abcf3989e2f9eb821cc5a956bdf7a4cb643d1a0bb8be", + "apisix/admin/global_rules.lua": "7e13e9af8bdbc659fe62608cb7312a9380a04996b6f23d1ef41ac1f2319d9702", + "apisix/admin/init.lua": "c39f41e6534bc72751dd7ea54120b1623210fc080ab34eccdcb643c7604d0c04", + "apisix/admin/plugin_config.lua": "7d0456d2bfe990439fa75c3af190f728e6499023a4a583880096b2805c4c472b", + "apisix/admin/plugin_metadata.lua": "b8549902d0c621f372dd285f29eb90255342b262cade9d28474e391c75c566d6", + "apisix/admin/plugins.lua": "055b3156cdd51feafde542efa1a2b4652051470296b0c454f4bae5a77eb806a4", + "apisix/admin/proto.lua": "0df6fd0c24bfb13dc13adbb009cb431fa22a6a4061995a16120b3d6e4f2793e7", + "apisix/admin/resource.lua": "983579898ca5e2576975a47168fc93862faa46d7d56833aa799a4d0b5bb9dcff", + "apisix/admin/routes.lua": "a2d402aa4b344f7d81380bc8c765e61f38238e9543a1348dd65a4923cf7b58b6", + "apisix/admin/schema.lua": "5bde4bacae4669239e7a75eb3ab4b869f50bd1fe1eeed90b6fe85b3b7654bc93", + "apisix/admin/secrets.lua": "518ef11416c3cf695192cca44f100167b3ac92fbb76e8f4fbf7af9971a22ad24", + "apisix/admin/services.lua": "cc981bd24d4dfe79ed963666cbc7457d7cd4af65e348233dc1620560e3c89fc5", + "apisix/admin/ssl.lua": "48cc0f23194087a9774c0b3659e90d2310bdcb8e206a9b6baeee348007c6e34a", + "apisix/admin/standalone.lua": "d6b7e1db70ebdbdcf08b0fc40f9d4cf526f07b47ffe15b7ea60ea8000bad1489", + "apisix/admin/stream_routes.lua": "4b63b1834cbbb1f788c604bb4ad77f0f7e06804814bc1e989b6909aca4fc0b88", + "apisix/admin/upstreams.lua": "c3029d2e8d6f876d082d6894464b56881411b51a2fce8eb4f4b503704fb44f8d", + "apisix/admin/utils.lua": "5d0c5c5502009015e9903f60ce09c38ab2d0261ec8539e4bbc84d0467dcb9487", + "apisix/admin/v3_adapter.lua": "aa0624cbd58b3c9f5a3c25319c88d132af6a5fa63bafa63ca98ebc1fe2112a97", + "apisix/cli/config.lua": "ea3b26c3957726b9533c36d766327963c22c2eae02d4319683b0319138ea5847", + "apisix/constants.lua": "dcf1d49855b1afe5d97840b9f1f92497de4d4053d683d99290a0331431c32090", + "apisix/core/etcd.lua": "880de487cff574f95507d0e1d993ce021d59479bae4ab48ac4bfffecf9dcce0b", + "apisix/core/table.lua": "202f0585f0dd8a70daa716c6020f0a9b29c1cd573e60c1986f9a1610c3ee7d82", + "apisix/core/version.lua": "da61092262f59e069136f86c033714bb18e4aa57abcbdf8fa1bdd0024464e98a", + "apisix/schema_def.lua": "776763c27a3f5e217725e17b388809dc7d9510da523c10888f5bcdaaea2b519d", + "apisix/secret/aws.lua": "88de41f6f043122bed4e2a79d77cf34fec9144f798617b1546855e3285b1c57d", + "apisix/secret/gcp.lua": "4cc77606d3bcba1c7243dddd5693c2c52a8476e949be16bcf954776fcc734433", + "apisix/secret/vault.lua": "43b3e6fc2510dcc0b227c77cfbb6dd83133fa4911e7d8b755621b30fcee7deab" +} diff --git a/utils/check-admin-api-openapi.py b/utils/check-admin-api-openapi.py new file mode 100644 index 000000000000..4b479f505342 --- /dev/null +++ b/utils/check-admin-api-openapi.py @@ -0,0 +1,249 @@ +#!/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. +"""Offline Admin OpenAPI structural, example, and release-source parity checks. + +Requires lupa, jsonschema and openapi-spec-validator. Run from the repository root. +Lua schema tables are evaluated without loading APISIX, Nginx or external modules. +This is static verification; runtime check_schema callbacks still apply. +""" +import copy +import hashlib +import json +import re +from pathlib import Path + +from lupa.lua51 import LuaRuntime, lua_type +from openapi_spec_validator import validate +from jsonschema import Draft202012Validator +from referencing import Registry, Resource +from referencing.jsonschema import DRAFT202012 + +if not __debug__: + raise RuntimeError("run this checker without Python optimization so validation assertions remain active") + +SPEC = Path('docs/en/latest/admin-api.json') +MAPPING = { + 'Route': 'route', 'Service': 'service', 'Upstream': 'upstream', + 'Consumer': 'consumer', 'Credential': 'credential', 'SSL': 'ssl', + 'GlobalRule': 'global_rule', 'StreamRoute': 'stream_route', + 'ConsumerGroup': 'consumer_group', 'PluginConfig': 'plugin_config', + 'Proto': 'proto', 'ResourceId': 'id_schema', +} + + +def no_duplicates(pairs): + result = {} + for key, value in pairs: + assert key not in result, f'duplicate JSON key: {key}' + result[key] = value + return result + + +def convert(value, key=''): + if lua_type(value) != 'table': + return value + keys = list(value.keys()) + if keys and all(isinstance(k, int) for k in keys): + return [convert(value[i]) for i in range(1, len(keys) + 1)] + if not keys and key in {'required', 'enum', 'oneOf', 'anyOf', 'allOf'}: + return [] + return {k: convert(v, k) for k, v in value.items()} + + +def normalize(value): + """Convert draft-04 dependencies/bounds to JSON Schema 2020-12.""" + if isinstance(value, list): + return [normalize(v) for v in value] + if not isinstance(value, dict): + return value + result = {k: normalize(v) for k, v in value.items()} + for key, val in result.pop('dependencies', {}).items(): + keyword = 'dependentRequired' if isinstance(val, list) else 'dependentSchemas' + result.setdefault(keyword, {})[key] = val + for exclusive, bound in [('exclusiveMinimum', 'minimum'), + ('exclusiveMaximum', 'maximum')]: + if isinstance(result.get(exclusive), bool): + if result.pop(exclusive): + result[exclusive] = result.pop(bound) + return result + + +def main(): + spec_text = SPEC.read_text() + assert not re.search(r'api7|apiseven', spec_text, re.IGNORECASE) + spec = json.loads(spec_text, object_pairs_hook=no_duplicates) + validate(spec) + assert spec['openapi'] == '3.1.0' + version = re.search(r'VERSION\s*=\s*"([^"]+)"', + Path('apisix/core/version.lua').read_text()).group(1) + assert spec['info']['version'] == version == '3.17.0' + assert spec['x-apisix-source']['branch'] == 'release/3.17' + # Reject all external $refs: validation never downloads a schema. + counters = dict(refs=0, schemas=0, examples=0, defaults=0, operations=0) + uri = 'urn:apisix:admin-openapi' + resource = Resource.from_contents(spec, default_specification=DRAFT202012) + registry = Registry().with_resource(uri, resource) + + def resolve(obj): + while isinstance(obj, dict) and '$ref' in obj: + counters['refs'] += 1 + assert obj['$ref'].startswith('#/'), obj['$ref'] + target = spec + for part in obj['$ref'][2:].split('/'): + target = target[part.replace('~1', '/').replace('~0', '~')] + obj = target + return obj + + def check(value, schema, location, kind): + counters[kind] += 1 + # Local references in a subschema refer to the complete OpenAPI document. + def absolute_refs(obj): + if isinstance(obj, list): + return [absolute_refs(x) for x in obj] + if not isinstance(obj, dict): + return obj + return {k: uri + v if k == '$ref' and v.startswith('#') + else absolute_refs(v) for k, v in obj.items()} + validator = Draft202012Validator(absolute_refs(schema), registry=registry) + errors = list(validator.iter_errors(value)) + assert not errors, f'{location}: {errors[0].message}' + + def walk(obj, location=''): + if isinstance(obj, list): + for i, value in enumerate(obj): + walk(value, f'{location}/{i}') + if not isinstance(obj, dict): + return + resolve(obj) + if 'schema' in obj: + schema = resolve(obj['schema']) + counters['schemas'] += 1 + if 'example' in obj: + check(obj['example'], schema, location, 'examples') + for example in obj.get('examples', {}).values(): + example = resolve(example) + if 'value' in example: + check(example['value'], schema, location, 'examples') + if 'type' in obj or any(k in obj for k in ('anyOf', 'oneOf', 'allOf')): + if 'default' in obj: + check(obj['default'], obj, location, 'defaults') + if isinstance(obj.get('examples'), list): + for value in obj['examples']: + check(value, obj, location, 'examples') + for key, value in obj.items(): + walk(value, location + '/' + key) + + walk(spec) + ids = set() + for path, item in spec['paths'].items(): + for method, operation in item.items(): + if method not in {'get', 'put', 'post', 'patch', 'delete', 'head'}: + continue + counters['operations'] += 1 + opid = operation['operationId'] + assert re.fullmatch('[A-Za-z][A-Za-z0-9]*', opid), opid + assert opid not in ids, opid + ids.add(opid) + parameters = [resolve(p) for p in item.get('parameters', []) + + operation.get('parameters', [])] + assert len({(p['in'], p['name']) for p in parameters}) == len(parameters) + path_params = [p for p in parameters if p['in'] == 'path'] + assert {p['name'] for p in path_params} == set(re.findall(r'{([^}]+)}', path)) + assert all(p.get('required') for p in path_params) + + annotations = {'description', 'title', 'examples', 'example', 'readOnly', 'writeOnly'} + + def constraints(obj): + if isinstance(obj, list): + return [constraints(x) for x in obj] + if not isinstance(obj, dict): + return obj + obj = resolve(obj) + return {k: constraints(v) for k, v in obj.items() if k not in annotations} + + lua = LuaRuntime(unpack_returned_tuples=True) + source = normalize(convert(lua.execute(Path('apisix/schema_def.lua').read_text()))) + schemas = spec['components']['schemas'] + for schema in schemas.values(): + Draft202012Validator.check_schema(schema) + for name, key in MAPPING.items(): + assert constraints(schemas[name]) == constraints(source[key]), name + for name, key in MAPPING.items(): + if name == 'ResourceId': + continue + expected = copy.deepcopy(source[key]) + for field in ('create_time', 'update_time'): + expected['properties'].pop(field, None) + if 'upstream' in expected['properties']: + expected['properties']['upstream']['properties'].pop(field, None) + if 'required' in expected: + expected['required'] = [x for x in expected['required'] if x != 'id'] + if not expected['required']: + expected.pop('required') + assert constraints(schemas[name + 'Input']) == constraints(expected), name + 'Input' + expected = copy.deepcopy(source['ssl']) + for field in ('key', 'keys'): + expected['properties'].pop(field) + for branch in expected['then']['oneOf'] + [expected['else']]: + branch['required'] = [x for x in branch['required'] if x != 'key'] + assert constraints(schemas['SSLRead']) == constraints(expected), 'SSLRead' + for backend, name in [('vault', 'VaultSecret'), ('aws', 'AwsSecret'), + ('gcp', 'GcpSecret')]: + text = Path(f'apisix/secret/{backend}.lua').read_text() + table = text[text.index('local schema ='):text.index('local _M =')] + core = lua.table_from({'schema': lua.table_from({ + 'uri_def': lua.table_from(source['uri_def'])})}) + actual = normalize(convert(lua.execute('local core = ...; ' + table + + '; return schema', core))) + assert constraints(schemas[name]) == constraints(actual), name + + # Handler surface: derive resource names, exclusions, collection POST and item methods. + init = Path('apisix/admin/init.lua').read_text() + block = init[init.index('local resources ='):init.index('local _M =')] + resources = dict(re.findall(r'(\w+)\s*=\s*require\("apisix.admin.([\w_]+)"\)', block)) + for resource_name, module in resources.items(): + if resource_name in {'schema', 'plugins'}: + continue + text = Path(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() + base = '/apisix/admin/' + resource_name + if resource_name == 'credentials': + base = '/apisix/admin/consumers/{username}/credentials' + if resource_name == 'secrets': + base += '/{secret_type}' + key = 'username' if resource_name == 'consumers' else ( + 'plugin_name' if resource_name == 'plugin_metadata' else 'id') + expected = {'get', 'put', 'patch', 'delete'} - unsupported + actual = set(spec['paths'][base + '/{' + key + '}']) + assert expected <= actual, (resource_name, expected - actual) + assert not (actual & unsupported), resource_name + if 'post' not in unsupported: + assert 'post' in spec['paths'][base], resource_name + # Hand-audited dispatch/response logic is frozen by local file hashes. An implementation + # change requires renewed semantic review, not silently updating a generated spec. + manifest = json.loads(Path('utils/admin-api-openapi-sources.json').read_text()) + for filename, digest in manifest.items(): + assert hashlib.sha256(Path(filename).read_bytes()).hexdigest() == digest, filename + print('PASS: JSON, OpenAPI 3.1, local refs, operation IDs, path parameters,') + print(' examples/defaults, version, 12 core + 3 secret schemas, handler methods,') + print(f' and {len(manifest)} reviewed source hashes') + print(json.dumps(counters, sort_keys=True)) + print('Spec SHA256:', hashlib.sha256(SPEC.read_bytes()).hexdigest()) + + +if __name__ == '__main__': + main() diff --git a/utils/requirements-admin-api.txt b/utils/requirements-admin-api.txt new file mode 100644 index 000000000000..8593728c7daf --- /dev/null +++ b/utils/requirements-admin-api.txt @@ -0,0 +1,20 @@ +# 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. + +# Validation-only dependencies; the checker reads only local files. +openapi-spec-validator==0.7.2 +jsonschema==4.23.0 +referencing==0.36.2 +lupa==2.8