How to Configure BYOK Authentication with External LLM Providers in the Copilot SDK
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, 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
modelname 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
typeandwireApifields. - 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 inbaseUrl. - 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
wireApisetting.
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) 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
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
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
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#)
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
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.
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
ProviderConfigobject tocreateSession()withtype,baseUrl, and authentication credentials. - Use
type: "azure"for Azure OpenAI (host only) andtype: "openai"for Microsoft Foundry or other compatible endpoints. - Select
wireApi: "responses"for multi-turn state management or"completions"for maximum compatibility. - Implement
bearerTokenProviderfor secure, short-lived token rotation with Azure Managed Identity or OAuth. - Explicitly declare model names and optionally provide
onListModelshandlers 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.
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).
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →