Getting Started with the GitHub Copilot SDK — Part 11: Bring Your Own Key (BYOK) with OpenRouter

This is Part 11 — the final part of the series. The companion code lives on GitHub at
egarim/GettingStartedWithGithubCopilotSDK.
We've spent ten parts on one assumption: you talk to Copilot's models.
This part breaks that assumption.
With Bring Your Own Key (BYOK) you can point the SDK at any provider — OpenRouter, Anthropic, Azure OpenAI, Google AI Studio, xAI — and use those models exactly like the built-in ones.
No second SDK. No hand-rolled HTTP calls. One config line.
The trick: there is no trick
Here's the part that surprised me.
Your code doesn't change. At all.
The key lives in GitHub, not in your app:
GitHub Enterprise / Org Settings
AI Controls > Copilot > Custom Models > Add API Key
Provider: OpenRouter
API Key: sk-or-v1-xxxxxxxx
Models: openai/gpt-4o-mini, anthropic/claude-3.5-haiku
Your admin pastes the key once. The custom models then appear automatically in
ListModelsAsync(). You select one with SessionConfig.Model — same as always.
The key never touches your code. You never write a
using OpenRouter;.
That's the whole feature.
Auth that works in CI too
Small but important detail in the first step: a dual auth strategy.
If there's a GITHUB_TOKEN in the environment, use it as a PAT. Otherwise fall back to
your VS Code login.
var token = Environment.GetEnvironmentVariable("GITHUB_TOKEN");
var client = new CopilotClient(new CopilotClientOptions
{
GithubToken = string.IsNullOrWhiteSpace(token) ? null : token,
UseLoggedInUser = string.IsNullOrWhiteSpace(token), // fall back to VS Code login
Logger = loggerFactory.CreateLogger<CopilotClient>()
});
await client.StartAsync();
UseLoggedInUser is the friendly path on your laptop. The PAT is the path in CI/CD,
where there's no VS Code to lean on.
See your custom models
This is where BYOK shows up. List the models and your provider's entries are just there,
mixed in with the built-ins:
var models = await client.ListModelsAsync();
Console.WriteLine(quot; Total models: {models.Count}");
foreach (var m in models)
Console.WriteLine(quot; {m.Id,-45} {m.Name}");
// The custom BYOK models appear in this list!
No flag, no filter, no special call. If the admin enabled it, it's in the list.
Pick one and chat
To use a model — any model — set Model on the session config:
var chosenModel = "gpt-4o"; // or any id from ListModelsAsync()
await using var session = await client.CreateSessionAsync(new SessionConfig
{
Model = chosenModel
});
var answer = await session.SendAndWaitAsync(
new MessageOptions { Prompt = "What model are you? Answer in one short sentence." });
Console.WriteLine(quot; {answer?.Data.Content}");
Swap "gpt-4o" for "anthropic/claude-3.5-haiku" and nothing else changes. That's the
payoff of routing through Copilot: the calling code stays identical across providers.
Compare models side by side
Because selecting a model is one line, comparing them is a loop. Same prompt, every model:
var modelsToTry = models.Select(m => m.Id).Take(4).ToList();
const string prompt = "What is the capital of Australia? One sentence.";
foreach (var id in modelsToTry)
{
try
{
await using var session = await client.CreateSessionAsync(new SessionConfig { Model = id });
var answer = await session.SendAndWaitAsync(new MessageOptions { Prompt = prompt });
Console.WriteLine(quot; {id,-45} -> {answer?.Data.Content?.Trim()}");
}
catch (Exception ex)
{
Console.WriteLine(quot; {id,-45} -> Error: {ex.Message.Split('\n')[0]}");
}
}
Wrap each call in
try/catch— some models in the list might not be available to you, and
you don't want one bad id to kill the whole comparison.
This is a genuinely useful harness: drop a real prompt in and eyeball quality, speed, and
style across four models in one run.
Everything else still works
The best part — the rest of the SDK doesn't care which model you picked. Tools,
streaming, the interactive loop: all of it works with BYOK models too.
Streaming is the same AssistantMessageDeltaEvent you already know — just with Model set
to a custom id:
await using var session = await client.CreateSessionAsync(new SessionConfig
{
Model = chosenModel,
Streaming = true
});
session.On(evt =>
{
if (evt is AssistantMessageDeltaEvent delta)
Console.Write(delta.Data.DeltaContent);
if (evt is SessionIdleEvent) idleTcs.TrySetResult(true);
});
Function calling, multi-turn conversations, error events — none of it changes. BYOK
isn't a separate mode. It's just more entries in your model list.
Takeaways
- BYOK lets you use any provider's models through the Copilot SDK — OpenRouter, Anthropic, Azure OpenAI, Google, xAI — with no new SDK and no direct API calls.
- The API key lives in GitHub org/enterprise settings, never in your code. Your admin adds it once.
- Custom models appear automatically in
ListModelsAsync(). Select one withSessionConfig.Model. - Your code doesn't change — tools, streaming, and the chat loop work identically across every model.
- Use a
GITHUB_TOKENPAT for CI,UseLoggedInUseron your laptop.
And that wraps the series. Across eleven parts we went from the bare client lifecycle all
the way to a multi-model, multi-provider agent — and the surface stayed small and
consistent the whole way. That consistency is the real lesson: once you know the client, the
session, and the event stream, swapping the model underneath is a one-liner.
Thanks for following along. Start from the top at the
series intro, and grab all
the runnable demos from the
course repo.