Using an inference gateway
Background: How fullsend authenticates.
Before you start: who you need. This route takes up to three roles (Who does what):
- Gateway operator, the provider admin: runs the gateway (operator guide) and hands over the gateway URL, the audience, and confirmation that your repository is on the gateway's allowed list. None of them is a secret. In the
api-keymode the operator hands over a gateway key instead of the audience.- Repo admin: writes the
inference.gatewayblock with those values and lists the models the agents use (Write the block). In theapi-keymode, also stores the key as a repository secret.- Repo writer: adds a workflow with
id-token: writeto run the live check in Check the block. Optional.
Agents on the pi, Claude Code and Codex runtimes can reach their models through an OpenAI/Anthropic-compatible inference gateway that trusts the job's GitHub Actions OIDC token (ADR 0137). Codex needs a gateway that serves the Responses API. The gateway holds the upstream provider credential, so on the recommended path your repository stores no reusable key. This page follows the agentgateway setup in the operator guide. The flags and fields are listed in fullsend github setup and the config reference; this page shows how they fit together.
The examples use example-org/example-repo and https://gateway.example.com.
Prerequisites
For the repo admin:
- The gateway URL and the audience from the operator, and confirmation that your repository is on the allowed list.
- A repository set up with fullsend on the pi, codex or Claude Code runtime. pi needs the model list below; codex and Claude Code do not need one. See Claude Code § Inference gateway route and Codex › Models through an inference gateway.
- The latest fullsend CLI.
The config block
The route is one block in .fullsend/config.yaml:
inference:
gateway:
url: https://gateway.example.com
audience: fullsend-inference
models:
claude-sonnet-4-6:
api: anthropic-messages
gemini-2.5-flash:
api: openai-completionsurlis the gateway's origin:https, port 443. If the gateway serves its API under a path prefix (for example behind a shared ingress), add the prefix without the trailing/v1:https://gateway.example.com/team-a. The runner adds the/v1/...paths itself.audienceis the string the operator configured on the gateway, such asfullsend-inference. It is an agreed value, not necessarily the URL, and must match the gateway's character for character.- The model list names every model the agents may call through the gateway, and the API the gateway serves it on:
anthropic-messagesfor Claude,openai-completionsfor Gemini and open-weight models,openai-responsesfor GPT. pi runs offline and cannot ask the gateway for its models, so agateway/model needs this list. Write it inline asmodels, or pointmodels_fileat a pi-inference-gateway config file in the repository. Set one or the other, not both.
An agent selects the route through its model: gateway/claude-sonnet-4-6 goes through the gateway. openai/ models and every other provider keep their own routes, so the block moves no existing model onto the gateway. One run can mix them.
Choosing auth
The block's auth field names the credential the runner presents. There is no fallback between the modes: if the credential for the chosen mode is missing, or the gateway refuses it, the run fails.
auth: oidcis the default and the recommended mode. It is direct OIDC trust (no token exchange): the job's GitHub OIDC token, minted foraudience, is itself the credential the gateway validates, and no reusable key is stored anywhere. The runner refreshes the token before it expires, so long runs keep working.auth: api-key: use with caution. It is for a gateway that cannot trust forge OIDC. The runner reads the gateway key fromFULLSEND_INFERENCE_GATEWAY_API_KEY: a repository secret in CI, or the environment on a local run. The block needs noaudiencein this mode. It is a supported mode, but not the primary or safest one, because it relies on a long-lived secret: a leaked key works until the operator revokes it. Useoidcwhenever the gateway supports it (ADR 0138).
The api-key mode also applies to local runs: a local fullsend run with FULLSEND_INFERENCE_GATEWAY_API_KEY in its environment takes the same route as CI. The oidc mode applies only to runs that have a GitHub OIDC endpoint, so a local run without one leaves the route to the harness-plugin setup.
For api-key, store the key the operator gave you as a repository secret:
fullsend github set example-org/example-repo FULLSEND_INFERENCE_GATEWAY_API_KEY '<gateway-key>' • Setting repo secret FULLSEND_INFERENCE_GATEWAY_API_KEY on example-org/example-repo
✓ Set repo secret FULLSEND_INFERENCE_GATEWAY_API_KEY on example-org/example-repoOr, with the GitHub CLI, which prompts for the value so it stays out of your shell history:
gh secret set FULLSEND_INFERENCE_GATEWAY_API_KEY -R example-org/example-repo? Paste your secret:Write the block
Pass the operator's values and your models to fullsend github setup:
fullsend github setup example-org/example-repo \
--inference-gateway-url https://gateway.example.com \
--inference-gateway-audience fullsend-inference \
--inference-gateway-model claude-sonnet-4-6=anthropic-messages \
--inference-gateway-model gemini-2.5-flash=openai-completions⚡ fullsend <version>
Autonomous agentic development for Git-hosted organizations
→ Setting up per-repo fullsend for example-org/example-repo
Updating existing .fullsend/config.yaml: inference.gateway (other keys kept; comments are not preserved)
• Checking token permissions
✓ Token permissions verified
• Creating scaffold PR for example-org/example-repo (target: main)
User example-user has write access — pushing directly to example-org/example-repo
✓ Created PR #1: https://github.com/example-org/example-repo/pull/1
Merge the PR to apply these changes
• Configuring repository variables
✓ Set 5 repository variables
• Configuring repository secrets
✓ Set 0 repository secrets
✓ Per-repo setup complete for example-org/example-repoUse --inference-gateway-models-file <file> instead of the --inference-gateway-model flags to commit a pi-inference-gateway config file. Setup changes only the keys you pass and opens a pull request with the change. The pull request's config.yaml diff, on a repository already set up with --runtime pi:
@@ -20,3 +20,12 @@ create_issues:
repos:
- example-org/example-repo
- fullsend-ai/fullsend
+inference:
+ gateway:
+ url: https://gateway.example.com
+ audience: fullsend-inference
+ models:
+ claude-sonnet-4-6:
+ api: anthropic-messages
+ gemini-2.5-flash:
+ api: openai-completionsFor the key mode, pass --inference-gateway-auth api-key and leave out --inference-gateway-audience:
fullsend github setup example-org/example-repo \
--inference-gateway-url https://gateway.example.com \
--inference-gateway-auth api-key \
--inference-gateway-model claude-sonnet-4-6=anthropic-messages \
--inference-gateway-model gemini-2.5-flash=openai-completionsThe output is the same as above. The pull request's diff:
@@ -20,3 +20,12 @@ create_issues:
repos:
- example-org/example-repo
- fullsend-ai/fullsend
+inference:
+ gateway:
+ url: https://gateway.example.com
+ auth: api-key
+ models:
+ claude-sonnet-4-6:
+ api: anthropic-messages
+ gemini-2.5-flash:
+ api: openai-completionsMerge the pull request to apply the change.
Check the block
Run fullsend inference gateway status from a checkout of the repository:
fullsend inference gateway status example-org/example-repoIn the oidc mode, outside GitHub Actions, it checks the block and stops:
⚡ fullsend <version>
Autonomous agentic development for Git-hosted organizations
→ Inference Gateway Status: example-org/example-repo
url: https://gateway.example.com (from config.yaml)
audience: fullsend-inference (from config.yaml)
auth: oidc (default)
models: claude-sonnet-4-6, gemini-2.5-flash (from config.yaml)
✓ url and audience are set
Not inside a GitHub Actions job with id-token: write
The assertion and gateway request can only be tested from a GitHub Actions workflowRun the same command in a GitHub Actions job in the repository, with id-token: write, and it also fetches one OIDC token for the audience and lists the models the gateway allows the repository (GET <url>/v1/models, under the URL's path prefix if it has one). It never prints the token.
Not executed here: this walkthrough had no gateway to check against, so the output for a healthy gateway is not shown. A maintainer should add it from a run in GitHub Actions.
In the api-key mode the command reports the mode, warns that it relies on a long-lived secret, and says whether FULLSEND_INFERENCE_GATEWAY_API_KEY is set (never its value). It does not call the gateway:
⚡ fullsend <version>
Autonomous agentic development for Git-hosted organizations
→ Inference Gateway Status: example-org/example-repo
url: https://gateway.example.com (from config.yaml)
audience: (not set)
auth: api-key (from config.yaml)
models: claude-sonnet-4-6, gemini-2.5-flash (from config.yaml)
✓ url is set
! auth is api-key: the route relies on a long-lived gateway API key (FULLSEND_INFERENCE_GATEWAY_API_KEY); use it with caution, and prefer auth: oidc when the gateway can validate forge OIDC tokens
FULLSEND_INFERENCE_GATEWAY_API_KEY is not set in this environment; a run in the api-key mode fails without itA repository with no block fails the check:
⚡ fullsend <version>
Autonomous agentic development for Git-hosted organizations
→ Inference Gateway Status: example-org/example-repo
url: (not set)
audience: (not set)
auth: oidc (default)
models: (not set; pi gateway/ models need a model list)
✗ No inference.gateway block configured
Configure it with 'fullsend github setup --inference-gateway-url <url> --inference-gateway-audience <aud>'
Error: no inference.gateway block configured for example-org/example-repoThe workflow permission
In the oidc mode the job that runs the agent needs id-token: write, or it cannot mint the token. The workflow fullsend github setup installs already grants it on its dispatch job:
permissions:
actions: write
id-token: writeIf you call fullsend from a workflow of your own, add id-token: write to that job's permissions.
Running locally
A block with auth: api-key also applies to local runs; see Choosing auth. For pi, the harness-plugin path for local runs is in Running agents locally § Using an inference gateway.
Troubleshooting the gateway route
| You see | Cause | Action |
|---|---|---|
inference.gateway is partial: missing audience (the oidc mode needs url and audience), or from status: inference.gateway is partially configured: missing audience | The block, after its layers merge, lacks url, or lacks audience in the oidc mode. | Set the missing field with fullsend github setup --inference-gateway-url or --inference-gateway-audience, or remove the block. |
pi gateway/ models need a model list: set inference.gateway.models or inference.gateway.models_file (pi runs offline and cannot discover gateway models) | An agent uses a gateway/ model, and the block has no model list. | List the models with --inference-gateway-model id=api or --inference-gateway-models-file. |
inference.gateway.auth is "api-key" but FULLSEND_INFERENCE_GATEWAY_API_KEY is not set; set it as a forge secret (or in the local environment) | The block is in the api-key mode and the run has no key. | Set the repository secret as shown in Choosing auth, or export the variable for a local run. |
From status: Gateway refused the assertion (HTTP 401) or (HTTP 403); in a run, the model call fails with the gateway's 401 or 403 | The gateway did not accept the credential: a different audience, a repository not on its allowed list, or a revoked key. | Check the audience and allowed repositories with the operator. The gateway's exact error text and its fixes are in the operator guide's troubleshooting table. |
Any other gateway error, such as authentication failure: ... or model_authorization_denied | The gateway refused the request for its own reasons. | See the operator guide's troubleshooting table. |
