Skip to main content
Version: 3.8

Admin API

Description#

The Admin API lets users control their deployed Apache APISIX instance. The architecture design gives an idea about how everything fits together.

Configuration#

When APISIX is started, the Admin API will listen on port 9180 by default and take the API prefixed with /apisix/admin.

Therefore, to avoid conflicts between your designed API and /apisix/admin, you can modify the configuration file /conf/config.yaml to modify the default listening port.

APISIX supports setting the IP access allowlist of Admin API to prevent APISIX from being illegally accessed and attacked. You can configure the IP addresses to allow access in the deployment.admin.allow_admin option in the ./conf/config.yaml file.

The X-API-KEY shown below refers to the deployment.admin.admin_key.key in the ./conf/config.yaml file, which is the access token for the Admin API.

tip

For security reasons, please modify the default admin_key, and check the allow_admin IP access list.

./conf/config.yaml
deployment:
admin:
admin_key:
- name: admin
key: edd1c9f034335f136f87ad84b625c8f1 # using fixed API token has security risk, please update it when you deploy to production environment
role: admin
allow_admin: # http://nginx.org/en/docs/http/ngx_http_access_module.html#allow
- 127.0.0.0/24
admin_listen:
ip: 0.0.0.0 # Specific IP, if not set, the default value is `0.0.0.0`.
port: 9180 # Specific port, which must be different from node_listen's port.

Using environment variables#

To configure via environment variables, you can use the ${{VAR}} syntax. For instance:

./conf/config.yaml
deployment:
admin:
admin_key:
- name: admin
key: ${{ADMIN_KEY}}
role: admin
allow_admin:
- 127.0.0.0/24
admin_listen:
ip: 0.0.0.0
port: 9180

And then run export ADMIN_KEY=$your_admin_key before running make init.

If the configured environment variable can't be found, an error will be thrown.

If you want to use a default value when the environment variable is not set, use ${{VAR:=default_value}} instead. For instance:

./conf/config.yaml
deployment:
admin:
admin_key:
- name: admin
key: ${{ADMIN_KEY:=edd1c9f034335f136f87ad84b625c8f1}}
role: admin
allow_admin:
- 127.0.0.0/24
admin_listen:
ip: 0.0.0.0
port: 9180

This will find the environment variable ADMIN_KEY first, and if it does not exist, it will use edd1c9f034335f136f87ad84b625c8f1 as the default value.

You can also specify environment variables in yaml keys. This is specifically useful in the standalone mode where you can specify the upstream nodes as follows:

./conf/apisix.yaml
routes:
-
uri: "/test"
upstream:
nodes:
"${{HOST_IP}}:${{PORT}}": 1
type: roundrobin
#END

Force Delete#

By default, the Admin API checks for references between resources and will refuse to delete resources in use.

You can make a force deletion by adding the request argument force=true to the delete request, for example:

$ curl http://127.0.0.1:9180/apisix/admin/upstreams/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '{
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}'
$ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '{
"uri": "/*",
"upstream_id": 1
}'
{"value":{"priority":0,"upstream_id":1,"uri":"/*","create_time":1689038794,"id":"1","status":1,"update_time":1689038916},"key":"/apisix/routes/1"}

$ curl http://127.0.0.1:9180/apisix/admin/upstreams/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X DELETE
{"error_msg":"can not delete this upstream, route [1] is still using it now"}
$ curl "http://127.0.0.1:9180/apisix/admin/upstreams/1?force=anyvalue" -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X DELETE
{"error_msg":"can not delete this upstream, route [1] is still using it now"}
$ curl "http://127.0.0.1:9180/apisix/admin/upstreams/1?force=true" -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X DELETE
{"deleted":"1","key":"/apisix/upstreams/1"}

V3 new feature#

The Admin API has made some breaking changes in V3 version, as well as supporting additional features.

Support new response body format#

  1. Remove action field in response body;
  2. Adjust the response body structure when fetching the list of resources, the new response body structure like:

Return single resource:

{
"modifiedIndex": 2685183,
"value": {
"id": "1",
...
},
"key": "/apisix/routes/1",
"createdIndex": 2684956
}

Return multiple resources:

{
"list": [
{
"modifiedIndex": 2685183,
"value": {
"id": "1",
...
},
"key": "/apisix/routes/1",
"createdIndex": 2684956
},
{
"modifiedIndex": 2685163,
"value": {
"id": "2",
...
},
"key": "/apisix/routes/2",
"createdIndex": 2685163
}
],
"total": 2
}

Support paging query#

Paging query is supported when getting the resource list, paging parameters include:

parameterDefaultValid rangeDescription
page1[1, ...]Number of pages.
page_size[10, 500]Number of resources per page.

The example is as follows:

curl "http://127.0.0.1:9180/apisix/admin/routes?page=1&page_size=10" \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X GET
{
"total": 1,
"list": [
{
...
}
]
}

Resources that support paging queries:

  • Consumer
  • Consumer Group
  • Global Rules
  • Plugin Config
  • Proto
  • Route
  • Service
  • SSL
  • Stream Route
  • Upstream
  • Secret

Support filtering query#

When getting a list of resources, it supports filtering resources based on name, label, uri.

parameterparameter
nameQuery resource by their name, which will not appear in the query results if the resource itself does not have name.
labelQuery resource by their label, which will not appear in the query results if the resource itself does not have label.
uriSupported on Route resources only. If the uri of a Route is equal to the uri of the query or if the uris contains the uri of the query, the Route resource appears in the query results.
tip

When multiple filter parameters are enabled, use the intersection of the query results for different filter parameters.

The following example will return a list of routes, and all routes in the list satisfy: the name of the route contains the string "test", the uri contains the string "foo", and there is no restriction on the label of the route, since the label of the query is the empty string.

curl 'http://127.0.0.1:9180/apisix/admin/routes?name=test&uri=foo&label=' \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X GET
{
"total": 1,
"list": [
{
...
}
]
}

Route#

Routes match the client's request based on defined rules, loads and executes the corresponding plugins, and forwards the request to the specified Upstream.

