Skip to main content
DailyCallOptions can be passed to several methods: the factory constructor (createCallObject, createFrame, etc.), load(), preAuth(), startCamera(), and join(). Each call merges the new options into the existing internal config, with later values overriding earlier ones. The key question is when a given setting goes live — because once it does, passing it again has no effect.

DailyCallOptions reference

Full list of all call properties and their types

When settings go live

1. On bundle load

Settings that control how the call bundle is fetched and evaluated go live the moment the bundle loads. The bundle loads on the first call to load(), preAuth(), startCamera(), or join() — whichever comes first. Examples: dailyConfig.bundlePathOverride, dailyConfig.avoidEval Any value set on these before the bundle loads will be overridden by a later call, but once the bundle has loaded, further changes to these settings are ignored.

2. On device initialization

Settings related to device and media pipeline startup go live when devices are first initialized — either in startCamera(), or in join() if startCamera() was not called first. Examples: startAudioOff, startVideoOff, inputSettings, dailyConfig.alwaysIncludeMicInPermissionPrompt, dailyConfig.alwaysIncludeCamInPermissionPrompt, dailyConfig.enableIndependentDevicePermissionPrompts Values passed across multiple methods are merged and overridden up until that initialization point. After devices have started, these settings no longer apply.

3. On join (and for the duration of the call)

Settings related to the call session go live at join() and remain in effect for the duration. Most of these also have dedicated methods for updating them dynamically after joining. Examples: userName, proxyUrl, receiveSettings, sendSettings, dailyConfig.noAutoDefaultDeviceChange, dailyConfig.micAudioMode The last value passed before or at join() is what takes effect. If you pass userName to the constructor and again to join(), the join() value wins.

4. url and token

These must be set in the config by the time preAuth() or join() is called — either passed directly to those methods or set in an earlier call. They have an additional constraint: if you call preAuth(), the url and token passed there must match what is used at join().
  • If you pass a token to preAuth(), you don’t need to pass it again to join() — but if you do, it must be the same token.
  • If you don’t pass a token to preAuth(), you cannot pass one to join().
  • Mismatching the url or token between preAuth() and join() will cause the join to fail.

Recommendation: pass config once

Because settings go live at different points, spreading config across multiple method calls can produce subtle, hard-to-trace behavior. The simplest approach is to pass everything once. For most apps, that means passing the full config to preAuth() or join():
await call.preAuth({
  url: 'https://your-domain.daily.co/room-name',
  userName: 'Alice',
  startAudioOff: true,
  token: meetingToken,
});
await call.join();
If you use startCamera() for a pre-join preview, pass only what’s needed to start the devices there and leave everything else for join():
// Device setup only
await call.startCamera({
  inputSettings: {
    audio: { processor: { type: 'noise-cancellation' } },
  },
});

// Everything else at join time
await call.join({
  url: 'https://your-domain.daily.co/room-name',
  userName: 'Alice',
  token: meetingToken,
});
The constructor is appropriate for settings that are truly global to the instance — things like subscribeToTracksAutomatically or dailyConfig.bundlePathOverride — but avoid splitting call-specific options like url, token, or userName across multiple methods.