> ## Documentation Index
> Fetch the complete documentation index at: https://docs.steward.fi/llms.txt
> Use this file to discover all available pages before exploring further.

# Routes

> Configure credential injection routes for the API proxy.

# Routes API

Routes tell the [Proxy Gateway](/concepts/proxy-gateway) how to inject credentials into outbound API requests. All endpoints are nested under `/secrets/routes` and require **tenant-level authentication**.

## Create Route

```
POST /secrets/routes
```

**Auth:** Tenant API key

**Request Body:**

```typescript theme={null}
{
  secretId: string;       // ID of the secret to inject
  hostPattern: string;    // Target host (e.g., "api.openai.com", "*.anthropic.com")
  pathPattern?: string;   // Path glob (default: "/*")
  method?: string;        // HTTP method filter (default: "*")
  injectAs: string;       // "header" | "query" | "body"
  injectKey: string;      // Header name, query param, or body field
  injectFormat?: string;  // Format string (default: "{value}")
  priority?: number;      // Higher = checked first (default: 0)
  enabled?: boolean;      // Toggle route (default: true)
}
```

**Response (201):**

```json theme={null}
{
  "ok": true,
  "data": {
    "id": "route-uuid",
    "secretId": "secret-uuid",
    "hostPattern": "api.openai.com",
    "pathPattern": "/*",
    "method": "*",
    "injectAs": "header",
    "injectKey": "Authorization",
    "injectFormat": "Bearer {value}",
    "priority": 0,
    "enabled": true,
    "createdAt": "2026-03-26T12:00:00Z"
  }
}
```

### Examples

<CodeGroup>
  ```bash OpenAI theme={null}
  curl -X POST https://api.steward.fi/secrets/routes \
    -H "X-Steward-Key: your-key" \
    -H "Content-Type: application/json" \
    -d '{
      "secretId": "openai-secret-id",
      "hostPattern": "api.openai.com",
      "pathPattern": "/*",
      "injectAs": "header",
      "injectKey": "Authorization",
      "injectFormat": "Bearer {value}"
    }'
  ```

  ```bash Anthropic theme={null}
  curl -X POST https://api.steward.fi/secrets/routes \
    -H "X-Steward-Key: your-key" \
    -H "Content-Type: application/json" \
    -d '{
      "secretId": "anthropic-secret-id",
      "hostPattern": "api.anthropic.com",
      "pathPattern": "/*",
      "injectAs": "header",
      "injectKey": "x-api-key",
      "injectFormat": "{value}"
    }'
  ```

  ```bash Query Param Auth theme={null}
  curl -X POST https://api.steward.fi/secrets/routes \
    -H "X-Steward-Key: your-key" \
    -H "Content-Type: application/json" \
    -d '{
      "secretId": "legacy-secret-id",
      "hostPattern": "api.legacy-service.com",
      "pathPattern": "/*",
      "injectAs": "query",
      "injectKey": "apiKey",
      "injectFormat": "{value}"
    }'
  ```
</CodeGroup>

***

## List Routes

```
GET /secrets/routes
```

**Auth:** Tenant API key

**Response:**

```json theme={null}
{
  "ok": true,
  "data": [
    {
      "id": "route-uuid-1",
      "secretId": "secret-uuid",
      "hostPattern": "api.openai.com",
      "pathPattern": "/*",
      "injectAs": "header",
      "injectKey": "Authorization",
      "injectFormat": "Bearer {value}",
      "priority": 0,
      "enabled": true
    }
  ]
}
```

***

## Update Route

```
PUT /secrets/routes/:id
```

**Auth:** Tenant API key

**Request Body:** Any subset of the create fields (partial update):

```typescript theme={null}
{
  hostPattern?: string;
  pathPattern?: string;
  method?: string;
  injectAs?: string;
  injectKey?: string;
  injectFormat?: string;
  priority?: number;
  enabled?: boolean;
}
```

```bash theme={null}
# Disable a route
curl -X PUT https://api.steward.fi/secrets/routes/route-uuid \
  -H "X-Steward-Key: your-key" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": false }'
```

***

## Delete Route

```
DELETE /secrets/routes/:id
```

**Auth:** Tenant API key

**Response:**

```json theme={null}
{
  "ok": true,
  "data": { "deleted": "route-uuid" }
}
```

***

## Route Matching

When the proxy receives a request, it matches routes in **priority order** (highest first). The first matching route wins.

A route matches when:

1. `hostPattern` matches the target host (supports `*` wildcards)
2. `pathPattern` matches the request path (supports `/*` glob)
3. `method` matches the HTTP method (or `*` for any)
4. `enabled` is `true`

### Priority Example

```typescript theme={null}
// Priority 10: Trading API gets privileged credentials
{ hostPattern: "api.example.com", pathPattern: "/v2/trading/*", priority: 10 }

// Priority 0: Everything else gets read-only credentials
{ hostPattern: "api.example.com", pathPattern: "/*", priority: 0 }
```

A request to `api.example.com/v2/trading/orders` matches the priority-10 route.
A request to `api.example.com/v1/data` matches the priority-0 fallback.

## Restricted inject headers

Some `injectKey` header names are rejected at route-create time. Hop-by-hop and
framing-sensitive headers (`connection`, `content-length`, `host`,
`transfer-encoding`, etc.) are unconditionally blocked.

Session-cookie headers (`Cookie` and `Set-Cookie`, matched case-insensitively)
are **blocked by default** and only become injectable when the operator sets
`STEWARD_ALLOW_COOKIE_INJECTION=true`. Injecting a raw `Cookie` replays a whole
browser session: there is no scoping, no revocation handle, and no `Set-Cookie`
rotation path, so the blast radius is the entire session. The preferred pattern
is a **scoped broker read token** injected as `Authorization: Bearer <token>` —
revocable, auditable, and narrowed per grant. Raw session replay stays available
for hosts that only speak cookies, but as a deliberate operator opt-in rather
than an implicit default. Existing routes are validated at create time, so a
route created before this default took effect keeps working; the flag only
gates creation of new cookie-injecting routes.