Route API#

Route resource request address: /apisix/admin/routes/{id}?ttl=0

Quick Note on ID Syntax#

ID's as a text string must be of a length between 1 and 64 characters and they should only contain uppercase, lowercase, numbers and no special characters apart from dashes ( - ), periods ( . ) and underscores ( _ ). For integer values they simply must have a minimum character count of 1.

Request Methods#

MethodRequest URIRequest BodyDescription
GET/apisix/admin/routesNULLFetches a list of all configured Routes.
GET/apisix/admin/routes/{id}NULLFetches specified Route by id.
PUT/apisix/admin/routes/{id}{...}Creates a Route with the specified id.
POST/apisix/admin/routes{...}Creates a Route and assigns a random id.
DELETE/apisix/admin/routes/{id}NULLRemoves the Route with the specified id.
PATCH/apisix/admin/routes/{id}{...}Updates the selected attributes of the specified, existing Route. To delete an attribute, set value of attribute set to null.
PATCH/apisix/admin/routes/{id}/{path}{...}Updates the attribute specified in the path. The values of other attributes remain unchanged.

URI Request Parameters#

parameterRequiredTypeDescriptionExample
ttlFalseAuxiliaryRequest expires after the specified target seconds.ttl=1

Request Body Parameters#

ParameterRequiredTypeDescriptionExample
nameFalseAuxiliaryIdentifier for the Route.route-xxxx
descFalseAuxiliaryDescription of usage scenarios.route xxxx
uriTrue, can't be used with urisMatch RulesMatches the uri. For more advanced matching see Router."/hello"
urisTrue, can't be used with uriMatch RulesMatches with any one of the multiple uris specified in the form of a non-empty list.["/hello", "/word"]
hostFalse, can't be used with hostsMatch RulesMatches with domain names such as foo.com or PAN domain names like *.foo.com."foo.com"
hostsFalse, can't be used with hostMatch RulesMatches with any one of the multiple hosts specified in the form of a non-empty list.["foo.com", "*.bar.com"]
remote_addrFalse, can't be used with remote_addrsMatch RulesMatches with the specified IP address in standard IPv4 format (192.168.1.101), CIDR format (192.168.1.0/24), or in IPv6 format (::1, fe80::1, fe80::1/64)."192.168.1.0/24"
remote_addrsFalse, can't be used with remote_addrMatch RulesMatches with any one of the multiple remote_addrs specified in the form of a non-empty list.["127.0.0.1", "192.0.0.0/8", "::1"]
methodsFalseMatch RulesMatches with the specified methods. Matches all methods if empty or unspecified.["GET", "POST"]
priorityFalseMatch RulesIf different Routes matches to the same uri, then the Route is matched based on its priority. A higher value corresponds to higher priority. It is set to 0 by default.priority = 10
varsFalseMatch RulesMatches based on the specified variables consistent with variables in Nginx. Takes the form [[var, operator, val], [var, operator, val], ...]]. Note that this is case sensitive when matching a cookie name. See lua-resty-expr for more details.[["arg_name", "==", "json"], ["arg_age", ">", 18]]
filter_funcFalseMatch RulesMatches using a user-defined function in Lua. Used in scenarios where vars is not sufficient. Functions accept an argument vars which provides access to built-in variables (including Nginx variables).function(vars) return tonumber(vars.arg_userid) % 4 > 2; end
pluginsFalsePluginPlugins that are executed during the request/response cycle. See Plugin for more.
scriptFalseScriptUsed for writing arbitrary Lua code or directly calling existing plugins to be executed. See Script for more.
upstreamFalseUpstreamConfiguration of the Upstream.
upstream_idFalseUpstreamId of the Upstream service.
service_idFalseServiceConfiguration of the bound Service.
plugin_config_idFalse, can't be used with scriptPluginPlugin config bound to the Route.
labelsFalseMatch RulesAttributes of the Route specified as key-value pairs.{"version":"v2","build":"16","env":"production"}
timeoutFalseAuxiliarySets the timeout (in seconds) for connecting to, and sending and receiving messages between the Upstream and the Route. This will overwrite the timeout value configured in your Upstream.{"connect": 3, "send": 3, "read": 3}
enable_websocketFalseAuxiliaryEnables a websocket. Set to false by default.
statusFalseAuxiliaryEnables the current Route. Set to 1 (enabled) by default.1 to enable, 0 to disable

Example configuration:

{
"id": "1", # id, unnecessary.
"uris": ["/a","/b"], # A set of uri.
"methods": ["GET","POST"], # Can fill multiple methods
"hosts": ["a.com","b.com"], # A set of host.
"plugins": {}, # Bound plugin
"priority": 0, # If different routes contain the same `uri`, determine which route is matched first based on the attribute` priority`, the default value is 0.
"name": "route-xxx",
"desc": "hello world",
"remote_addrs": ["127.0.0.1"], # A set of Client IP.
"vars": [["http_user", "==", "ios"]], # A list of one or more `[var, operator, val]` elements
"upstream_id": "1", # upstream id, recommended
"upstream": {}, # upstream, not recommended
"timeout": { # Set the upstream timeout for connecting, sending and receiving messages of the route.
"connect": 3,
"send": 3,
"read": 3
},
"filter_func": "" # User-defined filtering function
}

