{"meta":{"title":"BYOK (bring your own key)","intro":"BYOK allows you to use the Copilot SDK with your own API keys from model providers, bypassing GitHub Copilot authentication. This is useful for enterprise deployments, custom model hosting, or when you want direct billing with your model provider.","product":"GitHub Copilot","breadcrumbs":[{"href":"/en/copilot","title":"GitHub Copilot"},{"href":"/en/copilot/how-tos","title":"How-tos"},{"href":"/en/copilot/how-tos/copilot-sdk","title":"Copilot SDK"},{"href":"/en/copilot/how-tos/copilot-sdk/auth","title":"Authentication"},{"href":"/en/copilot/how-tos/copilot-sdk/auth/byok","title":"BYOK"}],"documentType":"article"},"body":"# BYOK (bring your own key)\n\nBYOK allows you to use the Copilot SDK with your own API keys from model providers, bypassing GitHub Copilot authentication. This is useful for enterprise deployments, custom model hosting, or when you want direct billing with your model provider.\n\n<!-- markdownlint-disable GHD046 GHD005 -->\n\n<!-- Suppressed: GHD046 (outdated release terminology), GHD005 (hardcoded data variable) -->\n\n## Supported providers\n\n| Provider                         | Type Value              | Notes                                                                      |\n| -------------------------------- | ----------------------- | -------------------------------------------------------------------------- |\n| OpenAI                           | `\"openai\"`              | OpenAI API and OpenAI-compatible endpoints                                 |\n| Microsoft Foundry / Azure OpenAI | `\"openai\"` or `\"azure\"` | Use `\"openai\"` for `/openai/v1/`; use `\"azure\"` for native Azure endpoints |\n| Anthropic                        | `\"anthropic\"`           | Claude models                                                              |\n| Ollama                           | `\"openai\"`              | Local models via OpenAI-compatible API                                     |\n| Microsoft Foundry Local          | `\"openai\"`              | Run AI models locally on your device via OpenAI-compatible API             |\n| Other OpenAI-compatible          | `\"openai\"`              | vLLM, LiteLLM, etc.                                                        |\n\n## Quick start: Microsoft Foundry\n\nMicrosoft Foundry is a common BYOK deployment target for enterprises. Here's a complete example:\n\n<div class=\"ghd-codetabs\">\n<div class=\"ghd-codetab\" data-lang=\"python\" data-label=\"Python\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Python</div>\n\n```python\nimport asyncio\nimport os\nfrom copilot import CopilotClient\nfrom copilot.session import PermissionHandler\n\nFOUNDRY_MODEL_URL = \"https://<resource-name>.openai.azure.com/openai/v1/\"\n# Set FOUNDRY_API_KEY environment variable\n\nasync def main():\n    client = CopilotClient()\n    await client.start()\n\n    session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model=\"gpt-5.2-codex\", provider={\n        \"type\": \"openai\",\n        \"base_url\": FOUNDRY_MODEL_URL,\n        \"wire_api\": \"responses\",  # Use \"completions\" for older models\n        \"api_key\": os.environ[\"FOUNDRY_API_KEY\"],\n    })\n\n    done = asyncio.Event()\n\n    def on_event(event):\n        if event.type.value == \"assistant.message\":\n            print(event.data.content)\n        elif event.type.value == \"session.idle\":\n            done.set()\n\n    session.on(on_event)\n    await session.send(\"What is 2+2?\")\n    await done.wait()\n\n    await session.disconnect()\n    await client.stop()\n\nasyncio.run(main())\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"typescript\" data-label=\"TypeScript\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">TypeScript</div>\n\n```typescript\nimport { CopilotClient } from \"@github/copilot-sdk\";\n\nconst FOUNDRY_MODEL_URL = \"https://<resource-name>.openai.azure.com/openai/v1/\";\n\nconst client = new CopilotClient();\nconst session = await client.createSession({\n    model: \"gpt-5.2-codex\",  // Your deployment name\n    provider: {\n        type: \"openai\",\n        baseUrl: FOUNDRY_MODEL_URL,\n        wireApi: \"responses\",  // Use \"completions\" for older models\n        apiKey: process.env.FOUNDRY_API_KEY,\n    },\n});\n\nsession.on(\"assistant.message\", (event) => {\n    console.log(event.data.content);\n});\n\nawait session.sendAndWait({ prompt: \"What is 2+2?\" });\nawait client.stop();\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"go\" data-label=\"Go\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Go</div>\n\n```golang\npackage main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"os\"\n    copilot \"github.com/github/copilot-sdk/go\"\n)\n\nfunc main() {\n    ctx := context.Background()\n    client := copilot.NewClient(nil)\n    if err := client.Start(ctx); err != nil {\n        panic(err)\n    }\n    defer client.Stop()\n\n    session, err := client.CreateSession(ctx, &copilot.SessionConfig{\n        Model: \"gpt-5.2-codex\",  // Your deployment name\n        Provider: &copilot.ProviderConfig{\n            Type:    \"openai\",\n            BaseURL: \"https://<resource-name>.openai.azure.com/openai/v1/\",\n            WireAPI: \"responses\",  // Use \"completions\" for older models\n            APIKey:  os.Getenv(\"FOUNDRY_API_KEY\"),\n        },\n    })\n    if err != nil {\n        panic(err)\n    }\n\n    response, err := session.SendAndWait(ctx, copilot.MessageOptions{\n        Prompt: \"What is 2+2?\",\n    })\n    if err != nil {\n        panic(err)\n    }\n\n    if d, ok := response.Data.(*copilot.AssistantMessageData); ok {\n        fmt.Println(d.Content)\n    }\n}\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"dotnet\" data-label=\".NET\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">.NET</div>\n\n```csharp\nusing GitHub.Copilot;\n\nawait using var client = new CopilotClient();\nawait using var session = await client.CreateSessionAsync(new SessionConfig\n{\n    Model = \"gpt-5.2-codex\",  // Your deployment name\n    Provider = new ProviderConfig\n    {\n        Type = \"openai\",\n        BaseUrl = \"https://<resource-name>.openai.azure.com/openai/v1/\",\n        WireApi = \"responses\",  // Use \"completions\" for older models\n        ApiKey = Environment.GetEnvironmentVariable(\"FOUNDRY_API_KEY\"),\n    },\n});\n\nvar response = await session.SendAndWaitAsync(new MessageOptions\n{\n    Prompt = \"What is 2+2?\",\n});\nConsole.WriteLine(response?.Data.Content);\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"java\" data-label=\"Java\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Java</div>\n\n```java\nimport com.github.copilot.CopilotClient;\nimport com.github.copilot.rpc.*;\n\nvar client = new CopilotClient();\nclient.start().get();\n\nvar session = client.createSession(new SessionConfig()\n    .setModel(\"gpt-5.2-codex\")  // Your deployment name\n    .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)\n    .setProvider(new ProviderConfig()\n        .setType(\"openai\")\n        .setBaseUrl(\"https://<resource-name>.openai.azure.com/openai/v1/\")\n        .setWireApi(\"responses\")  // Use \"completions\" for older models\n        .setApiKey(System.getenv(\"FOUNDRY_API_KEY\")))\n).get();\n\nvar response = session.sendAndWait(new MessageOptions()\n    .setPrompt(\"What is 2+2?\")).get();\nSystem.out.println(response.getData().content());\n\nclient.stop().get();\n```\n\n</div>\n\n</div>\n\n## Provider configuration reference\n\n### ProviderConfig fields\n\n| Field                                           | Type                                     | Description                                                                                                                                                                                                                                                                  |\n| ----------------------------------------------- | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `type`                                          | `\"openai\"` \\| `\"azure\"` \\| `\"anthropic\"` | Provider type (default: `\"openai\"`)                                                                                                                                                                                                                                          |\n| `baseUrl` / `base_url`                          | string                                   | **Required.** API endpoint URL                                                                                                                                                                                                                                               |\n| `apiKey` / `api_key`                            | string                                   | API key (optional for local providers like Ollama)                                                                                                                                                                                                                           |\n| `bearerToken` / `bearer_token`                  | string                                   | Bearer token auth (takes precedence over apiKey)                                                                                                                                                                                                                             |\n| `bearerTokenProvider` / `bearer_token_provider` | callback                                 | Returns a bearer token on demand (takes precedence over `apiKey` and `bearerToken`)                                                                                                                                                                                          |\n| `wireApi` / `wire_api`                          | `\"completions\"` \\| `\"responses\"`         | Select `\"completions\"` for broad model compatibility (the Chat Completions API); select `\"responses\"` for multi-turn state management, tool namespacing, and reasoning support (the Responses API). Anthropic models always use the Messages API regardless of this setting. |\n| `azure.apiVersion` / `azure.api_version`        | string                                   | Azure API version. When set, the runtime uses the versioned deployment route; when omitted, it uses the GA versionless `v1` route.                                                                                                                                           |\n\n### Wire API format\n\nThe `wireApi` setting determines which OpenAI API format to use:\n\n* **`\"completions\"`** (default) - Chat Completions API (`/chat/completions`) for broad model compatibility.\n* **`\"responses\"`** - Responses API for multi-turn state management, tool namespacing, and reasoning support.\n\nAnthropic models always use the Anthropic Messages API regardless of this setting.\n\n### Type-specific notes\n\n**OpenAI (`type: \"openai\"`)**\n\n* Works with OpenAI API and any OpenAI-compatible endpoint\n* `baseUrl` should include the full path (e.g., `https://api.openai.com/v1`)\n\n**Azure (`type: \"azure\"`)**\n\n* Use for native Azure OpenAI endpoints\n* `baseUrl` should be just the host (e.g., `https://my-resource.openai.azure.com`)\n* Do NOT include `/openai/v1` in the URL—the SDK handles path construction\n\n**Anthropic (`type: \"anthropic\"`)**\n\n* For direct Anthropic API access\n* Uses Claude-specific API format\n\n## Example configurations\n\n### OpenAI direct\n\n```typescript\nprovider: {\n    type: \"openai\",\n    baseUrl: \"https://api.openai.com/v1\",\n    apiKey: process.env.OPENAI_API_KEY,\n}\n```\n\n### Azure OpenAI (native Azure endpoint)\n\nUse `type: \"azure\"` for endpoints at `*.openai.azure.com`:\n\n```typescript\nprovider: {\n    type: \"azure\",\n    baseUrl: \"https://my-resource.openai.azure.com\",  // Just the host\n    apiKey: process.env.AZURE_OPENAI_KEY,\n    azure: {\n        apiVersion: \"2024-10-21\",\n    },\n}\n```\n\n### Microsoft Foundry (OpenAI-compatible endpoint)\n\nFor Microsoft Foundry deployments with `/openai/v1/` endpoints, use `type: \"openai\"`:\n\n```typescript\nprovider: {\n    type: \"openai\",\n    baseUrl: \"https://<resource-name>.openai.azure.com/openai/v1/\",\n    apiKey: process.env.FOUNDRY_API_KEY,\n    wireApi: \"responses\",  // For GPT-5 series models\n}\n```\n\n### Ollama (local)\n\n```typescript\nprovider: {\n    type: \"openai\",\n    baseUrl: \"http://localhost:11434/v1\",\n    // No apiKey needed for local Ollama\n}\n```\n\n### Microsoft Foundry Local\n\n[Microsoft Foundry Local](https://foundrylocal.ai) lets you run AI models locally on your own device with an OpenAI-compatible API. Install it via the Foundry Local CLI, then point the SDK at your local endpoint:\n\n```typescript\nprovider: {\n    type: \"openai\",\n    baseUrl: \"http://localhost:<PORT>/v1\",\n    // No apiKey needed for local Foundry Local\n}\n```\n\n> \\[!NOTE]\n> Foundry Local starts on a **dynamic port**—the port is not fixed. Use `foundry service status` to confirm the port the service is currently listening on, then use that port in your `baseUrl`.\n\nTo get started with Foundry Local:\n\n```bash\n# Windows: Install Foundry Local CLI (requires winget)\nwinget install Microsoft.FoundryLocal\n\n# macOS / Linux: see https://foundrylocal.ai for installation instructions\n# List available models\nfoundry model list\n\n# Run a model (starts the local server automatically)\nfoundry model run phi-4-mini\n\n# Check the port the service is running on\nfoundry service status\n```\n\n### Anthropic\n\n```typescript\nprovider: {\n    type: \"anthropic\",\n    baseUrl: \"https://api.anthropic.com\",\n    apiKey: process.env.ANTHROPIC_API_KEY,\n}\n```\n\n### Bearer token authentication\n\nSome providers require bearer token authentication instead of API keys. Supply a static token with `bearerToken`, or supply a `bearerTokenProvider` callback that the GitHub Copilot SDK runtime invokes before outbound provider requests. The callback or identity library it wraps manages token caching and refresh.\n\nUse `bearerToken` when your application already has a token:\n\n```typescript\nprovider: {\n    type: \"openai\",\n    baseUrl: \"https://<resource-name>.openai.azure.com/openai/v1/\",\n    bearerToken: process.env.MY_BEARER_TOKEN,  // Sets Authorization header\n}\n```\n\n> \\[!NOTE]\n> The `bearerToken` option accepts a **static token string** only. The SDK does not refresh this token automatically. If your token expires, requests will fail and you'll need to create a new session with a fresh token.\n\nUse `bearerTokenProvider` to acquire tokens on demand:\n\n<!-- docs-validate: skip -->\n\n```typescript\nprovider: {\n    type: \"openai\",\n    baseUrl: \"https://my-custom-endpoint.example.com/v1\",\n    bearerTokenProvider: async () => {\n        return await acquireBearerToken();\n    },\n}\n```\n\nFor more details about acquiring and refreshing Microsoft Entra bearer tokens, see [Azure managed identity with BYOK](/en/copilot/how-tos/copilot-sdk/setup/azure-managed-identity).\n\n## Custom model listing\n\nWhen using BYOK, the CLI server may not know which models your provider supports. You can supply a custom `onListModels` handler at the client level so that `client.listModels()` returns your provider's models in the standard `ModelInfo` format. This lets downstream consumers discover available models without querying the CLI.\n\n<div class=\"ghd-codetabs\">\n<div class=\"ghd-codetab\" data-lang=\"typescript\" data-label=\"TypeScript\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">TypeScript</div>\n\n```typescript\nimport { CopilotClient } from \"@github/copilot-sdk\";\nimport type { ModelInfo } from \"@github/copilot-sdk\";\n\nconst client = new CopilotClient({\n    onListModels: () => [\n        {\n            id: \"my-custom-model\",\n            name: \"My Custom Model\",\n            capabilities: {\n                supports: { vision: false, reasoningEffort: false },\n                limits: { max_context_window_tokens: 128000 },\n            },\n        },\n    ],\n});\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"python\" data-label=\"Python\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Python</div>\n\n```python\nfrom copilot import CopilotClient\nfrom copilot.client import ModelInfo, ModelCapabilities, ModelSupports, ModelLimits\n\nclient = CopilotClient(\n    on_list_models=lambda: [\n        ModelInfo(\n            id=\"my-custom-model\",\n            name=\"My Custom Model\",\n            capabilities=ModelCapabilities(\n                supports=ModelSupports(vision=False, reasoning_effort=False),\n                limits=ModelLimits(max_context_window_tokens=128000),\n            ),\n        )\n    ],\n)\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"go\" data-label=\"Go\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Go</div>\n\n```golang\npackage main\n\nimport (\n    \"context\"\n    copilot \"github.com/github/copilot-sdk/go\"\n)\n\nfunc main() {\n    client := copilot.NewClient(&copilot.ClientOptions{\n        OnListModels: func(ctx context.Context) ([]copilot.ModelInfo, error) {\n            return []copilot.ModelInfo{\n                {\n                    ID:   \"my-custom-model\",\n                    Name: \"My Custom Model\",\n                    Capabilities: copilot.ModelCapabilities{\n                        Supports: copilot.ModelSupports{Vision: false, ReasoningEffort: false},\n                        Limits:   copilot.ModelLimits{MaxContextWindowTokens: copilot.Int(128000)},\n                    },\n                },\n            }, nil\n        },\n    })\n    _ = client\n}\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"dotnet\" data-label=\".NET\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">.NET</div>\n\n```csharp\nusing GitHub.Copilot;\n\nvar client = new CopilotClient(new CopilotClientOptions\n{\n    OnListModels = (ct) => Task.FromResult<IList<ModelInfo>>(new List<ModelInfo>\n    {\n        new()\n        {\n            Id = \"my-custom-model\",\n            Name = \"My Custom Model\",\n            Capabilities = new ModelCapabilities\n            {\n                Supports = new ModelSupports { Vision = false, ReasoningEffort = false },\n                Limits = new ModelLimits { MaxContextWindowTokens = 128000 }\n            }\n        }\n    })\n});\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"java\" data-label=\"Java\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Java</div>\n\n```java\nimport com.github.copilot.CopilotClient;\nimport com.github.copilot.rpc.*;\nimport java.util.List;\nimport java.util.concurrent.CompletableFuture;\n\nvar client = new CopilotClient(new CopilotClientOptions()\n    .setOnListModels(() -> CompletableFuture.completedFuture(List.of(\n        new ModelInfo()\n            .setId(\"my-custom-model\")\n            .setName(\"My Custom Model\")\n            .setCapabilities(new ModelCapabilities()\n                .setSupports(new ModelSupports().setVision(false).setReasoningEffort(false))\n                .setLimits(new ModelLimits().setMaxContextWindowTokens(128000)))\n    )))\n);\n```\n\n</div>\n\n</div>\n\nResults are cached after the first call, just like the default behavior. The handler completely replaces the CLI's `models.list` RPC—no fallback to the server occurs.\n\n## Limitations\n\n### Feature limitations\n\nSome Copilot features may behave differently with BYOK:\n\n* **Model availability** - Only models supported by your provider are available\n* **Rate limiting** - Subject to your provider's rate limits, not Copilot's\n* **Usage tracking** - Usage is tracked by your provider, not GitHub Copilot\n* **Premium requests** - Do not count against Copilot premium request quotas\n\n### Provider-specific limitations\n\n| Provider                                           | Limitations                                                                    |\n| -------------------------------------------------- | ------------------------------------------------------------------------------ |\n| [Microsoft Foundry Local](https://foundrylocal.ai) | Local only; model availability depends on device hardware; no API key required |\n| Ollama                                             | No API key; local only; model support varies                                   |\n| OpenAI                                             | Subject to OpenAI rate limits and quotas                                       |\n\n## Troubleshooting\n\n### \"Model not specified\" error\n\nWhen using BYOK, the `model` parameter is **required**:\n\n```typescript\n// ❌ Error: Model required with custom provider\nconst session = await client.createSession({\n    provider: { type: \"openai\", baseUrl: \"...\" },\n});\n\n// ✅ Correct: Model specified\nconst session = await client.createSession({\n    model: \"gpt-4\",  // Required!\n    provider: { type: \"openai\", baseUrl: \"...\" },\n});\n```\n\n### Azure endpoint type confusion\n\nFor Azure OpenAI endpoints (`*.openai.azure.com`), use the correct type:\n\n```typescript\n// ❌ Wrong: Using \"openai\" type with native Azure endpoint\nprovider: {\n    type: \"openai\",  // This won't work correctly\n    baseUrl: \"https://my-resource.openai.azure.com\",\n}\n\n// ✅ Correct: Using \"azure\" type\nprovider: {\n    type: \"azure\",\n    baseUrl: \"https://my-resource.openai.azure.com\",\n}\n```\n\nHowever, if your Microsoft Foundry deployment provides an OpenAI-compatible endpoint path (for example, `/openai/v1/`), use `type: \"openai\"`:\n\n```typescript\n// ✅ Correct: OpenAI-compatible Microsoft Foundry endpoint\nprovider: {\n    type: \"openai\",\n    baseUrl: \"https://your-resource.openai.azure.com/openai/v1/\",\n}\n```\n\n### Connection refused (Ollama)\n\nEnsure Ollama is running and accessible:\n\n```bash\n# Check Ollama is running\ncurl http://localhost:11434/v1/models\n\n# Start Ollama if not running\nollama serve\n```\n\n### Connection refused (Foundry Local)\n\nFoundry Local uses a dynamic port that may change between restarts. Confirm the active port:\n\n```bash\n# Check the service status and port\nfoundry service status\n```\n\nUpdate your `baseUrl` to match the port shown in the output. If the service is not running, start a model to launch it:\n\n```bash\nfoundry model run phi-4-mini\n```\n\n### Authentication failed\n\n1. Verify your API key is correct and not expired\n2. Check the `baseUrl` matches your provider's expected format\n3. For bearer tokens, ensure the full token is provided (not just a prefix)\n\n## Next steps\n\n* [Authentication](/en/copilot/how-tos/copilot-sdk/auth) - Learn about all authentication methods\n* [Build your first Copilot-powered app](/en/copilot/how-tos/copilot-sdk/getting-started) - Build your first Copilot-powered app"}