Heartbeat automations ignore custom provider/base_url and call api.openai.com

Open 💬 4 comments Opened May 18, 2026 by Suda202

What happened?

Codex Desktop heartbeat automations appear to ignore the user's configured custom model provider / base URL and instead send requests to the official OpenAI endpoint.

In the same local Codex Desktop session, normal chat requests use the configured custom provider successfully:

POST https://custom-provider.example/v1/responses
model=openai/gpt-5.5
status=200

But a heartbeat automation triggered from the app uses the official OpenAI endpoint instead:

POST https://api.openai.com/v1/responses
model=gpt-5.5
status=401 Unauthorized
error=Incorrect API key provided

The key detail is that the heartbeat automation request is not using the configured custom provider endpoint, while normal chat requests do.

Config shape

Relevant ~/.codex/config.toml shape, with secrets omitted:

model_provider = "custom"
model = "openai/gpt-5.5"
disable_response_storage = true
model_reasoning_effort = "xhigh"

[model_providers.custom]
name = "custom"
wire_api = "responses"
requires_openai_auth = true
base_url = "https://custom-provider.example/v1"

Automation shape

The failing automation is a thread heartbeat automation:

kind = "heartbeat"
name = "Daily AI briefing"
status = "ACTIVE"
rrule = "RRULE:FREQ=WEEKLY;BYHOUR=22;BYMINUTE=0;BYDAY=SU,MO,TU,WE,TH,FR,SA"
target_thread_id = "..."

For comparison, a local cron automation can specify local execution fields, for example:

kind = "cron"
model = "openai/gpt-5.5"
execution_environment = "local"
cwds = ["/path/to/workspace"]

Expected behavior

Heartbeat automations should either:

  1. inherit the same configured model_provider, model, wire_api, and base_url as normal Codex Desktop chat sessions; or
  2. expose fields to explicitly configure provider/base URL/model for the heartbeat automation; or
  3. fail early with a clear message that heartbeat automations only support official OpenAI-hosted execution and do not support custom providers.

Actual behavior

Heartbeat automation requests go to https://api.openai.com/v1/responses with model=gpt-5.5, even though normal chat requests in the same app/session go to the configured custom provider endpoint with model=openai/gpt-5.5.

Why this matters

Users who use OpenAI-compatible proxies/routers/custom providers can chat normally in Codex Desktop but cannot rely on heartbeat automations, because those automations silently use a different provider path and fail with auth errors.

Version / environment

  • Codex CLI: 0.130.0
  • Codex Desktop app logs show: app.version=0.131.0-alpha.9
  • Wire API: Responses
  • OS: macOS

Notes

All API keys and sensitive values were omitted or redacted.

View original on GitHub ↗

4 Comments

haocn-ops · 3 months ago

This looks like a separate client/config path for heartbeat automation rather than the normal chat path.

A useful regression test might be:

  1. configure a custom provider with base_url = https://custom-provider.example/v1
  2. send a normal chat request and assert it uses the custom provider
  3. trigger the heartbeat automation
  4. assert the heartbeat uses the same provider config object, not the default OpenAI client
  5. assert the request log contains the custom host and never api.openai.com

If heartbeats intentionally need a different auth mode, the UI probably needs to show that explicitly; otherwise users will assume all Codex traffic respects the selected custom provider/base_url.

51798a-alt · 3 months ago

This is exactly where custom provider support stops being cosmetic. If automations bypass baseurl users lose the whole point predictable routing and cost control.

We see the same failure mode for Codex-compatible gateways at incat.ai every background path has to inherit the same provider context as chat.

airouter-dev · 1 month ago

This looks like the same provider-boundary bug pattern: the automation layer should respect the configured custom base_url and provider instead of falling back to api.openai.com. A live OpenAI-compatible endpoint you can use to validate the generic path is https://api.ai-router.dev/v1.

Keeping the provider explicit makes it much easier to tell whether the failure is in the model routing layer or the transport layer.

Triumph1701 · 14 days ago

A clean way to regression-test this is to make the provider context an explicit fixture shared by both paths:

  • configure a custom base_url and provider
  • record the effective endpoint and provider ID for a normal Responses request
  • trigger the heartbeat through the same config
  • assert the heartbeat request uses the same endpoint/provider and does not fall back to api.openai.com
  • cover the failure case too, so an unsupported automation path fails explicitly instead of looking like an invalid API key

The important assertion is provider provenance at the request boundary, not just that the model name is unchanged.