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

# Using Secrets in Routes

> Complete guide to injecting secrets into routes. Learn template syntax, headers, body injection, and environment-specific secrets.

# Using Secrets in Routes

Learn how to inject encrypted secrets into your route requests using KnoxCall's template system.

***

## Template Syntax

Secrets are injected using **template syntax** in your route configuration.

### Basic Format

```text theme={"dark"}
{{secret:SECRET_NAME}}
```

**Components:**

* `{{` - Opening delimiter
* `secret:` - Type identifier (required)
* `SECRET_NAME` - Your secret's name
* `}}` - Closing delimiter

### Example

If you have a secret named `stripe_prod_key`:

```json theme={"dark"}
{
  "Authorization": "Bearer {{secret:stripe_prod_key}}"
}
```

At runtime, KnoxCall replaces this with:

```json theme={"dark"}
{
  "Authorization": "Bearer sk_live_abc123xyz789..."
}
```

***

## Injecting Secrets in Headers

The most common use case - adding secrets to HTTP headers.

### Step 1: Edit Route

1. Go to **Routes**
2. Click on your route
3. Scroll to **Inject Headers** section

### Step 2: Add Header with Secret

Click **Add Header** or edit the JSON directly:

```json theme={"dark"}
{
  "Authorization": "Bearer {{secret:stripe_prod_key}}"
}
```

### Step 3: Save

Click **Save Changes**

✅ All requests through this route now have the secret injected!

### Common Header Patterns

#### Bearer Token

```json theme={"dark"}
{
  "Authorization": "Bearer {{secret:api_key}}"
}
```

Used by: Stripe, GitHub, most REST APIs

#### API Key Header

```json theme={"dark"}
{
  "X-API-Key": "{{secret:sendgrid_api_key}}"
}
```

Used by: SendGrid, Mailgun, custom APIs

#### Basic Authentication

```json theme={"dark"}
{
  "Authorization": "Basic {{secret:basic_auth_credentials}}"
}
```

**Note:** Secret value should be base64-encoded `username:password`

#### Custom Headers

```json theme={"dark"}
{
  "X-Custom-Token": "{{secret:custom_token}}",
  "X-Webhook-Secret": "{{secret:webhook_secret}}",
  "X-Client-ID": "{{secret:client_id}}"
}
```

***

## Injecting Secrets in the Request Body

KnoxCall can substitute placeholders **inside the request body your client sends** — not just headers. This lets your application reference secrets by name (or by ID) without ever holding the actual values. KnoxCall resolves the placeholders server-side, swaps them for the real decrypted values, and forwards the rewritten body upstream.

### How it works

1. Your client sends a POST/PUT/PATCH with placeholder tokens in the body
2. KnoxCall reads the body, scans for `{{secret:name}}` / `{{secret_id:uuid}}` / `{{var:key}}`
3. KnoxCall resolves each reference against your tenant's secrets for the route's **active environment**
4. KnoxCall rewrites `Content-Length` and forwards the substituted body to the upstream API
5. Your application never needs to possess the raw credential

### Placeholder forms accepted in the body

| Form                  | Lookup                                      | Example                                              | Recommended                    |
| --------------------- | ------------------------------------------- | ---------------------------------------------------- | ------------------------------ |
| `{{secret_id:UUID}}`  | By secret **UUID** — stable, rename-safe    | `{{secret_id:550e8400-e29b-41d4-a716-446655440000}}` | ✅ **Preferred**                |
| `{{secret:NAME}}`     | By secret **name** — legacy, human-friendly | `{{secret:stripe_prod_key}}`                         | ⚠️ Works, but breaks on rename |
| `{{var:QUERY_PARAM}}` | Value of an incoming query string parameter | `{{var:user_id}}`                                    | —                              |

<Warning>
  **Prefer the UUID form (`{{secret_id:...}}`) wherever possible.** A secret's UUID is permanent and never changes, so your code and templates keep working even if someone renames the secret in the dashboard. The name-based form (`{{secret:...}}`) is supported for backwards compatibility and readability, but any rename will silently break every reference to it and the next request will hard-fail with "Missing secret".

  Also note: `{{secret:UUID}}` is **not** a shortcut for ID-based lookup — it would try to find a secret literally *named* that UUID string and fail. For ID lookups always use the `secret_id:` prefix.
</Warning>

You can find a secret's UUID on its detail page in the KnoxCall dashboard, or in the response of the `create_secret` API / onboarding-agent tool call.

### Example — JSON body

Your client code sends this to KnoxCall (UUID form recommended):

```http theme={"dark"}
POST https://YOUR-SUBDOMAIN.knoxcall.com/v1/charges
Content-Type: application/json
x-knoxcall-route: ROUTE_ID
x-knoxcall-key: YOUR_CLIENT_KEY

{
  "api_key": "{{secret_id:550e8400-e29b-41d4-a716-446655440000}}",
  "amount": 1000,
  "currency": "usd"
}
```

What the upstream API actually receives:

```http theme={"dark"}
POST /v1/charges HTTP/1.1
Host: api.stripe.com

{
  "api_key": "sk_live_abc123xyz789...",
  "amount": 1000,
  "currency": "usd"
}
```

### Example — form-urlencoded

```http theme={"dark"}
POST https://YOUR-SUBDOMAIN.knoxcall.com/send
Content-Type: application/x-www-form-urlencoded

api_key={{secret_id:550e8400-e29b-41d4-a716-446655440000}}&to=%2B15551234567&body=hello
```

### Example — plain text / XML

```http theme={"dark"}
POST https://YOUR-SUBDOMAIN.knoxcall.com/soap
Content-Type: application/xml

<?xml version="1.0"?>
<Envelope>
  <Auth>{{secret_id:550e8400-e29b-41d4-a716-446655440000}}</Auth>
  <Action>GetInvoice</Action>
</Envelope>
```

### Supported Content-Types

KnoxCall only rewrites bodies for **text-shaped** Content-Types, so binary uploads stay byte-identical:

| Content-Type                                                                   | Body injection             |
| ------------------------------------------------------------------------------ | -------------------------- |
| `application/json`                                                             | ✅                          |
| `application/xml`, `text/xml`, `application/*+xml`                             | ✅                          |
| `application/*+json` (e.g. `application/vnd.api+json`)                         | ✅                          |
| `application/x-www-form-urlencoded`                                            | ✅                          |
| `application/javascript`, `application/ecmascript`                             | ✅                          |
| `text/*` (plain, html, csv, etc.)                                              | ✅                          |
| `multipart/form-data`                                                          | ❌ passed through unchanged |
| `application/octet-stream`, `image/*`, `audio/*`, `video/*`, `application/pdf` | ❌ passed through unchanged |

### Safety rules

* **Tenant isolation** — clients can only reference secrets belonging to their own tenant. A UUID guessed from another tenant will hard-fail.
* **Environment-aware** — KnoxCall resolves secrets against the route's active environment. If the secret has no value for that environment, the request fails with a clear error (no silent fallback).
* **Size limit** — bodies larger than 1 MiB pass through without substitution to protect the proxy (configurable via `BODY_INJECTION_MAX_BYTES`).
* **UTF-8 required** — the body must be valid UTF-8 with no NUL bytes; anything else is treated as binary and passed through untouched.
* **Hard-fail on missing secrets** — referencing a secret that doesn't exist (or has no value in the active environment) returns an error instead of sending an empty string upstream.
* **Logs are scrubbed** — audit logs store the body **with placeholders intact**, never the resolved secret values.

### Do I still need headers?

Header injection and body placeholders are **independent** and can be combined in the same request. Use headers for stable auth (e.g. `Authorization: Bearer`) and body placeholders for APIs that expect credentials in the payload or for per-call secret selection.

### Common Body Patterns

All examples use the **recommended UUID form** — substitute your actual secret IDs.

#### Simple API key in body (UUID, rename-safe)

```json theme={"dark"}
{ "api_key": "{{secret_id:550e8400-e29b-41d4-a716-446655440000}}" }
```

#### Nested objects

```json theme={"dark"}
{
  "credentials": {
    "username": "admin",
    "password": "{{secret_id:7a4c1f22-9e8b-4d60-9b32-12e1f4b9c8a1}}"
  }
}
```

#### Legacy name form (works, but breaks if the secret is renamed)

```json theme={"dark"}
{ "api_key": "{{secret:service_api_key}}" }
```

#### Multiple secrets in one request

```json theme={"dark"}
{
  "payment_key": "{{secret_id:550e8400-e29b-41d4-a716-446655440000}}",
  "email_key":   "{{secret_id:6b1f2f33-c1a4-4f75-a0b7-8d91e2a6f3c2}}",
  "sms_key":     "{{secret_id:a9c82e44-ff3a-4210-9c63-1b8f5cf9e7d1}}"
}
```

#### Mixing a secret with a query-string variable

```http theme={"dark"}
POST /notify?user_id=42
Content-Type: application/json

{
  "user":  "{{var:user_id}}",
  "token": "{{secret_id:550e8400-e29b-41d4-a716-446655440000}}"
}
```

***

## Multiple Secrets in One Route

You can use multiple different secrets in the same route configuration.

### Example: Multi-Service Integration

**Route:** `notification-service`

**Headers:**

```json theme={"dark"}
{
  "X-Email-Key": "{{secret:sendgrid_api_key}}",
  "X-SMS-Key": "{{secret:twilio_auth_token}}"
}
```

**Body:**

```json theme={"dark"}
{
  "email_config": {
    "api_key": "{{secret:sendgrid_api_key}}"
  },
  "sms_config": {
    "auth_token": "{{secret:twilio_auth_token}}",
    "account_sid": "{{secret:twilio_account_sid}}"
  },
  "push_config": {
    "server_key": "{{secret:fcm_server_key}}"
  }
}
```

**Result:** All 4 secrets injected in one request!

***

## Environment-Specific Secrets

Use different secrets per environment (dev, staging, production).

### Setup

**Create environment-specific secrets:**

1. **Production:**
   ```text theme={"dark"}
   Name: stripe_prod_key
   Value: sk_live_abc123...
   ```

2. **Staging:**
   ```text theme={"dark"}
   Name: stripe_staging_key
   Value: sk_test_xyz789...
   ```

3. **Development:**
   ```text theme={"dark"}
   Name: stripe_dev_key
   Value: sk_test_dev123...
   ```

### Configure Route Per Environment

**Base environment (production):**

```json theme={"dark"}
{
  "Authorization": "Bearer {{secret:stripe_prod_key}}"
}
```

**Override for staging:**

```json theme={"dark"}
{
  "Authorization": "Bearer {{secret:stripe_staging_key}}"
}
```

**Override for development:**

```json theme={"dark"}
{
  "Authorization": "Bearer {{secret:stripe_dev_key}}"
}
```

### How to Set Up

1. Go to route → **Environment** tab
2. Select "staging"
3. Edit **Inject Headers**:
   ```json theme={"dark"}
   {
     "Authorization": "Bearer {{secret:stripe_staging_key}}"
   }
   ```
4. Save
5. Repeat for "development"

**Result:**

* Production uses: `stripe_prod_key`
* Staging uses: `stripe_staging_key`
* Development uses: `stripe_dev_key`

All with the same route!

***

## Method-Specific Secrets

Different secrets for different HTTP methods on the same route.

### Example Scenario

Route handles both GET (read) and POST (write) to same API:

* GET needs read-only key
* POST needs full-access key

### Setup

**GET method config:**

```json theme={"dark"}
{
  "method": "GET",
  "inject_headers_json": {
    "Authorization": "Bearer {{secret:api_read_only_key}}"
  }
}
```

**POST method config:**

```json theme={"dark"}
{
  "method": "POST",
  "inject_headers_json": {
    "Authorization": "Bearer {{secret:api_full_access_key}}"
  }
}
```

**Result:**

* GET requests use read-only key
* POST requests use full-access key

***

## Secret Resolution Order

When multiple environments and methods are involved, KnoxCall resolves secrets in this order:

1. **Method-specific** environment override
2. **Environment override** (if method not specified)
3. **Base route** configuration

### Example

```text theme={"dark"}
Request: POST to route in "staging" environment

Lookup order:
1. Check: staging → POST method config → secrets
2. If not found: staging → default config → secrets
3. If not found: base (production) → POST method config → secrets
4. If not found: base (production) → default config → secrets
```

***

## Template Syntax Rules

### ✅ Correct Syntax

```json theme={"dark"}
{
  "Authorization": "Bearer {{secret:stripe_key}}"
}
```

**Rules:**

* Double curly braces: `{{` and `}}`
* Prefix: `secret:`
* No spaces inside braces
* Lowercase with underscores for secret names

### ❌ Common Mistakes

```json theme={"dark"}
// ❌ Single braces
{
  "Authorization": "Bearer {secret:stripe_key}"
}

// ❌ No prefix
{
  "Authorization": "Bearer {{stripe_key}}"
}

// ❌ Wrong prefix
{
  "Authorization": "Bearer {{env:stripe_key}}"
}

// ❌ Spaces inside
{
  "Authorization": "Bearer {{ secret:stripe_key }}"
}

// ❌ Hyphens in name
{
  "Authorization": "Bearer {{secret:stripe-key}}"
}
```

***

## Real-World Examples

### Example 1: Stripe Payments

**Secret:**

```text theme={"dark"}
Name: stripe_prod_key
Value: sk_live_abc123xyz789...
```

**Route config:**

```json theme={"dark"}
{
  "inject_headers_json": {
    "Authorization": "Bearer {{secret:stripe_prod_key}}",
    "Content-Type": "application/json"
  }
}
```

**Actual request sent to Stripe:**

```http theme={"dark"}
POST /v1/charges HTTP/1.1
Host: api.stripe.com
Authorization: Bearer sk_live_abc123xyz789...
Content-Type: application/json

{"amount": 1000, "currency": "usd"}
```

### Example 2: SendGrid Email

**Secret:**

```text theme={"dark"}
Name: sendgrid_api_key
Value: SG.abc123xyz789...
```

**Route config:**

```json theme={"dark"}
{
  "inject_headers_json": {
    "Authorization": "Bearer {{secret:sendgrid_api_key}}",
    "Content-Type": "application/json"
  },
  "inject_body_json": {
    "personalizations": [{
      "to": [{"email": "user@example.com"}]
    }],
    "from": {"email": "noreply@acme.com"},
    "subject": "Welcome!",
    "content": [{
      "type": "text/html",
      "value": "<p>Welcome!</p>"
    }]
  }
}
```

### Example 3: Database Access

**Secret:**

```text theme={"dark"}
Name: postgres_connection_url
Value: postgres://user:pass@host:5432/db
```

**Route config:**

```json theme={"dark"}
{
  "inject_body_json": {
    "connection_string": "{{secret:postgres_connection_url}}",
    "query": "SELECT * FROM users WHERE id = $1",
    "params": [123]
  }
}
```

### Example 4: PrintNode Printing

**Secret:**

```text theme={"dark"}
Name: printnode_api_key
Value: abc123xyz789...
```

**Route config:**

```json theme={"dark"}
{
  "inject_headers_json": {
    "Authorization": "Basic {{secret:printnode_api_key}}"
  },
  "inject_body_json": {
    "printerId": 123,
    "title": "Receipt",
    "contentType": "pdf_uri",
    "content": "https://example.com/receipt.pdf",
    "source": "KnoxCall POS"
  }
}
```

### Example 5: Multi-Service Orchestration

**Secrets:**

```text theme={"dark"}
stripe_key
sendgrid_key
twilio_key
```

**Route config:**

```json theme={"dark"}
{
  "inject_headers_json": {
    "X-Service": "orchestrator"
  },
  "inject_body_json": {
    "actions": [
      {
        "service": "payment",
        "api_key": "{{secret:stripe_key}}",
        "action": "charge"
      },
      {
        "service": "email",
        "api_key": "{{secret:sendgrid_key}}",
        "action": "send_receipt"
      },
      {
        "service": "sms",
        "api_key": "{{secret:twilio_key}}",
        "action": "send_notification"
      }
    ]
  }
}
```

***

## Combining Secrets with Other Templates

You can combine secrets with the other supported template forms in a single body or header set:

```json theme={"dark"}
{
  "Authorization": "Bearer {{secret_id:550e8400-e29b-41d4-a716-446655440000}}",
  "X-Legacy-Key": "{{secret:api_key}}",
  "X-User-ID": "{{var:user_id}}"
}
```

**Supported placeholder forms:**

* `{{secret_id:UUID}}` - Inject a secret by its **UUID** (preferred — rename-safe)
* `{{secret:name}}` - Inject a secret by its **name** (legacy — breaks on rename)
* `{{var:QUERY_PARAM}}` - Substitute the value of an incoming query-string parameter

These are the only placeholder forms the template engine resolves. Tokens such as `{{header:name}}`, `{{env:name}}`, or `{{uuid}}` are **not** supported and are passed through to the upstream unchanged.

***

## Security Considerations

### What Gets Logged

**In KnoxCall logs:**

* Template syntax shown: `{{secret:stripe_key}}`
* Actual value: **Never logged**

