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 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) 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 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.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →