> ## Documentation Index
> Fetch the complete documentation index at: https://bifrost-backport-outbound-fetchers.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Context Keys

> Use context keys to configure request behavior, pass metadata, and access response information throughout the request lifecycle.

Bifrost uses `BifrostContext` - a custom `context.Context` - to pass configuration and metadata through the request lifecycle. Context keys allow you to customize request behavior, pass request-specific settings, and read metadata set by Bifrost.

The idiomatic pattern is to create a `BifrostContext` and call `SetValue` (or the chainable `WithValue`) directly on it:

```go theme={null}
bfCtx := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline)
bfCtx.SetValue(schemas.BifrostContextKeyRequestID, "req-001")

response, err := client.ChatCompletionRequest(bfCtx, &schemas.BifrostChatRequest{...})
```

## Request Configuration Keys

These keys can be set before making a request to customize behavior.

### Virtual Key

Pass a virtual key identifier to the governance plugin for budget and rate-limit enforcement.

```go theme={null}
bfCtx := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline)
bfCtx.SetValue(schemas.BifrostContextKeyVirtualKey, "vk-my-team")
```

### Extra Headers

Pass custom headers with individual requests. Headers are automatically propagated to the provider.

```go theme={null}
bfCtx := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline)
bfCtx.SetValue(schemas.BifrostContextKeyExtraHeaders, map[string][]string{
    "user-id":    {"user-123"},
    "session-id": {"session-abc"},
})

response, err := client.ChatCompletionRequest(bfCtx, &schemas.BifrostChatRequest{
    Provider: schemas.OpenAI,
    Model:    "gpt-4o-mini",
    Input:    messages,
})
```