**Example log entry:**

```json theme={"dark"}
{
  "request_headers": {
    "Authorization": "Bearer {{secret:stripe_key}}"
  }
}
```

Plaintext value is not exposed in logs for security.

### Secret Transmission

**Flow:**

```text theme={"dark"}
1. Client makes request to KnoxCall
   ↓
2. KnoxCall receives request
   ↓
3. Loads route configuration
   ↓
4. Sees {{secret:stripe_key}} template
   ↓
5. Decrypts secret SERVER-SIDE
   ↓
6. Replaces template with plaintext
   ↓
7. Forwards request to backend
   ↓
8. Backend receives real API key
   ↓
9. Client NEVER sees the plaintext!
```

### Best Practices

✅ **Do:**

* Use secrets for all sensitive values
* Separate secrets per environment
* Rotate secrets regularly
* Use descriptive secret names
* Document what each secret is for

❌ **Don't:**

* Hardcode API keys in route configs
* Use production secrets in development
* Share secrets via email or chat
* Commit secrets to git
* Use generic names like "key" or "secret"

***

## Troubleshooting

### Secret Not Replacing

**Symptom:** Backend receives `{{secret:stripe_key}}` literally.

**Causes:**

1. Syntax error in template
2. Secret doesn't exist
3. Secret name typo

**Debug steps:**

1. Check template syntax: `{{secret:name}}`
2. Verify secret exists: Resources → Secrets
3. Check spelling matches exactly (case-sensitive)
4. Look at logs for error messages

### Wrong Value Injected

**Symptom:** Wrong API key being used.

**Causes:**

1. Using wrong environment
2. Wrong secret name
3. Secret not configured for environment

**Fix:**

1. Check which environment request is using
2. Verify environment override has correct secret
3. Check secret name spelling

### Secret Not Found Error

**Error:** `Secret 'api_key' not found`

**Solution:**

1. Go to Resources → Secrets
2. Check secret exists
3. Verify name matches template exactly
4. Check no typos: `api_key` vs `api_key_`

### Value Not Decrypting

**Symptom:** Encrypted value sent instead of plaintext.

**Cause:** KnoxCall internal error (very rare).

**Solution:**

1. Check KnoxCall status
2. Try creating new secret version
3. Contact support if persists

***

## Testing Secret Injection

### Test Endpoint

Use a test endpoint to verify secret injection:

1. **Create test route:**
   * Target: `https://httpbin.org/anything`
   * Method: POST

2. **Add secret in header:**
   ```json theme={"dark"}
   {
     "X-Test-Secret": "{{secret:test_api_key}}"
   }
   ```

3. **Make request:**
   ```bash theme={"dark"}
   curl -X POST https://YOUR-SUBDOMAIN.knoxcall.com/anything \
     -H "x-knoxcall-route: test-route" \
     -H "x-knoxcall-key: YOUR_KEY"
   ```

4. **Check response:**
   ```json theme={"dark"}
   {
     "headers": {
       "X-Test-Secret": "your-actual-secret-value-here"
     }
   }
   ```

✅ If you see actual value, injection works!

***

## Quick Reference

| Use Case        | Template Syntax          | Example                                        |
| --------------- | ------------------------ | ---------------------------------------------- |
| **Header**      | `{{secret:name}}`        | `"Authorization": "Bearer {{secret:api_key}}"` |
| **Body**        | `{{secret:name}}`        | `"api_key": "{{secret:api_key}}"`              |
| **Multiple**    | Multiple templates       | `{{secret:key1}}` and `{{secret:key2}}`        |
| **Environment** | Different secret per env | Production: `{{secret:prod_key}}`              |

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Creating Secrets" icon="plus" href="/essentials/secrets/creating-secrets">
    How to create and manage secrets
  </Card>

  <Card title="Secrets Overview" icon="shield" href="/essentials/secrets/secrets-overview">
    Complete secrets guide
  </Card>

  <Card title="Environment Basics" icon="layer-group" href="/essentials/environments/environment-basics">
    Environment-specific secrets
  </Card>

  <Card title="OAuth2 Flow" icon="rotate" href="/security/oauth2-flow">
    Auto-refreshing OAuth2 tokens
  </Card>
</CardGroup>

***

<Info>
  **Pro Tip:** Always test secret injection with a test endpoint (like httpbin.org) before using in production. This lets you verify the actual decrypted value is correct.
</Info>