Example API usage#

  • Create a route

    curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '
    {
    "uri": "/index.html",
    "hosts": ["foo.com", "*.bar.com"],
    "remote_addrs": ["127.0.0.0/8"],
    "methods": ["PUT", "GET"],
    "enable_websocket": true,
    "upstream": {
    "type": "roundrobin",
    "nodes": {
    "127.0.0.1:1980": 1
    }
    }
    }'
    HTTP/1.1 201 Created
    Date: Sat, 31 Aug 2019 01:17:15 GMT
    ...
  • Create a route expires after 60 seconds, then it's deleted automatically

    curl 'http://127.0.0.1:9180/apisix/admin/routes/2?ttl=60' \
    -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '
    {
    "uri": "/aa/index.html",
    "upstream": {
    "type": "roundrobin",
    "nodes": {
    "127.0.0.1:1980": 1
    }
    }
    }'
    HTTP/1.1 201 Created
    Date: Sat, 31 Aug 2019 01:17:15 GMT
    ...
  • Add an upstream node to the Route

    curl http://127.0.0.1:9180/apisix/admin/routes/1 \
    -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PATCH -i -d '
    {
    "upstream": {
    "nodes": {
    "127.0.0.1:1981": 1
    }
    }
    }'
    HTTP/1.1 200 OK
    ...

    After successful execution, upstream nodes will be updated to:

    {
    "127.0.0.1:1980": 1,
    "127.0.0.1:1981": 1
    }
  • Update the weight of an upstream node to the Route

    curl http://127.0.0.1:9180/apisix/admin/routes/1 \
    -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PATCH -i -d '
    {
    "upstream": {
    "nodes": {
    "127.0.0.1:1981": 10
    }
    }
    }'
    HTTP/1.1 200 OK
    ...

    After successful execution, upstream nodes will be updated to:

    {
    "127.0.0.1:1980": 1,
    "127.0.0.1:1981": 10
    }
  • Delete an upstream node for the Route

    curl http://127.0.0.1:9180/apisix/admin/routes/1 \
    -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PATCH -i -d '
    {
    "upstream": {
    "nodes": {
    "127.0.0.1:1980": null
    }
    }
    }'
    HTTP/1.1 200 OK
    ...

    After successful execution, upstream nodes will be updated to:

    {
    "127.0.0.1:1981": 10
    }
  • Replace methods of the Route -- array

    curl http://127.0.0.1:9180/apisix/admin/routes/1 \
    -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PATCH -i -d '{
    "methods": ["GET", "POST"]
    }'
    HTTP/1.1 200 OK
    ...

    After successful execution, methods will not retain the original data, and the entire update is:

    ["GET", "POST"]
  • Replace upstream nodes of the Route -- sub path

    curl http://127.0.0.1:9180/apisix/admin/routes/1/upstream/nodes \
    -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PATCH -i -d '
    {
    "127.0.0.1:1982": 1
    }'
    HTTP/1.1 200 OK
    ...

    After successful execution, nodes will not retain the original data, and the entire update is:

    {
    "127.0.0.1:1982": 1
    }
  • Replace methods of the Route -- sub path

    curl http://127.0.0.1:9180/apisix/admin/routes/1/methods \
    -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PATCH -i -d'["POST", "DELETE", " PATCH"]'
    HTTP/1.1 200 OK
    ...

    After successful execution, methods will not retain the original data, and the entire update is:

    ["POST", "DELETE", "PATCH"]
  • Disable route

    curl http://127.0.0.1:9180/apisix/admin/routes/1 \
    -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PATCH -i -d '
    {
    "status": 0
    }'
    HTTP/1.1 200 OK
    ...

    After successful execution, status nodes will be updated to:

    {
    "status": 0
    }
  • Enable route

    curl http://127.0.0.1:9180/apisix/admin/routes/1 \
    -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PATCH -i -d '
    {
    "status": 1
    }'
    HTTP/1.1 200 OK
    ...

    After successful execution, status nodes will be updated to:

    {
    "status": 1
    }

Response Parameters#

Currently, the response is returned from etcd.

Service#

A Service is an abstraction of an API (which can also be understood as a set of Route abstractions). It usually corresponds to an upstream service abstraction.

The relationship between Routes and a Service is usually N:1.

Service API#

Service resource request address: /apisix/admin/services/{id}

Request Methods#

MethodRequest URIRequest BodyDescription
GET/apisix/admin/servicesNULLFetches a list of available Services.
GET/apisix/admin/services/{id}NULLFetches specified Service by id.
PUT/apisix/admin/services/{id}{...}Creates a Service with the specified id.
POST/apisix/admin/services{...}Creates a Service and assigns a random id.
DELETE/apisix/admin/services/{id}NULLRemoves the Service with the specified id.
PATCH/apisix/admin/services/{id}{...}Updates the selected attributes of the specified, existing Service. To delete an attribute, set value of attribute set to null.
PATCH/apisix/admin/services/{id}/{path}{...}Updates the attribute specified in the path. The values of other attributes remain unchanged.

Request Body Parameters#

ParameterRequiredTypeDescriptionExample
pluginsFalsePluginPlugins that are executed during the request/response cycle. See Plugin for more.
upstreamFalseUpstreamConfiguration of the Upstream.
upstream_idFalseUpstreamId of the Upstream service.
nameFalseAuxiliaryIdentifier for the Service.service-xxxx
descFalseAuxiliaryDescription of usage scenarios.service xxxx
labelsFalseMatch RulesAttributes of the Service specified as key-value pairs.{"version":"v2","build":"16","env":"production"}
enable_websocketFalseAuxiliaryEnables a websocket. Set to false by default.
hostsFalseMatch RulesMatches with any one of the multiple hosts specified in the form of a non-empty list.["foo.com", "*.bar.com"]

Example configuration:

{
"id": "1", # id
"plugins": {}, # Bound plugin
"upstream_id": "1", # upstream id, recommended
"upstream": {}, # upstream, not recommended
"name": "service-test",
"desc": "hello world",
"enable_websocket": true,
"hosts": ["foo.com"]
}

