# How to Configure BYOK Authentication with External LLM Providers in the Copilot SDK

> Configure BYOK authentication with external LLM providers in the Copilot SDK. Connect directly to OpenAI compatible endpoints like Azure OpenAI, Anthropic, or local LLMs using ProviderConfig.

- Repository: [GitHub/copilot-sdk](https://github.com/github/copilot-sdk)
- Tags: how-to-guide
- Published: 2026-08-02

---

**The Copilot SDK enables BYOK (Bring-Your-Own-Key) authentication by passing a `ProviderConfig` object at session creation, allowing direct connections to any OpenAI-compatible LLM endpoint including Azure OpenAI, Anthropic, or local providers.**

The `github/copilot-sdk` supports bypassing default GitHub Copilot authentication to integrate external Large Language Model (LLM) providers. This BYOK configuration is critical for enterprise deployments requiring direct provider billing, custom-hosted models, or compliance with specific data residency requirements.

## Understanding BYOK Architecture

When you configure BYOK authentication, the SDK runtime uses your provider specification to construct HTTP requests, handle authentication headers, and manage wire-format compatibility. According to the source documentation in [`docs/auth/byok.md`](https://github.com/github/copilot-sdk/blob/main/docs/auth/byok.md), the architecture centers on explicit provider declaration at session initialization.

Key architectural requirements include:

- **Explicit Model Declaration** – Unlike default GitHub Copilot mode, BYOK requires you to specify the exact `model` name in the session configuration since the SDK cannot infer available models from external endpoints.
- **Path and Format Handling** – The SDK automatically selects the correct API path and request format based on the `type` and `wireApi` fields.
- **Authentication Flexibility** – Support for static API keys, static bearer tokens, or dynamic token providers integrating with Azure Managed Identity or OAuth flows.

## Provider Configuration Schema

The `ProviderConfig` object accepts the following fields as defined in the BYOK documentation reference:

| Field | Type | Description |
|-------|------|-------------|
| `type` | `"openai"` \| `"azure"` \| `"anthropic"` | Provider type identifier. Defaults to `"openai"`. |
| `baseUrl` / `base_url` | `string` | **Required.** Full API endpoint URL (or host for native Azure). |
| `apiKey` / `api_key` | `string` | Static API key; optional for local providers like Ollama. |
| `bearerToken` / `bearer_token` | `string` | Static bearer token; takes precedence over `apiKey` when provided. |
| `bearerTokenProvider` | `callback` | Async function returning a token on demand; recommended for short-lived tokens. |
| `wireApi` / `wire_api` | `"completions"` \| `"responses"` | Selects OpenAI Chat Completions API or the newer Responses API. |
| `azure.apiVersion` / `azure.api_version` | `string` | Azure-specific API version; omit for GA versionless `v1`. |

**Critical Configuration Rules:**

- For **Azure OpenAI**, you must set `type: "azure"` and provide only the host in `baseUrl`.
- For **Microsoft Foundry** (which exposes an OpenAI-compatible endpoint), use `type: "openai"` and include the `/openai/v1/` path in the URL.
- For **Anthropic models**, the SDK always uses the Messages API regardless of the `wireApi` setting.

## Authentication Methods

The Copilot SDK supports three authentication patterns for external providers:

**Static API Key** – Pass a fixed key via the `apiKey` field. This method works with most commercial providers but requires manual rotation.

**Static Bearer Token** – Supply a long-lived token via `bearerToken`. When present, this value overrides any `apiKey` configuration.

**Dynamic Token Provider** – Implement `bearerTokenProvider` as an async callback that returns fresh tokens on demand. This approach integrates with Azure Managed Identity (documented in [`docs/setup/azure-managed-identity.md`](https://github.com/github/copilot-sdk/blob/main/docs/setup/azure-managed-identity.md)) or custom OAuth flows, ensuring short-lived credentials never hardcode into your application.

**Local Providers** – Ollama and Microsoft Foundry Local run without authentication and do not require `apiKey` or token configuration.

## Wire API Selection

The `wireApi` field determines which OpenAI protocol variant the SDK uses:

- **`"completions"`** (default) – Routes to the Chat Completions endpoint (`/chat/completions`). This option offers broad compatibility with third-party providers but lacks multi-turn state management.
- **`"responses"`** – Routes to the OpenAI Responses endpoint. This enables multi-turn conversation state, tool namespacing, and richer reasoning capabilities.

Note that Anthropic models ignore this setting and always communicate via the Anthropic Messages API.

## Language-Specific Implementation Examples

The following examples demonstrate BYOK configuration across supported languages. Replace placeholder values such as `<resource-name>` and `FOUNDRY_API_KEY` with your actual endpoint and credentials.

### Python

```python
import asyncio
import os
from copilot import CopilotClient
from copilot.session import PermissionHandler

FOUNDRY_MODEL_URL = "https://<resource-name>.openai.azure.com/openai/v1/"

async def main():
    client = CopilotClient()
    await client.start()

    session = await client.create_session(
        on_permission_request=PermissionHandler.approve_all,
        model="gpt-5.2-codex",
        provider={
            "type": "openai",
            "base_url": FOUNDRY_MODEL_URL,
            "wire_api": "responses",
            "api_key": os.getenv("FOUNDRY_API_KEY"),
        },
    )

    done = asyncio.Event()
    session.on(lambda e: done.set() if e.type.value == "session.idle" else print(e.data.content))
    await session.send("What is 2+2?")
    await done.wait()
    await session.disconnect()
    await client.stop()

asyncio.run(main())

```

### Node.js / TypeScript

```typescript
import { CopilotClient } from "@github/copilot-sdk";

const FOUNDRY_MODEL_URL = "https://<resource-name>.openai.azure.com/openai/v1/";

const client = new CopilotClient();
const session = await client.createSession({
  model: "gpt-5.2-codex",
  provider: {
    type: "openai",
    baseUrl: FOUNDRY_MODEL_URL,
    wireApi: "responses",
    apiKey: process.env.FOUNDRY_API_KEY,
  },
});

session.on("assistant.message", e => console.log(e.data.content));
await session.sendAndWait({ prompt: "What is 2+2?" });
await client.stop();

```

### Go

```go
package main

import (
    "context"
    "fmt"
    "os"
    
    copilot "github.com/github/copilot-sdk/go"
)

func main() {
    ctx := context.Background()
    client := copilot.NewClient(nil)
    if err := client.Start(ctx); err != nil { panic(err) }
    defer client.Stop()

    session, err := client.CreateSession(ctx, &copilot.SessionConfig{
        Model: "gpt-5.2-codex",
        Provider: &copilot.ProviderConfig{
            Type:    "openai",
            BaseURL: "https://<resource-name>.openai.azure.com/openai/v1/",
            WireAPI: "responses",
            APIKey:  os.Getenv("FOUNDRY_API_KEY"),
        },
    })
    if err != nil { panic(err) }

    resp, err := session.SendAndWait(ctx, copilot.MessageOptions{Prompt: "What is 2+2?"})
    if err != nil { panic(err) }
    if d, ok := resp.Data.(*copilot.AssistantMessageData); ok {
        fmt.Println(d.Content)
    }
}

```

### .NET (C#)

```csharp
using GitHub.Copilot;
using System;

await using var client = new CopilotClient();
await using var session = await client.CreateSessionAsync(new SessionConfig {
    Model = "gpt-5.2-codex",
    Provider = new ProviderConfig {
        Type = "openai",
        BaseUrl = "https://<resource-name>.openai.azure.com/openai/v1/",
        WireApi = "responses",
        ApiKey = Environment.GetEnvironmentVariable("FOUNDRY_API_KEY"),
    },
});

var response = await session.SendAndWaitAsync(new MessageOptions { Prompt = "What is 2+2?" });
Console.WriteLine(response?.Data.Content);

```

### Java

```java
import com.github.copilot.CopilotClient;
import com.github.copilot.rpc.*;

var client = new CopilotClient();
client.start().get();

var session = client.createSession(new SessionConfig()
    .setModel("gpt-5.2-codex")
    .setProvider(new ProviderConfig()
        .setType("openai")
        .setBaseUrl("https://<resource-name>.openai.azure.com/openai/v1/")
        .setWireApi("responses")
        .setApiKey(System.getenv("FOUNDRY_API_KEY")))
).get();

var response = session.sendAndWait(new MessageOptions().setPrompt("What is 2+2?")).get();
System.out.println(response.getData().content());

client.stop().get();

```

## Custom Model Listing

To enable `client.listModels()` for your external provider, implement the `onListModels` handler at the client level. This handler must return an array of `ModelInfo` objects conforming to the SDK's internal schema, as documented in the *Custom model listing* section of [`docs/auth/byok.md`](https://github.com/github/copilot-sdk/blob/main/docs/auth/byok.md).

## Limitations and Considerations

BYOK authentication subjects your application to the external provider's rate limits, usage tracking, and model availability constraints. GitHub Copilot premium request quotas do not apply to BYOK sessions. Additionally, you must handle credential rotation and endpoint availability monitoring independently of GitHub's infrastructure.

## Summary

- Configure BYOK by passing a `ProviderConfig` object to `createSession()` with `type`, `baseUrl`, and authentication credentials.
- Use `type: "azure"` for Azure OpenAI (host only) and `type: "openai"` for Microsoft Foundry or other compatible endpoints.
- Select `wireApi: "responses"` for multi-turn state management or `"completions"` for maximum compatibility.
- Implement `bearerTokenProvider` for secure, short-lived token rotation with Azure Managed Identity or OAuth.
- Explicitly declare model names and optionally provide `onListModels` handlers for custom provider integrations.

## Frequently Asked Questions

### What is the difference between Azure OpenAI and Microsoft Foundry configuration?

For **Azure OpenAI**, set `type: "azure"` and provide only the host in `baseUrl` (the SDK constructs the full path). For **Microsoft Foundry**, which exposes an OpenAI-compatible endpoint, use `type: "openai"` and include the full path including `/openai/v1/` in the URL.

### How do I handle rotating API keys or OAuth tokens in production?

Instead of static `apiKey` or `bearerToken` values, implement the `bearerTokenProvider` callback function. This async function returns a fresh token on each request, integrating seamlessly with Azure Managed Identity or custom OAuth flows as documented in [`docs/setup/azure-managed-identity.md`](https://github.com/github/copilot-sdk/blob/main/docs/setup/azure-managed-identity.md).

### Why does my Anthropic model ignore the wireApi setting?

Anthropic models always use the Anthropic Messages API regardless of whether you set `wireApi` to `"completions"` or `"responses"`. The SDK handles this translation internally, but the Responses API features like multi-turn state management are not available when routing to Anthropic endpoints.

### Can I use BYOK with local models like Ollama?

Yes. Local providers such as Ollama and Microsoft Foundry Local do not require authentication. Omit the `apiKey`, `bearerToken`, and `bearerTokenProvider` fields from your `ProviderConfig`, and ensure `baseUrl` points to your local endpoint (typically `http://localhost:11434` for Ollama).