> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trycandid.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Candid.Configuration, SDK Setup Options Reference

> Full reference for Candid.Configuration, the value type that controls your API key, reward, recording limits, step timings, and visual appearance.

`Candid.Configuration` is a `Sendable` struct you construct once and pass to `Candid.configure(_:)`. Every field has a sensible default so you can start with just an `apiKey` and add more settings incrementally. The sections below cover each top-level field and every nested type in detail.

```swift theme={null}
// Minimal setup
Candid.configure(
    Candid.Configuration(apiKey: "cpk_your_project_key")
)
```

***

## Initializer

```swift theme={null}
public init(
    apiKey: String? = nil,
    reward: Reward? = nil,
    recordingDuration: TimeInterval = 600,
    stepTimings: [StepType: StepTiming] = [:],
    appearance: Appearance = .init()
)
```

The participant identifier is not part of the configuration: set it at any time with [`Candid.setUserId(_:)`](/reference/candid), for example once the user logs in.

***

## Top-Level Fields

<ParamField path="apiKey" type="String?">
  Your project's API key, available from the Candid dashboard. Keys follow the format `cpk_…`. Required for `Candid.register(trigger:)` to resolve studies and for recordings to upload.
</ParamField>

<ParamField path="reward" default="nil" type="Reward?">
  An optional reward the participant receives after completing the session. When non-nil, the SDK shows a reward callout on the intro screen and fulfills the reward after the upload succeeds. See **Reward** below.
</ParamField>

<ParamField path="recordingDuration" default="600" type="TimeInterval">
  Maximum length of the screen and microphone recording, in seconds. The SDK stops recording automatically when this limit is reached. `600` seconds (10 minutes) is the default.
</ParamField>

<ParamField path="stepTimings" default="[:]" type="[StepType: StepTiming]">
  Controls how long the SDK waits before showing a skip affordance, per step type. Step types without an entry use their default timing. See the **Step timings** section below.
</ParamField>

<ParamField path="appearance" default="Appearance()" type="Appearance">
  Controls the primary accent color, typeface, and widget position used throughout the Candid overlay UI. See the **Appearance** section below.
</ParamField>

***

## Step timings

`stepTimings` is a dictionary from `StepType` to `StepTiming`. Provide entries only for the step types you want to tune; the others keep their defaults.

```swift theme={null}
public enum StepType: String, Sendable {
    case openQuestion
    case action
}
```

```swift theme={null}
Candid.Configuration(
    stepTimings: [
        .openQuestion: .init(canSkipAfter: 5, promptEvery: 20),
        .action: .init(canSkipAfter: 45, promptEvery: 0)
    ]
)
```

### `StepTiming`

An individual timing configuration for one step type.

```swift theme={null}
public struct StepTiming: Sendable, Equatable {
    public var canSkipAfter: TimeInterval
    public var promptEvery: TimeInterval

    public static let openQuestionDefault = Self(canSkipAfter: 1, promptEvery: 15)
    public static let actionDefault       = Self(canSkipAfter: 30, promptEvery: 0)
}
```

<ParamField path="canSkipAfter" type="TimeInterval">
  Number of seconds after a step appears before the participant can tap **Skip**. Set to `0` to allow skipping immediately. Also settable via the `skipAfter` alias.
</ParamField>

<ParamField path="promptEvery" type="TimeInterval">
  Number of seconds between repeated on-screen prompts encouraging the participant to respond. Set to `0` to disable repeat prompts.
</ParamField>

`StepTiming` provides two static defaults you can reference directly:

| Constant               | `canSkipAfter` | `promptEvery` |
| ---------------------- | -------------- | ------------- |
| `.openQuestionDefault` | `1`            | `15`          |
| `.actionDefault`       | `30`           | `0`           |

***

## `Appearance`

`Appearance` controls the visual style of every screen in the Candid overlay.

```swift theme={null}
public struct Appearance: Sendable {
    public var primaryColor: String    // default: "#35C884"
    public var font: FontChoice        // default: .system()
}
```

<ParamField path="primaryColor" default="&#x22;#35C884&#x22;" type="String">
  The primary accent color used for buttons, progress indicators, and interactive elements throughout the overlay. Provide a six-digit hex string with a leading `#`, for example `"#FF5733"`.
</ParamField>