Example API usage#

  • Create a service

    curl http://127.0.0.1:9180/apisix/admin/services/201  \
    -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '
    {
    "plugins": {
    "limit-count": {
    "count": 2,
    "time_window": 60,
    "rejected_code": 503,
    "key": "remote_addr"
    }
    },
    "enable_websocket": true,
    "upstream": {
    "type": "roundrobin",
    "nodes": {
    "127.0.0.1:1980": 1
    }
    }
    }'
    HTTP/1.1 201 Created
    ...
  • Add an upstream node to the Service

    curl http://127.0.0.1:9180/apisix/admin/services/201 \
    -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PATCH -i -d '
    {
    "upstream": {
    "nodes": {
    "127.0.0.1:1981": 1
    }
    }
    }'
    HTTP/1.1 200 OK
    ...

    After successful execution, upstream nodes will be updated to:

    {
    "127.0.0.1:1980": 1,
    "127.0.0.1:1981": 1
    }
  • Update the weight of an upstream node to the Service

    curl http://127.0.0.1:9180/apisix/admin/services/201 \
    -H'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PATCH -i -d '
    {
    "upstream": {
    "nodes": {
    "127.0.0.1:1981": 10
    }
    }
    }'
    HTTP/1.1 200 OK
    ...

    After successful execution, upstream nodes will be updated to:

    {
    "127.0.0.1:1980": 1,
    "127.0.0.1:1981": 10
    }
  • Delete an upstream node for the Service

    curl http://127.0.0.1:9180/apisix/admin/services/201 \
    -H'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PATCH -i -d '
    {
    "upstream": {
    "nodes": {
    "127.0.0.1:1980": null
    }
    }
    }'
    HTTP/1.1 200 OK
    ...

    After successful execution, upstream nodes will be updated to:

    {
    "127.0.0.1:1981": 10
    }
  • Replace upstream nodes of the Service

    curl http://127.0.0.1:9180/apisix/admin/services/201/upstream/nodes \
    -H'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PATCH -i -d '
    {
    "127.0.0.1:1982": 1
    }'
    HTTP/1.1 200 OK
    ...

    After successful execution, upstream nodes will not retain the original data, and the entire update is:

    {
    "127.0.0.1:1982": 1
    }

Response Parameters#

Currently, the response is returned from etcd.

Consumer#

Consumers are users of services and can only be used in conjunction with a user authentication system. A Consumer is identified by a username property. So, for creating a new Consumer, only the HTTP PUT method is supported.

Consumer API#

Consumer resource request address: /apisix/admin/consumers/{username}

Request Methods#

MethodRequest URIRequest BodyDescription
GET/apisix/admin/consumersNULLFetches a list of all Consumers.
GET/apisix/admin/consumers/{username}NULLFetches specified Consumer by username.
PUT/apisix/admin/consumers{...}Create new Consumer.
DELETE/apisix/admin/consumers/{username}NULLRemoves the Consumer with the specified username.

Request Body Parameters#

ParameterRequiredTypeDescriptionExample
usernameTrueNameName of the Consumer.
group_idFalseNameGroup of the Consumer.
pluginsFalsePluginPlugins that are executed during the request/response cycle. See Plugin for more.
descFalseAuxiliaryDescription of usage scenarios.customer xxxx
labelsFalseMatch RulesAttributes of the Consumer specified as key-value pairs.{"version":"v2","build":"16","env":"production"}

Example Configuration:

{
"plugins": {}, # Bound plugin
"username": "name", # Consumer name
"desc": "hello world" # Consumer desc
}

When bound to a Route or Service, the Authentication Plugin infers the Consumer from the request and does not require any parameters. Whereas, when it is bound to a Consumer, username, password and other information needs to be provided.

Example API usage#

curl http://127.0.0.1:9180/apisix/admin/consumers  \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '
{
"username": "jack",
"plugins": {
"key-auth": {
"key": "auth-one"
},
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}
}'
HTTP/1.1 200 OK
Date: Thu, 26 Dec 2019 08:17:49 GMT
...

{"node":{"value":{"username":"jack","plugins":{"key-auth":{"key":"auth-one"},"limit-count":{"time_window":60,"count":2,"rejected_code":503,"key":"remote_addr","policy":"local"}}},"createdIndex":64,"key":"\/apisix\/consumers\/jack","modifiedIndex":64},"prevNode":{"value":"{\"username\":\"jack\",\"plugins\":{\"key-auth\":{\"key\":\"auth-one\"},\"limit-count\":{\"time_window\":60,\"count\":2,\"rejected_code\":503,\"key\":\"remote_addr\",\"policy\":\"local\"}}}","createdIndex":63,"key":"\/apisix\/consumers\/jack","modifiedIndex":63}}

Since v2.2, we can bind multiple authentication plugins to the same consumer.

Response Parameters#

Currently, the response is returned from etcd.

Upstream#

Upstream is a virtual host abstraction that performs load balancing on a given set of service nodes according to the configured rules.

An Upstream configuration can be directly bound to a Route or a Service, but the configuration in Route has a higher priority. This behavior is consistent with priority followed by the Plugin object.

Upstream API#

Upstream resource request address: /apisix/admin/upstreams/{id}

For notes on ID syntax please refer to: ID Syntax

Request Methods#

MethodRequest URIRequest BodyDescription
GET/apisix/admin/upstreamsNULLFetch a list of all configured Upstreams.
GET/apisix/admin/upstreams/{id}NULLFetches specified Upstream by id.
PUT/apisix/admin/upstreams/{id}{...}Creates an Upstream with the specified id.
POST/apisix/admin/upstreams{...}Creates an Upstream and assigns a random id.
DELETE/apisix/admin/upstreams/{id}NULLRemoves the Upstream with the specified id.
PATCH/apisix/admin/upstreams/{id}{...}Updates the selected attributes of the specified, existing Upstream. To delete an attribute, set value of attribute set to null.
PATCH/apisix/admin/upstreams/{id}/{path}{...}Updates the attribute specified in the path. The values of other attributes remain unchanged.

Request Body Parameters#

In addition to the equalization algorithm selections, Upstream also supports passive health check and retry for the upstream. See the table below for more details:

