Clients - Dark-Alex-17/coyote GitHub Wiki
Coyote supports a large number of model providers (referred to as clients since Coyote is a client of these providers). In
order to use them, you must configure each one in the clients array in the global Coyote configuration file.
The location of the global Coyote configuration file varies between systems, so you can use the following command to locate your configuration file:
coyote --info | grep '^config_file' | awk '{print $2}'Coyote supports the following model client types:
- Azure AI Foundry
- AWS Bedrock
- Anthropic Claude
- Cohere
- Google Gemini
- OpenAI
- OpenAI-Compatible (e.g. Ollama)
- GCP Vertex AI
In addition to the settings detailed below, each client may have additional settings specific to the provider. Check the example global configuration file to verify that your client has all the necessary fields defined.
Each client in Coyote has the same configuration settings available to them, with only special authentication fields added
for specific clients as necessary. They are each placed under the clients array in your global configuration file:
clients:
- name: client1
# ... client configuration ...
- name: client2
# ... client configuration ...The client metadata uniquely identifies the client in Coyote so you can reference it across your configurations. The available settings are listed below:
| Setting | Description |
|---|---|
name |
The name of the client (e.g. openai, gemini, etc.) |
auth |
Authentication method: oauth for OAuth, or omit to use api_key (see Authentication) |
models |
See the model settings documentation below |
extend_models |
Merge the models entries on top of the model catalog for this client instead of replacing it (see Extending vs. replacing the catalog). Optional, default false
|
patch |
See the client patch configuration documentation |
extra |
See the extra settings documentation below |
Be sure to also check provider-specific configurations for any extra fields that are added for authentication purposes.
The models array lists the available models from the model client. Each one has the following settings:
| Setting | Required | Model Type | Description |
|---|---|---|---|
name |
* | all |
The name of the model |
real_name |
all |
You can define model aliases via the name field. However, Coyote still needs to know the real name of the model so it can query it. For example: If you have name: gpt-alias, then you must also define real_name: gpt-oss:latest
|
|
type |
* | all |
The type of model. Coyote supports only 3 types of models:
|
input_price |
all |
The cost in USD per 1M tokens for each input sequence; Coyote will keep track of usage costs if this is defined | |
output_price |
all |
The cost in USD per 1M tokens of the model output; Coyote will keep track of usage costs if this is defined | |
cache_read_price |
all |
The cost in USD per 1M tokens for reading prompt-cache tokens; populated automatically from the model catalog where the sources publish it, and used in usage-cost tracking | |
cache_write_price |
all |
The cost in USD per 1M tokens for writing prompt-cache tokens; populated automatically from the model catalog where the sources publish it, and used in usage-cost tracking | |
patch |
all |
See the model-specific patch configuration documentation | |
max_input_tokens |
all |
The maximum number of input tokens for the model | |
max_output_tokens |
chat |
The maximum number of output tokens for the model | |
require_max_tokens |
chat |
Whether to enforce the max_output_tokens constraint. |
|
supports_vision |
chat |
Indicates if the model supports multimodal queries that would require vision (i.e. image recognition) | |
supports_function_calling |
chat |
Indicates if the model supports function calling | |
reasoning_levels |
chat |
A list of named reasoning effort levels the model accepts (e.g. [low, medium, high]). When non-empty, .reasoning and .set reasoning_effort become available in the REPL for this model |
|
default_reasoning_effort |
chat |
The effort level applied by default when reasoning_levels is non-empty and no explicit override is set. Must be one of the values listed in reasoning_levels
|
|
no_stream |
chat |
Enable or disable streaming API responses | |
no_system_message |
chat |
Controls whether the model supports system messages | |
system_prompt_prefix |
chat |
An additional prefix prompt to add to all system prompts to ensure consistent behavior across all interactions | |
max_tokens_per_chunk |
embedding |
The maximum chunk size supported by the embedding model | |
default_chunk_size |
embedding |
The default chunk size to use with the given model | |
max_batch_size |
embedding |
The maximum batch size that the given embedding model supports |
By default, listing any models in a client's models array replaces the client's
model catalog list entirely. The client offers exactly the models you
enumerate and nothing else. That's the right behavior when the endpoint only serves a few
specific models (e.g. a gateway exposing three fine-tunes), but it's inconvenient when you just
want to add a model, for example, when a corporate gateway that serves everything Anthropic
does plus a custom auto-claude route.
Set extend_models: true to merge instead:
clients:
- type: claude
name: gateway-claude
api_base: https://gateway.example.com/v1
extend_models: true
models:
- name: auto-claude
max_input_tokens: 200000
supports_function_calling: trueThe client now lists the full claude catalog plus auto-claude (custom entries are
appended after the catalog models). Because the definition lives in your config.yaml, it
survives catalog syncs and version upgrades, unlike hand edits to models-override.yaml,
which is machine-owned and rewritten wholesale by every sync.
Merge semantics and edge cases:
-
Collisions merge field-wise. A config entry whose
nameandtypematch a catalog model overrides just the fields you set; unset fields inherit the catalog values. -
Boolean capabilities can only be turned on, not off. Flags like
supports_visionmerge with OR, sosupports_vision: falseon a catalog model that declares vision is ignored. To disable a capability, use the default replace mode and define the model fully yourself. -
An empty
modelslist behaves as if the flag were unset. You get the catalog, exactly like today. -
No matching catalog provider -> config models only. If the client resolves to no catalog
provider (e.g. an
openai-compatibleclient whosenamedoesn't start with any catalog provider's name), there is nothing to extend and the config entries are used alone, silently.
The Anthropic-protocol clients (i.e. claude, bedrock (Anthropic models only), and vertexai (Claude models)) support
Anthropic prompt caching, and Coyote enables it
by default. Requests automatically place cache breakpoints on the system prompt, the tool definitions, and the two
most recent user messages, so long sessions and tool-heavy loops re-read the cached prefix each turn instead of paying
the full input-token price.
Set prompt_cache: false on the client to opt out:
clients:
- type: claude
prompt_cache: false # Optional - Anthropic prompt caching (default: true)Note
- On Bedrock, cache points are only added for Anthropic models (
anthropic.*/*claude*model IDs); requests for all other Bedrock models are unaffected. - Cache activity is visible in the REPL: the default right prompt shows a ⚡ segment with the tokens read from cache
on the most recent call (see the token usage prompt variables), and
.info sessionshows cumulative cache reads/writes for the session. - Coyote also tracks the real USD cost of each call (input/output/cache-read/cache-write tokens × the model's
catalog prices): the default right prompt shows the session's running
$total, and.info session/ session exports includetotal_cost. Cost is only reported when every billed component has a known price. No estimates. - Setting
prompt_cache: falseproduces request bodies byte-identical to Coyote versions that predate prompt caching.
The openai and openai-compatible clients can speak two different OpenAI wire protocols:
-
responses: The OpenAI Responses API ({api_base}/responses) -
chat: The classic Chat Completions API ({api_base}/chat/completions)
Select the wire explicitly with the wire_api field on the client:
clients:
- type: openai-compatible
name: groq
api_base: https://api.groq.com/openai/v1
api_key: '{{GROQ_API_KEY}}'
wire_api: responses # Optional - Possible values: responses and chatAny other value is a configuration error and fails loudly at load time.
When wire_api is not set, Coyote resolves the wire as follows:
| Client | Default wire |
|---|---|
openai with no custom api_base (stock api.openai.com) |
responses |
openai with a custom api_base
|
chat |
openai-compatible |
chat |
openai with Codex OAuth (auth: oauth, no api_base) |
responses (always) |
An explicit wire_api wins everywhere except on the Codex OAuth path: the ChatGPT Codex
backend only speaks the Responses API, so wire_api: chat on a Codex OAuth client is a
configuration error (remove it or configure an api_base).
Important
Migration note (default flip): stock openai clients previously used Chat Completions. If
you depend on the old wire, e.g. a proxy that only understands /chat/completions, or
body-inspecting middleware, pin it back with wire_api: chat on the client. If you have
request patches keyed for the chat wire, see
wire-aware patching: chat-keyed patches never
apply to responses bodies, so they must be migrated to the responses patch key to keep working
on the new default wire.
-
Coyote requests encrypted reasoning (
include: ["reasoning.encrypted_content"], withstore: false) and replays the returnedreasoningitems on subsequent tool-loop rounds, so reasoning models keep their chain-of-thought across function calls. These reasoning blocks are persisted in session files; sessions saved by this version cannot be loaded by older Coyote binaries once they contain them. -
Tool definitions are sent with
"strict": falseso Coyote's JSON-Schema tool declarations are accepted unmodified. When opting a third-party/v1/responsesendpoint in viawire_api: responses, be aware that strict servers may reject theinclude/strictparameters as unknown. If that happens, strip the offending parameter with a responses-keyed client patch (e.g. Groq's/openai/v1/responsesendpoint rejectsinclude), and works with:clients: - type: openai-compatible name: groq api_base: https://api.groq.com/openai/v1 wire_api: responses patch: responses: '.*': body: include: null
or simply stay on
wire_api: chat. -
Token usage (including cached input tokens) is reported on the responses wire and feeds the same cost tracking as the chat wire.
Coyote clients support two authentication methods: API keys and OAuth. Each client entry in your configuration must use one or the other.
Most clients authenticate using an API key. Simply set the api_key field directly or inject it from the
Coyote vault:
clients:
- type: claude
api_key: '{{ANTHROPIC_API_KEY}}'API keys can also be provided via environment variables named {CLIENT_NAME}_API_KEY (e.g. OPENAI_API_KEY,
GEMINI_API_KEY). See the environment variables documentation
for details.
For providers that support OAuth, you can authenticate using your existing subscription instead of an API key. This uses the OAuth 2.0 PKCE flow.
Step 1: Configure the client
Add a client entry with auth: oauth and no api_key:
clients:
- type: claude
name: my-claude-oauth
auth: oauthStep 2: Authenticate
Run the --authenticate flag with the client name:
coyote --authenticate my-claude-oauthOr if you have only one OAuth-configured client, you can omit the name:
coyote --authenticateAlternatively, you can use the REPL command .authenticate.
This opens your browser for the OAuth authorization flow. Depending on the provider, Coyote will either start a
temporary localhost server to capture the callback automatically (e.g. Gemini, OpenAI) or ask you to paste the authorization
code back into the terminal (e.g. Claude). Coyote stores the tokens in <coyote_cache_dir>/coyote/oauth and automatically refreshes
them when they expire.
Step 3: Use normally
Once authenticated, the client works like any other. Coyote uses the stored OAuth tokens automatically:
coyote -m my-claude-oauth:claude-sonnet-4-20250514 "Hello!"Note: You can have multiple clients for the same provider. For example: you can have one with an API key and another with OAuth. Use the
namefield to distinguish them.
OpenAI OAuth requires a ChatGPT Plus or Pro subscription. It uses the shared Codex CLI OAuth app registered with OpenAI, so no additional API credentials or app registration are needed.
When authenticating, Coyote starts a localhost server on port 1455 to capture the OAuth callback automatically. This port is
fixed by OpenAI's app registration and cannot be changed. Make sure port 1455 is available when you run
--authenticate/.authenticate.
When auth: oauth is set and no api_base is configured, Coyote automatically routes all requests through
chatgpt.com/backend-api/codex/responses (the OpenAI Responses API), which is what ChatGPT Plus/Pro subscribers have
access to. Because the Codex backend only speaks the Responses API, setting wire_api: chat on this client is a
configuration error. If you later add an api_key or a custom api_base, Coyote will use the standard
api.openai.com/v1 endpoint instead (see Wire API for which wire is used).
Example configuration:
clients:
- type: openai
name: openai-oauth
auth: oauthCoyote uses the following scopes for OAuth with Gemini:
- https://www.googleapis.com/auth/generative-language.peruserquota
- https://www.googleapis.com/auth/userinfo.email
- https://www.googleapis.com/auth/generative-language.retriever (Sensitive)
Since the generative-language.retriever scope is a sensitive scope, Google needs to verify Coyote, which requires full
branding (logo, official website, privacy policy, terms of service, etc.). The Coyote app is open-source and is designed
to be used as a simple CLI. As such, there's no terms of service or privacy policy associated with it, and thus Google
cannot verify Coyote.
So, when you kick off OAuth with Gemini, you may see a page similar to the following:

Simply click the Advanced link and click Go to Coyote (unsafe) to continue the OAuth flow.

xAI OAuth requires a SuperGrok or X Premium+ subscription. Coyote ships with the required OAuth endpoints and
client ID bundled in models.yaml, so you can enable OAuth by just adding auth: oauth to your xAI client entry. No
additional endpoints or app registration needed.
When authenticating, Coyote starts a localhost server on port 56121 to capture the OAuth callback automatically.
This port matches what xAI's OAuth app expects and cannot be changed. Make sure port 56121 is available when you run
--authenticate/.authenticate.
If the authorization succeeds but the token exchange returns 403, that indicates your account does not have a valid SuperGrok / X Premium+ subscription. This is not a Coyote configuration error.
Example configuration:
clients:
- type: openai-compatible
name: xai
api_base: https://api.x.ai/v1
auth: oauthFor any provider that speaks the OpenAI API shape (type: openai-compatible), you can enable OAuth by setting
auth: oauth and configuring an oauth: block. This section is your reference for what fields each OAuth flow needs.
Not needed for built-in providers (Claude, Gemini, OpenAI, xAI-when-using-bundled-defaults): those work with just
auth: oauth, meaning no fields are required. See the provider-specific notes above for details.
The claude, openai, and gemini clients can also take an inline oauth: block to authenticate against custom
endpoints (e.g. a private LLM gateway). See Custom OAuth Endpoints on Built-in Clients.
Look at your provider's own login docs or CLI for signal words:
| What your provider does | Flow to configure | Section |
|---|---|---|
Opens your browser, redirects to a http://localhost or http://127.0.0.1 URL |
Authorization Code + PKCE (loopback callback) | Flow 1, Mode A |
Opens your browser, redirects to a https://... URL you'd copy from the address bar |
Authorization Code + PKCE (browser-paste) | Flow 1, Mode B |
| Displays a short user code you enter at a URL, with no browser callback | Device Authorization Grant (RFC 8628) | Flow 2 |
No user at all; just client_id + client_secret exchanged headlessly |
Client Credentials (RFC 6749 §4.4) | Flow 3 |
If your provider's OAuth app is already registered with a specific redirect URI, Coyote's routing follows it: put the
value in redirect_uri and Coyote does the right thing based on the URL shape (loopback vs public).
Standard OAuth 2.0 with PKCE (RFC 7636). Coyote opens the authorize URL in your browser, receives back an authorization code, and exchanges it for tokens. Refresh tokens are supported (silently refreshed on expiry).
Coyote picks between two delivery modes automatically based on what you set for the callback:
Coyote binds a TCP listener on 127.0.0.1 and the browser redirects there directly. This is the smoothest UX (i.e. no manual copy/paste), but requires that the provider's OAuth app allows a loopback redirect URI.
Required fields:
-
flow: pkce(or omit. This is the default) -
client_id: your OAuth app's client ID -
authorize_url: where the browser goes to authorize -
token_url: where Coyote exchanges the code for tokens
Callback config (pick one):
-
redirect_port: <u16>: if the provider registered a specific port (e.g.56121for xAI). Coyote synthesizeshttp://127.0.0.1:<port>/callback. -
redirect_uri: http://127.0.0.1:<port>/<path>: full custom loopback URL (overridesredirect_port). - Omit both: Coyote picks an ephemeral port on 127.0.0.1 (only works if the provider allows arbitrary loopback ports).
Optional fields:
-
scopes: [scope1, scope2]: space-joined into thescope=query param. -
extra_authorize_params: { key: value }: extra query params on the authorize URL (e.g. xAI needsplan: generic). -
extra_token_params: { key: value }: extra form params on every token request (code exchange and refresh); the token-side mirror ofextra_authorize_params. -
resource: <uri>: RFC 8707 resource indicator. Sent asresource=on the authorize URL and on every token request (code exchange and refresh). Use this when your provider scopes tokens to a specific API/resource server. -
echo_pkce_in_token_exchange: true: some providers (like xAI) require re-sendingcode_challengeon token exchange. -
include_state_in_token_exchange: false: some providers rejectstateon the token endpoint (defaulttrue). -
token_request_format: form_url_encoded: some providers require form-encoded token bodies (default isform_url_encodedfor openai-compatible). -
client_secret: rare with PKCE, but supported for hybrid setups.
Example:
clients:
- type: openai-compatible
name: my-provider
api_base: https://api.provider.com/v1
auth: oauth
oauth:
client_id: '{{PROVIDER_CLIENT_ID}}'
authorize_url: https://auth.provider.com/oauth/authorize
token_url: https://auth.provider.com/oauth/token
redirect_port: 8765
scopes: [read, write]For providers whose OAuth app is registered with a public HTTPS URL (not loopback). The browser redirects to that URL, the provider's page displays the code (or you copy it from the URL bar), and you paste it back into Coyote.
Coyote detects Mode B automatically when redirect_uri is set to a non-loopback URL (anything other than 127.0.0.1,
localhost, or ::1). No opt-in flag needed.
Required fields: same as Mode A above, except the callback config:
-
redirect_uri: https://<provider-callback-host>/<path>: the exact URL the provider is registered with.
What Coyote accepts at the paste prompt:
-
Full callback URL from the browser bar (e.g.
https://provider.example.com/callback?code=abc&state=xyz). Coyote parsescodeandstatefrom the query string. -
<code>#<state>fragment (Anthropic-style; supported for provider pages that display both concatenated). - Bare code (just the authorization code, no state). Accepted with a warning. CSRF state validation is skipped. Use this for providers whose callback page shows only the code.
Example:
clients:
- type: openai-compatible
name: my-hosted-provider
api_base: https://api.provider.com/v1
auth: oauth
oauth:
client_id: '{{PROVIDER_CLIENT_ID}}'
authorize_url: https://auth.provider.com/oauth/authorize
token_url: https://auth.provider.com/oauth/token
redirect_uri: https://provider.com/oauth/callback # public URL -> paste mode
scopes: [read]For CLIs and headless environments: no browser callback required. Coyote displays a short user_code and a
verification_uri. You visit the URL on any device (phone, another laptop, or the same browser Coyote tries to open)
and enter the code to approve. Coyote polls the token endpoint until you approve or the code expires. Refresh tokens
are supported.
Required fields:
flow: device_code-
client_id: your OAuth app's client ID -
device_authorization_url: where Coyote requests the initialuser_code -
token_url: where Coyote polls for the token
Optional fields:
scopes: [scope1, scope2]-
use_pkce_in_device_flow: true: some providers (like MiniMax) require PKCE with device flow. Coyote generates a freshcode_verifier+code_challenge(S256) on each authentication. -
extra_token_params: { key: value }: extra form params on every token request (device polling and refresh). -
resource: <uri>: RFC 8707 resource indicator. Sent asresource=on the device-authorization request and on every token request (device polling and refresh). Use this when your provider scopes tokens to a specific API/resource server. -
token_request_format: form_url_encoded: usually needed for device flow (default for openai-compatible). -
extra_token_headers: { key: value }: custom headers applied to both device and token endpoints. -
client_secret: rare but supported.
Example:
clients:
- type: openai-compatible
name: my-device-flow-provider
api_base: https://api.example.com/v1
auth: oauth
oauth:
client_id: '{{MY_CLIENT_ID}}'
device_authorization_url: https://auth.example.com/oauth/device_authorization
token_url: https://auth.example.com/oauth/token
flow: device_code
resource: https://api.example.com/
scopes: [openid, profile]Sandbox bonus: when IS_SANDBOX=1 (set automatically by coyote --sandbox), Coyote renders a scannable QR code
in the terminal so you can point your phone camera at the verification URL and complete the flow in seconds.
For headless machine-to-machine authentication. No user, no browser. Coyote POSTs directly to the token endpoint with
client_id + client_secret. Best for CI, servers, agent runtimes.
Required fields:
flow: client_credentialsclient_idclient_secrettoken_url
Optional fields:
scopes: [scope1, scope2]token_request_format-
extra_token_params: { key: value }: extra form params on the token request (and each silent re-exchange). -
resource: <uri>: RFC 8707 resource indicator, sent asresource=on the token request. extra_token_headers: { key: value }
Example:
clients:
- type: openai-compatible
name: acme-gateway
api_base: https://gateway.acme.com/v1
auth: oauth
oauth:
client_id: '{{ACME_CLIENT_ID}}'
client_secret: '{{ACME_CLIENT_SECRET}}'
token_url: https://auth.acme.com/oauth/token
scopes: [openai.chat]
flow: client_credentialsNote on refresh: RFC 6749 forbids refresh tokens for this flow. When the access token expires, Coyote silently re-runs the client credentials exchange (still no browser or user interaction).
Coyote ships OAuth defaults for the following providers, so you only need auth: oauth in your config, with no fields
required:
- xAI: SuperGrok / X Premium+ subscribers. See xAI OAuth Note above for behavior details.
To bundle defaults for additional providers locally, edit your models.yaml (see Local Models Override)
and add an oauth: block under the provider entry using the field reference for the appropriate flow above.
Coyote resolves OAuth configuration in two layers:
-
Bundled defaults in
models.yamlunderprovider: <name>. -
Inline overrides in your
config.yamlunderclients[i].oauth.
Per-field: config.yaml wins over models.yaml. If neither source provides a required field for your flow (see the
"Required fields" list in each flow section above), Coyote bails with a clear error at authentication time telling you
which field is missing.
You can override individual fields. For example, if the bundled default has the wrong token URL for your corporate
gateway, just set oauth.token_url inline and the rest of the bundled defaults still apply.
Two special cases:
-
Map fields merge per-key rather than being replaced wholesale:
extra_authorize_params,extra_token_params, andextra_token_headersfromconfig.yamlare merged into the bundled defaults, with your keys winning on conflict. -
A
resourceentry insideextra_authorize_params/extra_token_paramswins over the first-classresourcefield on its respective request surface. This keeps configs that predate theresourcefield (which passed it via the extra-params maps) working without sending duplicate or conflictingresource=values.
OAuth tokens are stored on disk at ~/.cache/coyote/oauth/{client_name}_oauth_tokens.json, keyed by the client's name
field only (not by provider type). Two clients with the same name, even if they use different provider types, will
overwrite each other's tokens.
To prevent accidental collisions, Coyote rejects configuration files with duplicate name values across all
clients[] entries at load time. Fixing the underlying single-namespace layout is future work.
Stored tokens carry an issuer fingerprint (token_url + client_id). If you change a client's oauth:
configuration to point at a different issuer, its previously stored tokens are treated as absent and Coyote prompts
you to re-authenticate. A stale token is never replayed against the wrong endpoint, even across a name collision.
When Coyote runs inside a sandbox (coyote --sandbox), your host OAuth tokens (~/.cache/coyote/oauth/) are copied
into the container so agents can call OAuth-authenticated providers without re-authenticating. If you do need to
authenticate from inside a sandbox:
- Flow 2 (Device Grant) works out of the box. Coyote prints a QR code you can scan with your phone camera.
- Flow 3 (Client Credentials) works fine (no browser or callback needed).
- Flow 1 Mode A (loopback callback) generally cannot complete because the loopback listener binds inside the sandbox's network namespace, unreachable from the host browser.
- Flow 1 Mode B (browser-paste) works if you can open the authorize URL in a browser somewhere and paste back.
Recommendation: authenticate on the host first and let the sandbox token copy carry you through.
The claude, openai, and gemini clients support the same inline oauth: block as OpenAI-compatible providers.
Use it when your models are served by something other than the stock provider itself; e.g. a corporate gateway that
speaks the Anthropic Messages API (or the OpenAI / Gemini API shapes) but issues its own tokens:
clients:
- type: claude
name: gateway-claude
auth: oauth
api_base: https://gateway.example/anthropic/v1
oauth:
client_id: '{{GATEWAY_CLIENT_ID}}'
device_authorization_url: https://auth.gateway.example/oauth/device_authorization
token_url: https://auth.gateway.example/oauth/token
flow: device_code
scopes: [claude.chat]
resource: https://gateway.example/v1The same shape works with type: openai and type: gemini. Custom-OAuth requests use each provider's plain REST API
shape against your api_base (chat completions for openai, generateContent for gemini, Messages for claude).
Behavior notes:
-
Without an
oauth:block,auth: oauthuses the stock provider for that client type (Claude Pro/Max, ChatGPT Plus/Pro Codex, or Google account), exactly as before. Adding the block switches that client to generic OAuth against your endpoints; all flows from the field reference above are supported. -
api_baseis required alongside anoauth:block. Coyote refuses to silently fall back to the stock provider host. A custom-issuer token would be sent to the wrong host. - Stock-provider artifacts are never sent to custom endpoints:
-
claude: the Claude-Code identification markers (system-prompt prefix and the OAuthanthropic-betaheader) go only to the stock Claude Pro/Max provider. -
openai: the ChatGPT Codex backend routing (and its Responses-API request shape) and theChatGPT-Account-Idheader apply only to the stock provider. Custom-OAuth clients speak plain/chat/completions. -
gemini: requests are structurally identical in both modes; only the auth header differs (bearer token instead ofx-goog-api-key).
-
-
auth: oauthmust be set for the block to take effect; anoauth:block without it is a configuration error, and Coyote fails loudly rather than silently ignoring the block. - An
oauth:block supplied by the models catalog (e.g. a synced mirror) behaves the same underauth: oauth, but is inert for clients using api-key auth. - Tokens minted against custom endpoints are stamped with an issuer fingerprint, so re-pointing a client at a different issuer forces re-authentication instead of replaying the old token.
Built-in providers: (just set auth: oauth, no fields required):
-
Claude: See Claude OAuth Note; custom endpoints/gateways are also supported via an
inline
oauth:block (see Custom OAuth Endpoints on Built-in Clients) -
Gemini: See Gemini OAuth Note; custom endpoints/gateways are also supported via an inline
oauth:block (see Custom OAuth Endpoints on Built-in Clients) -
OpenAI: See OpenAI OAuth Note (requires ChatGPT Plus or Pro subscription); custom
endpoints/gateways are also supported via an inline
oauth:block (see Custom OAuth Endpoints on Built-in Clients) -
xAI: See xAI OAuth Note (requires SuperGrok or X Premium+ subscription; configured via
type: openai-compatible, uses bundled defaults)
Any OpenAI-compatible provider: Configured inline via an oauth: block. Coyote supports all four common flows:
Authorization Code + PKCE (loopback callback OR browser-paste), Device Authorization Grant (RFC 8628), and Client
Credentials. See OAuth for OpenAI-Compatible Providers for the field reference
for each flow.
Coyote also lets you customize some extra settings for interacting with APIs:
| Setting | Description | Default |
|---|---|---|
proxy |
Set a proxy to use | - |
connect_timeout |
Timeout in seconds for establishing a connection to the API | 10 |
read_timeout |
Stall detector: maximum seconds allowed between successive reads from the API before the request is aborted. Streaming-safe; the clock resets on every chunk received, so long streaming responses are fine as long as data keeps flowing. Set to 0 to disable entirely (e.g. for very slow local models or large non-streaming embedding calls). |
300 |
clients:
- type: openai
extra:
proxy: socks5://127.0.0.1:1080
connect_timeout: 10
read_timeout: 300Note
Before read_timeout existed, a connection that stalled after connecting (e.g. a streaming response that
silently stopped mid-stream) would hang Coyote indefinitely. The default now aborts any request that goes 300
seconds without receiving a single byte.