> For the complete documentation index, see [llms.txt](https://docs.digitalapi.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.digitalapi.ai/api-gateway/api-reference/service-route-spec-reference.md).

# Service and route spec reference

This page provides the field-level detail and working examples you need when constructing `serviceSpec`, `routeSpec`, and upstream payloads for the AILIX management API. It complements the [API reference](/api-gateway/api-reference/api-reference.md), which documents the endpoints themselves.

## Plugin configuration by category

Each snippet below shows the JSON you place inside the `plugins` map of a service spec or route spec. Plugin names are case-sensitive.

### Authentication

```json
{"key-auth": {"header": "X-API-Key"}}
```

```json
{"basic-auth": {}}
```

```json
{"jwt-auth": {}}
```

```json
{
  "openid-connect": {
    "client_id": "...",
    "client_secret": "...",
    "discovery": "https://.../.well-known/openid-configuration",
    "bearer_only": true
  }
}
```

```json
{
  "ldap-auth": {
    "ldap_uri": "ldap://host:1389",
    "base_dn": "ou=users,dc=example,dc=com",
    "uid": "cn"
  }
}
```

### Authorisation

```json
{
  "opa": {
    "host": "http://opa-host:8181",
    "policy": "rbac",
    "with_body": true
  }
}
```

```json
{"consumer-restriction": {"whitelist": ["consumer-a"]}}
```

```json
{"ip-restriction": {"whitelist": ["10.0.0.0/8"]}}
```

### Traffic

```json
{
  "limit-count": {
    "count": 100,
    "time_window": 60,
    "key_type": "var",
    "key": "consumer_name",
    "rejected_code": 429
  }
}
```

```json
{
  "limit-req": {
    "rate": 10,
    "burst": 20,
    "key_type": "var",
    "key": "consumer_name"
  }
}
```

```json
{
  "proxy-cache": {
    "cache_strategy": "memory",
    "cache_zone": "memory_cache",
    "cache_key": ["$uri"],
    "cache_ttl": 60
  }
}
```

{% hint style="warning" %}
**`proxy-cache` does not support `$request_method` in `cache_key`.** Use only `$uri`, `$host`, and supported Nginx variables.
{% endhint %}

### Transformation

```json
{"proxy-rewrite": {"regex_uri": ["^/prefix(.*)$", "$1"]}}
```

```json
{"response-rewrite": {"headers": {"set": {"X-Custom": "value"}}}}
```

```json
{
  "cors": {
    "allow_origins": "*",
    "allow_methods": "GET,POST,OPTIONS",
    "allow_headers": "*",
    "max_age": 3600
  }
}
```

### Observability

```json
{"prometheus": {"prefer_name": true}}
```

```json
{
  "zipkin": {
    "endpoint": "http://host:9411/api/v2/spans",
    "sample_ratio": 1,
    "service_name": "my-gateway"
  }
}
```

```json
{
  "kafka-logger": {
    "broker_list": {"host": 9092},
    "kafka_topic": "logs"
  }
}
```

```json
{
  "clickhouse-logger": {
    "endpoint_addr": "http://host:8123",
    "database": "apisix",
    "logtable": "access_log"
  }
}
```

```json
{
  "loki-logger": {
    "endpoint_addrs": ["http://host:3100"],
    "tenant_id": "default"
  }
}
```

{% hint style="warning" %}
**`loki-logger` uses `endpoint_addrs` (plural, array).** Using `endpoint_addr` (singular, string) triggers `property "endpoint_addrs" is required`.
{% endhint %}

```json
{
  "elasticsearch-logger": {
    "endpoint_addr": "http://host:9200",
    "field": {"index": "logs"}
  }
}
```

```json
{"http-logger": {"uri": "http://host/log-endpoint"}}
```

### Security

```json
{"csrf": {"key": "csrf-secret-key"}}
```

```json
{
  "uri-blocker": {
    "block_rules": [".*\\.sql$", ".*\\.env$"],
    "rejected_code": 403
  }
}
```

```json
{"chaitin-waf": {"host": "waf-host", "port": 8001}}
```

### AI

```json
{"ai-prompt-guard": {"match_all_roles": true}}
```

```json
{
  "ai-prompt-template": {
    "templates": [
      {
        "name": "my-tpl",
        "template": {
          "model": "gpt-4o",
          "messages": [
            {"role": "user", "content": "{{question}}"}
          ]
        }
      }
    ]
  }
}
```

```json
{
  "ai-rate-limiting": {
    "limit": 100,
    "time_window": 60
  }
}
```

```json
{
  "ai-proxy": {
    "provider": "openai",
    "model": {
      "source": "header",
      "header": "X-Model"
    },
    "auth": {
      "header": {
        "X-Api-Key": "sk-..."
      }
    }
  }
}
```

{% hint style="danger" %}
**AI plugin schemas differ from upstream APISIX open-source documentation.** Known differences:

* `ai-prompt-template`: requires a `templates` array where each entry needs `name` (string) and `template` (object with `model` and `messages[]`).
* `ai-proxy`: the `auth` field is required, not optional.
* Always fetch the schema first: `GET /api/orgs/{org}/plugin-schemas/{pluginName}`.
  {% endhint %}

## Plugin scope and precedence

Plugins can be attached at four scopes. When the same plugin is configured at multiple scopes, the narrower scope wins.

| Scope    | Precedence | Where configured                                                                |
| -------- | ---------- | ------------------------------------------------------------------------------- |
| Global   | Lowest     | Platform-level configuration (admin only).                                      |
| Service  | Low        | `serviceSpec.plugins` — applies to all routes in the service.                   |
| Route    | High       | `routeSpec[].plugins` — applies to a single route.                              |
| Consumer | Highest    | Consumer-level attachment — applies to a specific consumer regardless of route. |

{% hint style="info" %}
**Merge behaviour:** Route-level plugin config does not extend service-level config for the same plugin — it replaces it entirely. If you set `key-auth` at service level with `{"header": "X-API-Key"}` and also set `key-auth: {}` at route level, the route uses the default header, not `X-API-Key`.
{% endhint %}

## Health-check configuration

A full active + passive health-check configuration for an upstream's `specification.checks` field:

```json
{
  "checks": {
    "active": {
      "type": "http",
      "http_path": "/healthz",
      "host": "backend.example.com",
      "port": 8080,
      "timeout": 5,
      "concurrency": 2,
      "healthy": {
        "interval": 5,
        "successes": 3,
        "http_statuses": [200, 302]
      },
      "unhealthy": {
        "interval": 3,
        "http_failures": 3,
        "tcp_failures": 3,
        "timeouts": 3,
        "http_statuses": [500, 502, 503]
      }
    },
    "passive": {
      "type": "http",
      "healthy": {
        "successes": 3,
        "http_statuses": [200, 201, 301, 302]
      },
      "unhealthy": {
        "http_failures": 5,
        "tcp_failures": 2,
        "timeouts": 3,
        "http_statuses": [500, 502, 503]
      }
    }
  }
}
```

{% hint style="info" %}
**Active checks** poll the upstream on a timer. **Passive checks** observe real traffic and mark nodes unhealthy based on error counts. Combine both for the most responsive health detection.
{% endhint %}

## Complete working examples

### Example 1: Key-auth with rate limiting and proxy-rewrite

A service that authenticates via API key, rate-limits to 100 requests per minute per consumer, and strips a `/payments` prefix before forwarding to the backend.

```json
{
  "serviceSpec": {
    "name": "payments-api",
    "desc": "Payment processing endpoints.",
    "plugins": {
      "key-auth": {"header": "X-API-Key"},
      "limit-count": {
        "count": 100,
        "time_window": 60,
        "key_type": "var",
        "key": "consumer_name",
        "rejected_code": 429
      },
      "proxy-rewrite": {
        "regex_uri": ["^/payments(.*)$", "$1"]
      }
    }
  },
  "routeSpec": [
    {
      "name": "listPayments",
      "uri": "/payments/api/v1/transactions",
      "methods": ["GET"],
      "plugins": {}
    },
    {
      "name": "createPayment",
      "uri": "/payments/api/v1/transactions",
      "methods": ["POST"],
      "plugins": {}
    },
    {
      "name": "getPayment",
      "uri": "/payments/api/v1/transactions/:id",
      "methods": ["GET"],
      "plugins": {}
    },
    {
      "name": "refundPayment",
      "uri": "/payments/api/v1/transactions/:id/refund",
      "methods": ["POST"],
      "plugins": {}
    }
  ],
  "environmentUpstreams": [
    {
      "environmentId": "env-uuid",
      "upstreamId": "upstream-uuid"
    }
  ]
}
```

{% hint style="info" %}
**Why `proxy-rewrite`?** The routes use a `/payments` prefix to avoid collisions with other APIs in the same environment. The `proxy-rewrite` plugin strips this prefix so the backend receives `/api/v1/transactions` instead of `/payments/api/v1/transactions`.
{% endhint %}

### Example 2: Multiple observability plugins

A service with Prometheus metrics and Kafka logging, suitable for production monitoring.

```json
{
  "serviceSpec": {
    "name": "orders-api",
    "desc": "Order management service.",
    "plugins": {
      "prometheus": {"prefer_name": true},
      "kafka-logger": {
        "broker_list": {"kafka-broker-1": 9092},
        "kafka_topic": "gateway-access-logs"
      }
    }
  },
  "routeSpec": [
    {
      "name": "listOrders",
      "uri": "/api/orders",
      "methods": ["GET"],
      "plugins": {}
    },
    {
      "name": "createOrder",
      "uri": "/api/orders",
      "methods": ["POST"],
      "plugins": {}
    },
    {
      "name": "getOrder",
      "uri": "/api/orders/:id",
      "methods": ["GET"],
      "plugins": {}
    },
    {
      "name": "updateOrder",
      "uri": "/api/orders/:id",
      "methods": ["PUT"],
      "plugins": {}
    },
    {
      "name": "deleteOrder",
      "uri": "/api/orders/:id",
      "methods": ["DELETE"],
      "plugins": {}
    }
  ],
  "environmentUpstreams": [
    {
      "environmentId": "env-uuid",
      "upstreamId": "upstream-uuid"
    }
  ]
}
```

{% hint style="info" %}
**Kafka logger tip:** The `broker_list` maps hostnames to ports. In production, list all brokers for failover: `{"broker-1": 9092, "broker-2": 9092, "broker-3": 9092}`.
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.digitalapi.ai/api-gateway/api-reference/service-route-spec-reference.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