ParameterRequiredTypeDescriptionExample
typeFalseEnumerationLoad balancing algorithm to be used, and the default value is roundrobin.
nodesTrue, can't be used with service_nameNodeIP addresses (with optional ports) of the Upstream nodes represented as a hash table or an array. In the hash table, the key is the IP address and the value is the weight of the node for the load balancing algorithm. For hash table case, if the key is IPv6 address with port, then the IPv6 address must be quoted with square brackets. In the array, each item is a hash table with keys host, weight, and the optional port and priority (defaults to 0). Nodes with lower priority are used only when all nodes with a higher priority are tried and are unavailable. Empty nodes are treated as placeholders and clients trying to access this Upstream will receive a 502 response.192.168.1.100:80, [::1]:80
service_nameTrue, can't be used with nodesStringService name used for service discovery.a-bootiful-client
discovery_typeTrue, if service_name is usedStringThe type of service discovery.eureka
hash_onFalseAuxiliaryOnly valid if the type is chash. Supports Nginx variables (vars), custom headers (header), cookie and consumer. Defaults to vars.
keyFalseMatch RulesOnly valid if the type is chash. Finds the corresponding node id according to hash_on and key values. When hash_on is set to vars, key is a required parameter and it supports Nginx variables. When hash_on is set as header, key is a required parameter, and header name can be customized. When hash_on is set to cookie, key is also a required parameter, and cookie name can be customized. When hash_on is set to consumer, key need not be set and the key used by the hash algorithm would be the authenticated consumer_name.uri, server_name, server_addr, request_uri, remote_port, remote_addr, query_string, host, hostname, arg_***, arg_***
checksFalseHealth CheckerConfigures the parameters for the health check.
retriesFalseIntegerSets the number of retries while passing the request to Upstream using the underlying Nginx mechanism. Set according to the number of available backend nodes by default. Setting this to 0 disables retry.
retry_timeoutFalseIntegerTimeout to continue with retries. Setting this to 0 disables the retry timeout.
timeoutFalseTimeoutSets the timeout (in seconds) for connecting to, and sending and receiving messages to and from the Upstream.{"connect": 0.5,"send": 0.5,"read": 0.5}
nameFalseAuxiliaryIdentifier for the Upstream.
descFalseAuxiliaryDescription of usage scenarios.
pass_hostFalseEnumerationConfigures the host when the request is forwarded to the upstream. Can be one of pass, node or rewrite. Defaults to pass if not specified. pass- transparently passes the client's host to the Upstream. node- uses the host configured in the node of the Upstream. rewrite- Uses the value configured in upstream_host.
upstream_hostFalseAuxiliarySpecifies the host of the Upstream request. This is only valid if the pass_host is set to rewrite.
schemeFalseAuxiliaryThe scheme used when communicating with the Upstream. For an L7 proxy, this value can be one of http, https, grpc, grpcs. For an L4 proxy, this value could be one of tcp, udp, tls. Defaults to http.
labelsFalseMatch RulesAttributes of the Upstream specified as key-value pairs.{"version":"v2","build":"16","env":"production"}
tls.client_certFalse, can't be used with tls.client_cert_idHTTPS certificateSets the client certificate while connecting to a TLS Upstream.
tls.client_keyFalse, can't be used with tls.client_cert_idHTTPS certificate private keySets the client private key while connecting to a TLS Upstream.
tls.client_cert_idFalse, can't be used with tls.client_cert and tls.client_keySSLSet the referenced SSL id.
keepalive_pool.sizeFalseAuxiliarySets keepalive directive dynamically.
keepalive_pool.idle_timeoutFalseAuxiliarySets keepalive_timeout directive dynamically.
keepalive_pool.requestsFalseAuxiliarySets keepalive_requests directive dynamically.

An Upstream can be one of the following types:

  • roundrobin: Round robin balancing with weights.
  • chash: Consistent hash.
  • ewma: Pick the node with minimum latency. See EWMA Chart for more details.
  • least_conn: Picks the node with the lowest value of (active_conn + 1) / weight. Here, an active connection is a connection being used by the request and is similar to the concept in Nginx.
  • user-defined load balancer loaded via require("apisix.balancer.your_balancer").

The following should be considered when setting the hash_on value:

  • When set to vars, a key is required. The value of the key can be any of the Nginx variables without the $ prefix.
  • When set to header, a key is required. This is equal to "http_key".
  • When set to cookie, a key is required. This key is equal to "cookie_key". The cookie name is case-sensitive.
  • When set to consumer, the key is optional and the key is set to the consumer_name captured from the authentication Plugin.
  • When set to vars_combinations, the key is required. The value of the key can be a combination of any of the Nginx variables like $request_uri$remote_addr.

The features described below requires APISIX to be run on APISIX-Runtime:

You can set the scheme to tls, which means "TLS over TCP".

To use mTLS to communicate with Upstream, you can use the tls.client_cert/key in the same format as SSL's cert and key fields.

Or you can reference SSL object by tls.client_cert_id to set SSL cert and key. The SSL object can be referenced only if the type field is client, otherwise the request will be rejected by APISIX. In addition, only cert and key will be used in the SSL object.

To allow Upstream to have a separate connection pool, use keepalive_pool. It can be configured by modifying its child fields.

Example Configuration:

{
"id": "1", # id
"retries": 1, # retry times
"timeout": { # Set the timeout for connecting, sending and receiving messages, each is 15 seconds.
"connect":15,
"send":15,
"read":15
},
"nodes": {"host:80": 100}, # Upstream machine address list, the format is `Address + Port`
# is the same as "nodes": [ {"host": "host", "port": 80, "weight": 100} ],
"type":"roundrobin",
"checks": {}, # Health check parameters
"hash_on": "",
"key": "",
"name": "upstream-for-test",
"desc": "hello world",
"scheme": "http" # The scheme used when communicating with upstream, the default is `http`
}

Example API usage#