<Note>
  See [Custom Headers Per Request](./provider-configuration#custom-headers-per-request) for detailed information on header handling and security restrictions.
</Note>

### API Key Selection

Bifrost supports selecting a specific key by **ID** or **name**. When both are present, ID takes priority.

#### By ID

Explicitly select a key by its unique ID.

```go theme={null}
bfCtx.SetValue(schemas.BifrostContextKeyAPIKeyID, "key-uuid-1234")
```

#### By Name

Explicitly select a named API key from your configured keys.

```go theme={null}
bfCtx.SetValue(schemas.BifrostContextKeyAPIKeyName, "premium-key")
```

### Skip Key Selection

Skip key selection entirely and pass an empty key to the provider. Useful for providers that don't require authentication or when using ambient credentials (e.g., IAM roles).

```go theme={null}
bfCtx.SetValue(schemas.BifrostContextKeySkipKeySelection, true)
```

### Direct Key

Supply a raw provider API key to use directly, bypassing the registered key pool. Set `BifrostContextKeyDirectKey` to a `schemas.Key` and Bifrost uses it as-is — there is no flag to enable in the SDK (the gateway's `allow_direct_keys` setting only gates the HTTP header path that populates this same key).

```go theme={null}
bfCtx.SetValue(schemas.BifrostContextKeyDirectKey, schemas.Key{
    ID:     "caller-provided",
    Name:   "caller-provided",
    Value:  schemas.EnvVar{Val: "sk-your-real-openai-key"},
    Models: []string{},
    Weight: 1.0,
})
```

<Warning>
  A direct key bypasses the **key pool** (no weighted selection, no rotation or fallback, no per-key model restrictions) — the key is used exactly as supplied. It does **not** bypass governance: if a virtual key (`BifrostContextKeyVirtualKey`) is set alongside it, that key's budgets, rate limits, allow-lists, and routing are still enforced. Prefer [API Key Selection](#api-key-selection) when you want to reference a Bifrost-managed key instead of passing the secret.
</Warning>

### Session Stickiness (Session ID)

Keep a session on what served it before: the provider that last served it, when routing still offers that provider, and the key that last served it within that provider. A request that names its provider is never reordered; only the key level applies to it. Useful for keeping provider prompt caches warm across a conversation, predictable rate-limit buckets, and cost attribution per user.

Bindings are written when a request is served, never when it fails. A later request with the same session ID puts the bound provider first among the routes the hooks produced and uses the bound key while it remains eligible; a key that is no longer in the supported set (disabled, removed, or model support changed) is dropped and the next served request rebinds. A fallback that serves becomes the session's new home. Decisions the session made are recorded in the request's routing logs under the `session-affinity` engine.

<Note>
  The policy that picks and keeps the key is `BifrostConfig.SessionAffinity`. When it is unset, Bifrost's own default applies: it needs a `KVStore` in `BifrostConfig`, binds the session to the key selected for its first request, and scopes bindings to the virtual key and user the request's grant identity is attributed to, so a session id reused by another caller does not share them. A custom `SessionAffinity` may keep its state elsewhere and scope it differently.
</Note>

```go theme={null}
bfCtx.SetValue(schemas.BifrostContextKeySessionID, "user-123-session-abc")
```

### Session TTL

Controls how long the session bindings are kept. If not set, Bifrost uses a default TTL of 1 hour. The TTL is refreshed by each served request so active sessions do not expire.

```go theme={null}
bfCtx.SetValue(schemas.BifrostContextKeySessionTTL, 30*time.Minute)
```

### Session Affinity Switch

Whether a request lets its session decide where it goes. On by default; `false` leaves the provider to routing and the key to key selection, and writes no session state for the request.

```go theme={null}
bfCtx.SetValue(schemas.BifrostContextKeySessionAffinity, false)
```

### Request ID

Set a custom request ID for tracking and correlation.

```go theme={null}
bfCtx.SetValue(schemas.BifrostContextKeyRequestID, "req-12345-abc")
```

### Custom URL Path

Append a custom path to the provider's base URL. Useful for accessing provider-specific endpoints.

```go theme={null}
bfCtx.SetValue(schemas.BifrostContextKeyURLPath, "/custom/endpoint")
```

### Stream Idle Timeout

Set a per-chunk idle timeout for streaming responses. If no chunk arrives within this duration, the stream is considered stalled and cancelled.

```go theme={null}
bfCtx.SetValue(schemas.BifrostContextKeyStreamIdleTimeout, 10*time.Second)
```

### Raw Request Body

Send a raw request body instead of Bifrost's standardized format. The provider receives your payload as-is. You must both set the context key AND populate `RawRequestBody` on the request.

```go theme={null}
rawPayload := []byte(`{
    "model": "gpt-4o",
    "messages": [{"role": "user", "content": "Hello!"}],
    "custom_field": "provider-specific-value"
}`)

bfCtx := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline)
bfCtx.SetValue(schemas.BifrostContextKeyUseRawRequestBody, true)

response, err := client.ChatCompletionRequest(bfCtx, &schemas.BifrostChatRequest{
    Provider:       schemas.OpenAI,
    Model:          "gpt-4o",
    RawRequestBody: rawPayload,
})
```

<Note>
  When using raw request body, Bifrost bypasses its request conversion and sends your payload directly to the provider. You are responsible for ensuring the payload matches the provider's expected format.
</Note>

### Send Back Raw Request/Response

Include the original request or response bytes in `ExtraFields` for debugging.

```go theme={null}
bfCtx := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline)
bfCtx.SetValue(schemas.BifrostContextKeySendBackRawRequest, true)
bfCtx.SetValue(schemas.BifrostContextKeySendBackRawResponse, true)

response, _ := client.ChatCompletionRequest(bfCtx, request)
if response.ChatResponse != nil {
    rawReq := response.ChatResponse.ExtraFields.RawRequest
    rawResp := response.ChatResponse.ExtraFields.RawResponse
}
```

### Passthrough Extra Parameters

When enabled, any parameters in `ExtraParams` are merged directly into the JSON body sent to the provider, bypassing Bifrost's parameter filtering. Useful for provider-specific parameters that Bifrost doesn't natively support.

```go theme={null}
bfCtx := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline)
bfCtx.SetValue(schemas.BifrostContextKeyPassthroughExtraParams, true)

response, err := client.ChatCompletionRequest(bfCtx, &schemas.BifrostChatRequest{
    Provider: schemas.OpenAI,
    Model:    "gpt-4o-mini",
    Input:    messages,
    Params: &schemas.ChatParameters{
        ExtraParams: map[string]interface{}{
            "custom_param": "value",
            "another_param": 123,
            "nested_param": map[string]interface{}{
                "nested_key": "nested_value",
            },
        },
    },
})
```

<Note>
  * This feature only works for JSON requests, not multipart/form-data requests
  * Parameters already handled by Bifrost are not duplicated - they appear in their proper location
  * Nested parameters are merged recursively with existing nested structures
</Note>

## MCP Context Keys

These keys control MCP tool execution behavior on a per-request basis. Request-level filtering takes priority over client-level configuration.

### Include Clients

Restrict which MCP clients can provide tools for this request. Pass `[]string{"*"}` to include all clients, or an empty slice to exclude all.

```go theme={null}
bfCtx.SetValue(schemas.MCPContextKeyIncludeClients, []string{"github", "filesystem"})
```

### Include Tools

Restrict which tools are available for this request. Use `"clientName-toolName"` format for individual tools or `"clientName-*"` as a wildcard for all tools from a client.

```go theme={null}
// Allow only the search tool from the github client
bfCtx.SetValue(schemas.MCPContextKeyIncludeTools, []string{"github-search_repositories"})

// Allow all tools from filesystem client
bfCtx.SetValue(schemas.MCPContextKeyIncludeTools, []string{"filesystem-*"})
```

### MCP Extra Headers

Forward additional headers to MCP servers during tool execution. Only headers present in the MCP client's configured allowlist are forwarded.

```go theme={null}
bfCtx.SetValue(schemas.BifrostContextKeyMCPExtraHeaders, map[string][]string{
    "x-user-id":    {"user-123"},
    "x-session-id": {"session-abc"},
})
```

## Response Metadata Keys

These keys are set by Bifrost and can be read from the context after a request completes. They are particularly useful in plugins and post-hooks.

### Selected Key Information

After Bifrost selects an API key, it stores the selection details in the context.

```go theme={null}
keyID := ctx.Value(schemas.BifrostContextKeySelectedKeyID).(string)
keyName := ctx.Value(schemas.BifrostContextKeySelectedKeyName).(string)
```

### Retry and Fallback Information

Track retry attempts and fallback progression.

```go theme={null}
// Number of retries attempted (0 = first attempt)
retries := ctx.Value(schemas.BifrostContextKeyNumberOfRetries).(int)

// Fallback index (0 = primary, 1 = first fallback, etc.)
fallbackIdx := ctx.Value(schemas.BifrostContextKeyFallbackIndex).(int)

// Request ID used for the fallback attempt
fallbackReqID := ctx.Value(schemas.BifrostContextKeyFallbackRequestID).(string)
```

### Stream End Indicator

For streaming responses, indicates when the stream has completed. Set by Bifrost automatically.

```go theme={null}
isStreamEnd := ctx.Value(schemas.BifrostContextKeyStreamEndIndicator).(bool)
```

<Note>
  Plugin developers: When implementing a short-circuit streaming response in `PreLLMHook` or `PostLLMHook`, set `BifrostContextKeyStreamEndIndicator` to `true` on the last chunk to trigger proper cleanup.
</Note>

### Integration Type

Identifies which SDK integration format is in use (useful in gateway plugins).

```go theme={null}
integrationType := ctx.Value(schemas.BifrostContextKeyIntegrationType).(string)
// e.g., "openai", "anthropic", "bedrock"
```

## Complete Example

```go theme={null}
package main

import (
    "context"
    "fmt"
    "log"
    "time"

    "github.com/maximhq/bifrost"
    "github.com/maximhq/bifrost/core/schemas"
)

func makeRequest(client *bifrost.Bifrost) {
    bfCtx := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline)

    // Request tracking
    bfCtx.SetValue(schemas.BifrostContextKeyRequestID, "req-001")

    // Custom headers forwarded to the provider
    bfCtx.SetValue(schemas.BifrostContextKeyExtraHeaders, map[string][]string{
        "x-correlation-id": {"corr-12345"},
        "x-tenant-id":      {"tenant-abc"},
    })

    // Include raw provider response for debugging
    bfCtx.SetValue(schemas.BifrostContextKeySendBackRawResponse, true)

    // Restrict MCP tools to a specific client
    bfCtx.SetValue(schemas.MCPContextKeyIncludeClients, []string{"filesystem"})

    messages := []schemas.BifrostMessage{
        {Role: "user", Content: &schemas.BifrostMessageContent{Text: bifrost.Ptr("Hello!")}},
    }

    response, err := client.ChatCompletionRequest(bfCtx, &schemas.BifrostChatRequest{
        Provider: schemas.OpenAI,
        Model:    "gpt-4o-mini",
        Input:    messages,
    })
    if err != nil {
        log.Printf("Request failed: %v", err)
        return
    }

    if response.ChatResponse != nil {
        extra := response.ChatResponse.ExtraFields
        fmt.Printf("Provider: %s\n", extra.Provider)
        fmt.Printf("Latency: %dms\n", extra.Latency)
        if extra.RawResponse != nil {
            fmt.Println("Raw response captured for debugging")
        }
    }
}
```

## Context Keys Reference

| Key | Type | Direction | Description |
| - | - | - | - |
| `BifrostContextKeyVirtualKey` | `string` | Set | Virtual key identifier for governance |
| `BifrostContextKeyAPIKeyName` | `string` | Set | Explicit API key name selection |
| `BifrostContextKeyAPIKeyID` | `string` | Set | Explicit API key ID selection (priority over name) |
| `BifrostContextKeyDirectKey` | `schemas.Key` | Set | Use a caller-supplied raw provider key directly, bypassing the key pool |
| `BifrostContextKeyRequestID` | `string` | Set | Custom request ID for tracking |
| `BifrostContextKeyFallbackRequestID` | `string` | Read | Request ID used for fallback attempt |
| `BifrostContextKeySkipKeySelection` | `bool` | Set | Skip key selection entirely |
| `BifrostContextKeySessionID` | `string` | Set | Session ID for provider and key session affinity (requires KV store) |
| `BifrostContextKeySessionTTL` | `time.Duration` | Set | TTL for session bindings (default: 1 hour) |
| `BifrostContextKeySessionAffinity` | `bool` | Set | Whether the session decides routing for the request (default: true) |
| `BifrostContextKeyExtraHeaders` | `map[string][]string` | Set | Custom headers forwarded to the provider |
| `BifrostContextKeyURLPath` | `string` | Set | Custom URL path appended to provider base URL |
| `BifrostContextKeyStreamIdleTimeout` | `time.Duration` | Set | Per-chunk idle timeout for streaming responses |
| `BifrostContextKeyUseRawRequestBody` | `bool` | Set | Send raw request body directly to provider |
| `BifrostContextKeySendBackRawRequest` | `bool` | Set | Include raw request in `ExtraFields` |
| `BifrostContextKeySendBackRawResponse` | `bool` | Set | Include raw provider response in `ExtraFields` |
| `BifrostContextKeyPassthroughExtraParams` | `bool` | Set | Merge `ExtraParams` directly into provider request |
| `MCPContextKeyIncludeClients` | `[]string` | Set | Allowlist of MCP client names for this request |
| `MCPContextKeyIncludeTools` | `[]string` | Set | Allowlist of MCP tools (`"client-tool"` or `"client-*"`) |
| `BifrostContextKeyMCPExtraHeaders` | `map[string][]string` | Set | Extra headers forwarded to MCP servers during tool execution |
| `BifrostContextKeySelectedKeyID` | `string` | Read | ID of the key selected by Bifrost |
| `BifrostContextKeySelectedKeyName` | `string` | Read | Name of the key selected by Bifrost |
| `BifrostContextKeyNumberOfRetries` | `int` | Read | Number of retry attempts made |
| `BifrostContextKeyFallbackIndex` | `int` | Read | Current fallback index (0 = primary) |
| `BifrostContextKeyStreamEndIndicator` | `bool` | Read | Whether the stream has completed |
| `BifrostContextKeyIntegrationType` | `string` | Read | SDK integration format in use (e.g. `"openai"`) |
| `BifrostContextKeyUserAgent` | `string` | Read | User agent of the incoming request |

## Next Steps

* **[Provider Configuration](./provider-configuration)** - Configure providers and keys
* **[Streaming Responses](./streaming)** - Real-time response handling
* **[Tool Calling](./tool-calling)** - Enable AI function calling
* **[Core Features](../../features/)** - Advanced Bifrost capabilities


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.