{"meta":{"title":"Server-to-server authentication","intro":"Use a short-lived installation access token when a service needs to make Copilot requests on behalf of an organization without a user's credentials. In GitHub Actions, use the built-in GITHUB_TOKEN instead.","product":"GitHub Copilot","breadcrumbs":[{"href":"/en/enterprise-cloud@latest/copilot","title":"GitHub Copilot"},{"href":"/en/enterprise-cloud@latest/copilot/how-tos","title":"How-tos"},{"href":"/en/enterprise-cloud@latest/copilot/how-tos/copilot-sdk","title":"Copilot SDK"},{"href":"/en/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/auth","title":"Authentication"},{"href":"/en/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/auth/server-to-server-tokens","title":"Server-to-server tokens"}],"documentType":"article"},"body":"# Server-to-server authentication\n\nUse a short-lived installation access token when a service needs to make Copilot requests on behalf of an organization without a user's credentials. In GitHub Actions, use the built-in GITHUB_TOKEN instead.\n\n<!-- markdownlint-disable GHD046 GHD005 -->\n\n<!-- Suppressed: GHD046 (outdated release terminology), GHD005 (hardcoded data variable) -->\n\n## GitHub Actions\n\nFor workflows in an organization-owned repository, grant the built-in token permission to make Copilot requests:\n\n```yaml\npermissions:\n  contents: read\n  copilot-requests: write\n\njobs:\n  copilot:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - run: your-application\n        env:\n          GITHUB_TOKEN: $\n```\n\nThe organization's **Allow use of Copilot CLI billed to the organization** policy must be enabled. This approach needs no GitHub App or stored authentication secret. For details, see [Using Copilot CLI in GitHub Actions with GITHUB\\_TOKEN](/en/enterprise-cloud@latest/copilot/how-tos/copilot-cli/use-copilot-cli-in-actions).\n\n## Other services and CI systems\n\nFor services outside GitHub Actions:\n\n1. Create a GitHub App with the **Copilot Requests** repository permission set to **Read & write**.\n\n2. Install it on the organization that should be billed. The current Copilot permission check requires **All repositories** access.\n\n3. [Generating an installation access token for a GitHub App](/en/enterprise-cloud@latest/apps/creating-github-apps/authenticating-with-a-github-app/generating-an-installation-access-token-for-a-github-app) with a repository ID and the Copilot permission:\n\n   ```json\n   {\n     \"repository_ids\": [123456789],\n     \"permissions\": {\n       \"copilot_requests\": \"write\"\n     }\n   }\n   ```\n\n4. Pass the resulting `ghs_` token to the runtime as `COPILOT_GITHUB_TOKEN`.\n\nThe organization must be enabled for Copilot requests from GitHub App installations. Installation tokens expire after one hour.\n\n> \\[!WARNING]\n> Do not pass an installation token through the SDK's `gitHubToken`, `github_token`, or equivalent option. That option is for user tokens. Installation tokens must use the runtime environment authentication path.\n\n## Configure the runtime\n\nThe following examples assume the minted token is in `INSTALLATION_TOKEN`. They pass it only to the child runtime and disable fallback to stored user credentials.\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, RuntimeConnection } from \"@github/copilot-sdk\";\n\nconst token = process.env.INSTALLATION_TOKEN;\nif (!token) throw new Error(\"INSTALLATION_TOKEN is required\");\n\nconst client = new CopilotClient({\n    connection: RuntimeConnection.forStdio(),\n    env: {\n        ...process.env,\n        COPILOT_GITHUB_TOKEN: token,\n    },\n    useLoggedInUser: false,\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\nimport os\n\nfrom copilot import CopilotClient, RuntimeConnection\n\nclient = CopilotClient(\n    connection=RuntimeConnection.for_stdio(),\n    env={**os.environ, \"COPILOT_GITHUB_TOKEN\": os.environ[\"INSTALLATION_TOKEN\"]},\n    use_logged_in_user=False,\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\t\"log\"\n\t\"os\"\n\n\tcopilot \"github.com/github/copilot-sdk/go\"\n)\n\nfunc main() {\n\ttoken, ok := os.LookupEnv(\"INSTALLATION_TOKEN\")\n\tif !ok {\n\t\tlog.Fatal(\"INSTALLATION_TOKEN is required\")\n\t}\n\tclient := copilot.NewClient(&copilot.ClientOptions{\n\t\tConnection:      copilot.StdioConnection{},\n\t\tEnv:             append(os.Environ(), \"COPILOT_GITHUB_TOKEN=\"+token),\n\t\tUseLoggedInUser: copilot.Bool(false),\n\t})\n\t_ = client\n}\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"rust\" data-label=\"Rust\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Rust</div>\n\n```rust\nuse github_copilot_sdk::{ClientOptions, Transport};\n\nfn main() {\n    let token = std::env::var(\"INSTALLATION_TOKEN\").expect(\"INSTALLATION_TOKEN is required\");\n    let options = ClientOptions::new()\n        .with_transport(Transport::Stdio)\n        .with_env([(\"COPILOT_GITHUB_TOKEN\", token)])\n        .with_use_logged_in_user(false);\n    drop(options);\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 System.Collections;\nusing GitHub.Copilot;\n\nvar token = Environment.GetEnvironmentVariable(\"INSTALLATION_TOKEN\")\n    ?? throw new InvalidOperationException(\"INSTALLATION_TOKEN is required\");\nvar environment = Environment.GetEnvironmentVariables()\n    .Cast<DictionaryEntry>()\n    .ToDictionary(entry => (string)entry.Key, entry => entry.Value?.ToString() ?? \"\");\nenvironment[\"COPILOT_GITHUB_TOKEN\"] = token;\n\nawait using var client = new CopilotClient(new CopilotClientOptions\n{\n    Connection = RuntimeConnection.ForStdio(),\n    Environment = environment,\n    UseLoggedInUser = false,\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.CopilotClientOptions;\nimport java.util.HashMap;\nimport java.util.Objects;\n\nvar environment = new HashMap<>(System.getenv());\nvar token = Objects.requireNonNull(\n    System.getenv(\"INSTALLATION_TOKEN\"), \"INSTALLATION_TOKEN is required\");\nenvironment.put(\"COPILOT_GITHUB_TOKEN\", token);\n\ntry (var client = new CopilotClient(new CopilotClientOptions()\n        .setEnvironment(environment)\n        .setUseLoggedInUser(false))) {\n    // Use the client.\n}\n```\n\n</div>\n\n</div>\n\nFor in-process FFI, set `COPILOT_GITHUB_TOKEN` in the host environment before loading the runtime; per-client environment options are not supported. For an existing runtime URI, set it on that runtime process.\n\n## Refresh tokens\n\nMint a new installation token before the current token expires. For a child process, restart the SDK client with the new environment. For an in-process or existing runtime, restart the host runtime with the new token.\n\n## Billing\n\nUsage is attributed and billed to the account that owns the GitHub App installation. Use an organization installation for organization billing; a user-account installation attributes usage to that user.\n\n## Troubleshooting\n\n| Symptom                                                                              | Check                                                                                             |\n| ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- |\n| `401 Unauthorized`                                                                   | Confirm the organization supports GitHub App installation authentication for Copilot.             |\n| `403 Resource not accessible by integration` or an error mentioning user information | Confirm the installation token is in `COPILOT_GITHUB_TOKEN`, not the SDK's explicit token option. |\n| `403 Forbidden` from the Copilot API                                                 | Confirm the token request contains `repository_ids` and `copilot_requests: write`.                |\n| `403 Forbidden` with the required token request                                      | Confirm the app installation has **All repositories** access, then mint a new token.              |\n| Requested model is unavailable                                                       | Confirm the organization's Copilot policy allows the model and the bundled runtime supports it.   |\n| Wrong account billed                                                                 | Confirm the installation belongs to the intended organization.                                    |\n\n## Further reading\n\n* [Authentication](/en/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/auth/authenticate): other authentication methods and priority\n* [Generating an installation access token for a GitHub App](/en/enterprise-cloud@latest/apps/creating-github-apps/authenticating-with-a-github-app/generating-an-installation-access-token-for-a-github-app): GitHub App token creation"}