Create an Upstream and modify the data in nodes#

  1. Create upstream

    curl http://127.0.0.1:9180/apisix/admin/upstreams/100  \
    -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -i -X PUT -d '
    {
    "type":"roundrobin",
    "nodes":{
    "127.0.0.1:1980": 1
    }
    }'
    HTTP/1.1 201 Created
    ...
  2. Add a node to the Upstream

    curl http://127.0.0.1:9180/apisix/admin/upstreams/100 \
    -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PATCH -i -d '
    {
    "nodes": {
    "127.0.0.1:1981": 1
    }
    }'
    HTTP/1.1 200 OK
    ...

    After successful execution, nodes will be updated to:

    {
    "127.0.0.1:1980": 1,
    "127.0.0.1:1981": 1
    }
  3. Update the weight of a node to the Upstream

    curl http://127.0.0.1:9180/apisix/admin/upstreams/100 \
    -H'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PATCH -i -d '
    {
    "nodes": {
    "127.0.0.1:1981": 10
    }
    }'
    HTTP/1.1 200 OK
    ...

    After successful execution, nodes will be updated to:

    {
    "127.0.0.1:1980": 1,
    "127.0.0.1:1981": 10
    }
  4. Delete a node for the Upstream

    curl http://127.0.0.1:9180/apisix/admin/upstreams/100 \
    -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PATCH -i -d '
    {
    "nodes": {
    "127.0.0.1:1980": null
    }
    }'
    HTTP/1.1 200 OK
    ...

    After successful execution, nodes will be updated to:

    {
    "127.0.0.1:1981": 10
    }
  5. Replace the nodes of the Upstream

    curl http://127.0.0.1:9180/apisix/admin/upstreams/100/nodes \
    -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PATCH -i -d '
    {
    "127.0.0.1:1982": 1
    }'
    HTTP/1.1 200 OK
    ...

    After the execution is successful, nodes will not retain the original data, and the entire update is:

    {
    "127.0.0.1:1982": 1
    }

Proxy client request to https Upstream service#

  1. Create a route and configure the upstream scheme as https.

    curl -i http://127.0.0.1:9180/apisix/admin/routes/1 \
    -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
    {
    "uri": "/get",
    "upstream": {
    "type": "roundrobin",
    "scheme": "https",
    "nodes": {
    "httpbin.org:443": 1
    }
    }
    }'

    After successful execution, the scheme when requesting to communicate with the upstream will be https.

  2. Send a request to test.

    curl http://127.0.0.1:9080/get
    {
    "args": {},
    "headers": {
    "Accept": "*/*",
    "Host": "127.0.0.1",
    "User-Agent": "curl/7.29.0",
    "X-Amzn-Trace-Id": "Root=1-6058324a-0e898a7f04a5e95b526bb183",
    "X-Forwarded-Host": "127.0.0.1"
    },
    "origin": "127.0.0.1",
    "url": "https://127.0.0.1/get"
    }

The request is successful, meaning that the proxy Upstream https is valid.

note

Each node can be configured with a priority. A node with low priority will only be used when all the nodes with higher priority have been tried or are unavailable.

As the default priority is 0, nodes with negative priority can be configured as a backup.

For example:

{
"uri": "/hello",
"upstream": {
"type": "roundrobin",
"nodes": [
{ "host": "127.0.0.1", "port": 1980, "weight": 2000 },
{ "host": "127.0.0.1", "port": 1981, "weight": 1, "priority": -1 }
],
"checks": {
"active": {
"http_path": "/status",
"healthy": {
"interval": 1,
"successes": 1
},
"unhealthy": {
"interval": 1,
"http_failures": 1
}
}
}
}
}

Node 127.0.0.2 will be used only after 127.0.0.1 is tried or unavailable. It can therefore act as a backup for the node 127.0.0.1.

Response Parameters#

Currently, the response is returned from etcd.

SSL#

SSL API#

SSL resource request address: /apisix/admin/ssls/{id}

For notes on ID syntax please refer to: ID Syntax

Request Methods#

MethodRequest URIRequest BodyDescription
GET/apisix/admin/sslsNULLFetches a list of all configured SSL resources.
GET/apisix/admin/ssls/{id}NULLFetch specified resource by id.
PUT/apisix/admin/ssls/{id}{...}Creates a resource with the specified id.
POST/apisix/admin/ssls{...}Creates a resource and assigns a random id.
DELETE/apisix/admin/ssls/{id}NULLRemoves the resource with the specified id.

Request Body Parameters#

ParameterRequiredTypeDescriptionExample
certTrueCertificateHTTPS certificate. This field supports saving the value in Secret Manager using the APISIX Secret resource.
keyTruePrivate keyHTTPS private key. This field supports saving the value in Secret Manager using the APISIX Secret resource.
certsFalseAn array of certificatesUsed for configuring multiple certificates for the same domain excluding the one provided in the cert field.
keysFalseAn array of private keysPrivate keys to pair with the certs.
client.caFalseCertificateSets the CA certificate that verifies the client. Requires OpenResty 1.19+.
client.depthFalseCertificateSets the verification depth in client certificate chains. Defaults to 1. Requires OpenResty 1.19+.
client.skip_mtls_uri_regexFalseAn array of regular expressions, in PCRE formatUsed to match URI, if matched, this request bypasses the client certificate checking, i.e. skip the MTLS.["/hello[0-9]+", "/foobar"]
snisTrue, only if type is serverMatch RulesA non-empty array of HTTPS SNI
labelsFalseMatch RulesAttributes of the resource specified as key-value pairs.{"version":"v2","build":"16","env":"production"}
typeFalseAuxiliaryIdentifies the type of certificate, default server.client Indicates that the certificate is a client certificate, which is used when APISIX accesses the upstream; server Indicates that the certificate is a server-side certificate, which is used by APISIX when verifying client requests.
statusFalseAuxiliaryEnables the current SSL. Set to 1 (enabled) by default.1 to enable, 0 to disable
ssl_protocolsFalseAn array of ssl protocolsIt is used to control the SSL/TLS protocol version used between servers and clients. See SSL Protocol for more examples.["TLSv1.2", "TLSv2.3"]

Example Configuration:

{
"id": "1", # id
"cert": "cert", # Certificate
"key": "key", # Private key
"snis": ["t.com"] # https SNI
}

See Certificate for more examples.

Global Rule#

Sets Plugins which run globally. i.e these Plugins will be run before any Route/Service level Plugins.

Global Rule API#

Global Rule resource request address: /apisix/admin/global_rules/{id}

Request Methods#

