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}'

Supported Clients

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.

Client Configuration

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 ...

Metadata

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.

Model Settings

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:
  • chat
  • embedding
  • reranker
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

Extending vs. replacing the catalog

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: true

The 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 name and type match 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_vision merge with OR, so supports_vision: false on 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 models list 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-compatible client whose name doesn't start with any catalog provider's name), there is nothing to extend and the config entries are used alone, silently.

Prompt Caching

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 session shows 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 include total_cost. Cost is only reported when every billed component has a known price. No estimates.
  • Setting prompt_cache: false produces request bodies byte-identical to Coyote versions that predate prompt caching.

Wire API (OpenAI Protocol)

The openai and openai-compatible clients can speak two different OpenAI wire protocols:

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 chat

Any other value is a configuration error and fails loudly at load time.

Defaults

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.

Responses-wire behavior notes

  • Coyote requests encrypted reasoning (include: ["reasoning.encrypted_content"], with store: false) and replays the returned reasoning items 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": false so Coyote's JSON-Schema tool declarations are accepted unmodified. When opting a third-party /v1/responses endpoint in via wire_api: responses, be aware that strict servers may reject the include/strict parameters as unknown. If that happens, strip the offending parameter with a responses-keyed client patch (e.g. Groq's /openai/v1/responses endpoint rejects include), 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.

Authentication

Coyote clients support two authentication methods: API keys and OAuth. Each client entry in your configuration must use one or the other.

API Key Authentication

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.

OAuth Authentication

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: oauth

Step 2: Authenticate

Run the --authenticate flag with the client name:

coyote --authenticate my-claude-oauth

Or if you have only one OAuth-configured client, you can omit the name:

coyote --authenticate

Alternatively, 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 name field to distinguish them.

OpenAI OAuth Note

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: oauth

Gemini OAuth Note

Coyote uses the following scopes for OAuth with Gemini:

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 Note

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: oauth

OAuth for OpenAI-Compatible Providers

For 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.

Which flow does my provider use?

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).

Flow 1: Authorization Code + PKCE

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:

Mode A: Loopback callback (Coyote listens on your machine)

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. 56121 for xAI). Coyote synthesizes http://127.0.0.1:<port>/callback.
  • redirect_uri: http://127.0.0.1:<port>/<path>: full custom loopback URL (overrides redirect_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 the scope= query param.
  • extra_authorize_params: { key: value }: extra query params on the authorize URL (e.g. xAI needs plan: generic).
  • extra_token_params: { key: value }: extra form params on every token request (code exchange and refresh); the token-side mirror of extra_authorize_params.
  • resource: <uri>: RFC 8707 resource indicator. Sent as resource= 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-sending code_challenge on token exchange.
  • include_state_in_token_exchange: false: some providers reject state on the token endpoint (default true).
  • token_request_format: form_url_encoded: some providers require form-encoded token bodies (default is form_url_encoded for 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]

Mode B: Browser-paste (you paste the callback back into the terminal)

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:

  1. Full callback URL from the browser bar (e.g. https://provider.example.com/callback?code=abc&state=xyz). Coyote parses code and state from the query string.
  2. <code>#<state> fragment (Anthropic-style; supported for provider pages that display both concatenated).
  3. 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]

Flow 2: Device Authorization Grant (RFC 8628)

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 initial user_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 fresh code_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 as resource= 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.

Flow 3: Client Credentials (M2M)

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_credentials
  • client_id
  • client_secret
  • token_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 as resource= 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_credentials

Note 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).

Providers with Bundled OAuth Defaults

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.

Configuration Precedence

Coyote resolves OAuth configuration in two layers:

  1. Bundled defaults in models.yaml under provider: <name>.
  2. Inline overrides in your config.yaml under clients[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, and extra_token_headers from config.yaml are merged into the bundled defaults, with your keys winning on conflict.
  • A resource entry inside extra_authorize_params / extra_token_params wins over the first-class resource field on its respective request surface. This keeps configs that predate the resource field (which passed it via the extra-params maps) working without sending duplicate or conflicting resource= values.

Known Limitation: Token Namespacing

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.

Sandbox and headless environments

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.

Custom OAuth Endpoints on Built-in Clients

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/v1

The 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: oauth uses 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_base is required alongside an oauth: 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 OAuth anthropic-beta header) go only to the stock Claude Pro/Max provider.
    • openai: the ChatGPT Codex backend routing (and its Responses-API request shape) and the ChatGPT-Account-Id header 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 of x-goog-api-key).
  • auth: oauth must be set for the block to take effect; an oauth: 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 under auth: 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.

Providers That Support OAuth

Built-in providers: (just set auth: oauth, no fields required):

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.

Extra Settings

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: 300

Note

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.

⚠️ **GitHub.com Fallback** ⚠️