<ParamField path="font" default=".system()" type="FontChoice">
  The typeface used for all text in the overlay. Use one of the `FontChoice` factory methods described below.

  <Expandable title="FontChoice options">
    `FontChoice` has three factory methods:

    **`.system(_ design: SystemFontDesign = .default)`**: Uses the iOS system font (SF Pro) in the specified design variant. Available designs:

    | Design        | Value           |
    | ------------- | --------------- |
    | `.default`    | Standard SF Pro |
    | `.rounded`    | SF Pro Rounded  |
    | `.serif`      | SF Pro Serif    |
    | `.monospaced` | SF Mono         |

    **`.custom(name: String)`**: Uses a custom font registered in your app bundle by PostScript name. Make sure the font is listed in your `Info.plist` under `UIAppFonts`.

    **`.custom(_ provider: any FontProvider)`**: Accepts any type that conforms to the `FontProvider` protocol, giving you full control over font resolution per size and weight. See **FontProvider** below.

    ```swift theme={null}
    // System rounded
    Appearance(font: .system(.rounded))

    // Custom bundle font
    Appearance(font: .custom(name: "Nunito-Regular"))
    ```
  </Expandable>
</ParamField>

### `FontProvider`

A protocol you can conform to when you need complete control over how fonts are resolved. Implement `font(size:weight:)` and pass your conforming type to `FontChoice.custom(_:)`.

```swift theme={null}
public protocol FontProvider: Sendable {
    func font(size: CGFloat, weight: Font.Weight) -> Font
}
```

<ResponseField name="font(size:weight:)" type="(CGFloat, Font.Weight) -> Font">
  Called by the SDK whenever it needs to render text. Return the `SwiftUI.Font` that corresponds to the requested `size` and `weight`.
</ResponseField>

```swift theme={null}
struct BrandFontProvider: Candid.FontProvider {
    func font(size: CGFloat, weight: Font.Weight) -> Font {
        // Map SDK weight requests to your brand typeface
        switch weight {
        case .bold, .semibold:
            return .custom("MyBrandFont-Bold", size: size)
        default:
            return .custom("MyBrandFont-Regular", size: size)
        }
    }
}

// Use it in your configuration
Appearance(font: .custom(BrandFontProvider()))
```

***

## `Reward`

`Reward` defines an optional reward that participants receive after finishing a session. When you set this, the Candid overlay shows a reward callout on the intro screen and fulfills the reward once the upload succeeds.

```swift theme={null}
public struct Reward: Sendable {
    public var calloutText: String
    public var calloutImageSystemName: String
    public var successCompletion: @MainActor @Sendable () async throws -> RewardSuccessMessage
}
```

<ParamField path="calloutText" type="String" required>
  The headline text shown on the reward callout, for example `"Claim your free month of Premium"`. Keep it short and action-oriented.
</ParamField>

<ParamField path="calloutImageSystemName" default="&#x22;gift.fill&#x22;" type="String">
  The SF Symbols name for the icon displayed alongside the callout text. Any valid SF Symbol name is accepted.
</ParamField>

<ParamField path="successCompletion" type="@MainActor @Sendable () async throws -> RewardSuccessMessage" required>
  An async closure the SDK calls once the participant completed every step and the recording uploaded. It must return a `RewardSuccessMessage` value telling the SDK how to proceed. Throw an error to surface a failure state to the participant.
</ParamField>

### `RewardSuccessMessage`

The return value from the `successCompletion` closure, telling the SDK what to show after a successful fulfillment.

```swift theme={null}
public enum RewardSuccessMessage: Sendable, Equatable {
    case `default`(message: String? = nil)
    case hostHandled
}
```

<ResponseField name=".default(message:)" type="RewardSuccessMessage">
  Instructs the SDK to display its built-in thank-you screen. When `message` is provided it is shown as the subtitle; when `nil` (or when you return `.default()`), Candid uses its default thank-you copy.
</ResponseField>

<ResponseField name=".hostHandled" type="RewardSuccessMessage">
  Tells the SDK that your app is taking over presentation. The overlay dismisses and no further SDK UI is shown. Use this when you want to present a custom sheet, push to a new screen, or handle confirmation entirely in your own code.
</ResponseField>

***

## Complete Example

```swift theme={null}
Candid.configure(
    Candid.Configuration(
        apiKey: "cpk_your_project_key",
        reward: Candid.Reward(
            calloutText: "Claim your free week of Pro",
            calloutImageSystemName: "star.fill",
            successCompletion: {
                // Call your backend to fulfill the reward
                try await MyAPI.redeemGift()
                return .default(message: "Your free week is on its way!")
            }
        ),
        recordingDuration: 300,  // 5-minute cap
        stepTimings: [
            .openQuestion: .init(canSkipAfter: 5, promptEvery: 20)
        ],
        appearance: Candid.Appearance(
            primaryColor: "#6C63FF",
            font: .system(.rounded)
        )
    )
)

Candid.setUserId(currentUser.id)
```