MethodRequest URIRequest BodyDescription
GET/apisix/admin/global_rulesNULLFetches a list of all Global Rules.
GET/apisix/admin/global_rules/{id}NULLFetches specified Global Rule by id.
PUT/apisix/admin/global_rules/{id}{...}Creates a Global Rule with the specified id.
DELETE/apisix/admin/global_rules/{id}NULLRemoves the Global Rule with the specified id.
PATCH/apisix/admin/global_rules/{id}{...}Updates the selected attributes of the specified, existing Global Rule. To delete an attribute, set value of attribute set to null.
PATCH/apisix/admin/global_rules/{id}/{path}{...}Updates the attribute specified in the path. The values of other attributes remain unchanged.

Request Body Parameters#

ParameterRequiredDescriptionExample
pluginsTruePlugins that are executed during the request/response cycle. See Plugin for more.

Consumer group#

Group of Plugins which can be reused across Consumers.

Consumer group API#

Consumer group resource request address: /apisix/admin/consumer_groups/{id}

Request Methods#

MethodRequest URIRequest BodyDescription
GET/apisix/admin/consumer_groupsNULLFetches a list of all Consumer groups.
GET/apisix/admin/consumer_groups/{id}NULLFetches specified Consumer group by id.
PUT/apisix/admin/consumer_groups/{id}{...}Creates a new Consumer group with the specified id.
DELETE/apisix/admin/consumer_groups/{id}NULLRemoves the Consumer group with the specified id.
PATCH/apisix/admin/consumer_groups/{id}{...}Updates the selected attributes of the specified, existing Consumer group. To delete an attribute, set value of attribute set to null.
PATCH/apisix/admin/consumer_groups/{id}/{path}{...}Updates the attribute specified in the path. The values of other attributes remain unchanged.

Request Body Parameters#

ParameterRequiredDescriptionExample
pluginsTruePlugins that are executed during the request/response cycle. See Plugin for more.
descFalseDescription of usage scenarios.customer xxxx
labelsFalseAttributes of the Consumer group specified as key-value pairs.{"version":"v2","build":"16","env":"production"}

Plugin config#

Group of Plugins which can be reused across Routes.

Plugin Config API#

Plugin Config resource request address: /apisix/admin/plugin_configs/{id}

Request Methods#

MethodRequest URIRequest BodyDescription
GET/apisix/admin/plugin_configsNULLFetches a list of all Plugin configs.
GET/apisix/admin/plugin_configs/{id}NULLFetches specified Plugin config by id.
PUT/apisix/admin/plugin_configs/{id}{...}Creates a new Plugin config with the specified id.
DELETE/apisix/admin/plugin_configs/{id}NULLRemoves the Plugin config with the specified id.
PATCH/apisix/admin/plugin_configs/{id}{...}Updates the selected attributes of the specified, existing Plugin config. To delete an attribute, set value of attribute set to null.
PATCH/apisix/admin/plugin_configs/{id}/{path}{...}Updates the attribute specified in the path. The values of other attributes remain unchanged.

Request Body Parameters#

ParameterRequiredDescriptionExample
pluginsTruePlugins that are executed during the request/response cycle. See Plugin for more.
descFalseDescription of usage scenarios.customer xxxx
labelsFalseAttributes of the Plugin config specified as key-value pairs.{"version":"v2","build":"16","env":"production"}

Plugin Metadata#

Plugin Metadata API#

Plugin Metadata resource request address: /apisix/admin/plugin_metadata/{plugin_name}

Request Methods#

MethodRequest URIRequest BodyDescription
GET/apisix/admin/plugin_metadata/{plugin_name}NULLFetches the metadata of the specified Plugin by plugin_name.
PUT/apisix/admin/plugin_metadata/{plugin_name}{...}Creates metadata for the Plugin specified by the plugin_name.
DELETE/apisix/admin/plugin_metadata/{plugin_name}NULLRemoves metadata for the Plugin specified by the plugin_name.

Request Body Parameters#

A JSON object defined according to the metadata_schema of the Plugin ({plugin_name}).

Example Configuration:

curl http://127.0.0.1:9180/apisix/admin/plugin_metadata/example-plugin  \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -i -X PUT -d '
{
"skey": "val",
"ikey": 1
}'
HTTP/1.1 201 Created
Date: Thu, 26 Dec 2019 04:19:34 GMT
Content-Type: text/plain

Plugin#

Plugin API#

Plugin resource request address: /apisix/admin/plugins/{plugin_name}

Request Methods#

MethodRequest URIRequest BodyDescription
GET/apisix/admin/plugins/listNULLFetches a list of all Plugins.
GET/apisix/admin/plugins/{plugin_name}NULLFetches the specified Plugin by plugin_name.
GET/apisix/admin/plugins?all=trueNULLGet all properties of all plugins.
GET/apisix/admin/plugins?all=true&subsystem=streamNULLGets properties of all Stream plugins.
GET/apisix/admin/plugins?all=true&subsystem=httpNULLGets properties of all HTTP plugins.
PUT/apisix/admin/plugins/reloadNULLReloads the plugin according to the changes made in code
GETapisix/admin/plugins/{plugin_name}?subsystem=streamNULLGets properties of a specified plugin if it is supported in Stream/L4 subsystem.
GETapisix/admin/plugins/{plugin_name}?subsystem=httpNULLGets properties of a specified plugin if it is supported in HTTP/L7 subsystem.
caution

The interface of getting properties of all plugins via /apisix/admin/plugins?all=true will be deprecated soon.

Request Body Parameters#

The Plugin ({plugin_name}) of the data structure.

Request Arguments#

NameDescriptionDefault
subsystemThe subsystem of the Plugins.http

The plugin can be filtered on subsystem so that the ({plugin_name}) is searched in the subsystem passed through query params.

Example API usage:#

curl "http://127.0.0.1:9180/apisix/admin/plugins/list" \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1'
["zipkin","request-id",...]
curl "http://127.0.0.1:9180/apisix/admin/plugins/key-auth?subsystem=http" -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1'
{"$comment":"this is a mark for our injected plugin schema","properties":{"header":{"default":"apikey","type":"string"},"hide_credentials":{"default":false,"type":"boolean"},"_meta":{"properties":{"filter":{"type":"array","description":"filter determines whether the plugin needs to be executed at runtime"},"disable":{"type":"boolean"},"error_response":{"oneOf":[{"type":"string"},{"type":"object"}]},"priority":{"type":"integer","description":"priority of plugins by customized order"}},"type":"object"},"query":{"default":"apikey","type":"string"}},"type":"object"}
tip

You can use the /apisix/admin/plugins?all=true API to get all properties of all plugins. This API will be deprecated soon.

Stream Route#

Route used in the Stream Proxy.

Stream Route API#

Stream Route resource request address: /apisix/admin/stream_routes/{id}

Request Methods#

MethodRequest URIRequest BodyDescription
GET/apisix/admin/stream_routesNULLFetches a list of all configured Stream Routes.
GET/apisix/admin/stream_routes/{id}NULLFetches specified Stream Route by id.
PUT/apisix/admin/stream_routes/{id}{...}Creates a Stream Route with the specified id.
POST/apisix/admin/stream_routes{...}Creates a Stream Route and assigns a random id.
DELETE/apisix/admin/stream_routes/{id}NULLRemoves the Stream Route with the specified id.

Request Body Parameters#

ParameterRequiredTypeDescriptionExample
upstreamFalseUpstreamConfiguration of the Upstream.
upstream_idFalseUpstreamId of the Upstream service.
service_idFalseStringId of the Service service.
remote_addrFalseIPv4, IPv4 CIDR, IPv6Filters Upstream forwards by matching with client IP."127.0.0.1" or "127.0.0.1/32" or "::1"
server_addrFalseIPv4, IPv4 CIDR, IPv6Filters Upstream forwards by matching with APISIX Server IP."127.0.0.1" or "127.0.0.1/32" or "::1"
server_portFalseIntegerFilters Upstream forwards by matching with APISIX Server port.9090
sniFalseHostServer Name Indication."test.com"
protocol.nameFalseStringName of the protocol proxyed by xRPC framework."redis"
protocol.confFalseConfigurationProtocol-specific configuration.

To learn more about filtering in stream proxies, check this document.

Secret#

Secret means Secrets Management, which could use any secret manager supported, e.g. vault.

Secret API#

Secret resource request address: /apisix/admin/secrets/{secretmanager}/{id}

Request Methods#

MethodRequest URIRequest BodyDescription
GET/apisix/admin/secretsNULLFetches a list of all secrets.
GET/apisix/admin/secrets/{manager}/{id}NULLFetches specified secrets by id.
PUT/apisix/admin/secrets/{manager}{...}Create new secrets configuration.
DELETE/apisix/admin/secrets/{manager}/{id}NULLRemoves the secrets with the specified id.
PATCH/apisix/admin/secrets/{manager}/{id}{...}Updates the selected attributes of the specified, existing secrets. To delete an attribute, set value of attribute set to null.
PATCH/apisix/admin/secrets/{manager}/{id}/{path}{...}Updates the attribute specified in the path. The values of other attributes remain unchanged.

Request Body Parameters#

When {secretmanager} is vault:

ParameterRequiredTypeDescriptionExample
uriTrueURIURI of the vault server.
prefixTruestringkey prefix
tokenTruestringvault token.

Example Configuration:

{
"uri": "https://localhost/vault",
"prefix": "/apisix/kv",
"token": "343effad"
}

Example API usage:

curl -i http://127.0.0.1:9180/apisix/admin/secrets/vault/test2 \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"uri": "http://xxx/get",
"prefix" : "apisix",
"token" : "apisix"
}'
HTTP/1.1 200 OK
...

{"key":"\/apisix\/secrets\/vault\/test2","value":{"id":"vault\/test2","token":"apisix","prefix":"apisix","update_time":1669625828,"create_time":1669625828,"uri":"http:\/\/xxx\/get"}}

Response Parameters#

Currently, the response is returned from etcd.

Proto#

Proto is used to store protocol buffers so that APISIX can communicate in gRPC.

See grpc-transcode plugin doc for more examples.

Proto API#

Proto resource request address: /apisix/admin/protos/{id}

Request Methods#

MethodRequest URIRequest BodyDescription
GET/apisix/admin/protosNULLList all Protos.
GET/apisix/admin/protos/{id}NULLGet a Proto by id.
PUT/apisix/admin/protos/{id}{...}Create or update a Proto with the given id.
POST/apisix/admin/protos{...}Create a Proto with a random id.
DELETE/apisix/admin/protos/{id}NULLDelete Proto by id.

Request Body Parameters#

ParameterRequiredTypeDescriptionExample
contentTrueStringcontent of .proto or .pb filesSee here

Schema validation#

Check the validity of a configuration against its entity schema. This allows you to test your input before submitting a request to the entity endpoints of the Admin API.

Note that this only performs the schema validation checks, checking that the input configuration is well-formed. Requests to the entity endpoint using the given configuration may still fail due to other reasons, such as invalid foreign key relationships or uniqueness check failures against the contents of the data store.

Schema validation#

Schema validation request address: /apisix/admin/schema/validate/{resource}

Request Methods#

MethodRequest URIRequest BodyDescription
POST/apisix/admin/schema/validate/{resource}{..resource conf..}Validate the resource configuration against corresponding schema.

Request Body Parameters#

  • 200: validate ok.
  • 400: validate failed, with error as response body in JSON format.

Example:

curl http://127.0.0.1:9180/apisix/admin/schema/validate/routes \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X POST -i -d '{
"uri": 1980,
"upstream": {
"scheme": "https",
"type": "roundrobin",
"nodes": {
"nghttp2.org": 1
}
}
}'
HTTP/1.1 400 Bad Request
Date: Mon, 21 Aug 2023 07:37:13 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
Server: APISIX/3.4.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: *
Access-Control-Max-Age: 3600

{"error_msg":"property \"uri\" validation failed: wrong type: expected string, got number"}