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

# Email and SMS OTP authentication

> Set up and implement email and SMS OTP authentication using the Turnkey Swift SDK.

export const FeatureCard = ({title, description, icon, logo, href}) => {
  return <a href={href} className="not-prose font-normal group ring-0 ring-transparent cursor-pointer block rounded-lg border border-zinc-950/10 dark:border-white/10 bg-white dark:bg-transparent p-5 no-underline hover:border-primary/40 transition-colors">
      <div className="tk-card-row">
        <span className="tk-card-icon-wrap">
          {logo ? <img src={`/images/networks/${logo}.svg`} className="tk-card-network-logo" alt="" /> : <span className="tk-card-icon" style={{
    maskImage: `url(/images/icons/${icon}.svg)`,
    WebkitMaskImage: `url(/images/icons/${icon}.svg)`
  }} />}
        </span>
        <div>
          <div className="font-semibold text-sm text-zinc-950 dark:text-white group-hover:text-primary transition-colors">
            {title}
          </div>
          {description && <div className="text-sm text-zinc-500 dark:text-zinc-400 mt-1">
              {description}
            </div>}
        </div>
      </div>
    </a>;
};

## Overview

This guide shows how to implement email or SMS OTP authentication using the Turnkey Swift SDK and the Auth Proxy.\
You'll send an OTP to a user's contact, present a verification screen, and complete the flow with a single call that handles login or signup.

Before you begin:

* Ensure you've completed the setup in [Getting Started](/solutions/embedded-wallets/integration-guide/swift/getting-started) and enabled the Auth Proxy with Email/SMS OTP in the Turnkey Dashboard.
* Make sure your app is configured with `TurnkeyConfig` and `TurnkeyContext.configure(...)`.

## Send an OTP code

Create or update your login screen to collect the user's email. When the user submits the form, call `initOtp(contact:otpType:)` with the email and `.email`.
This sends a one-time code to the provided address.

On success, keep the returned `otpId` along with the email so you can navigate to your OTP screen. In the next section, you'll verify the code the user enters.

```swift LoginView.swift focus={2,5-6,11,25-27} theme={"system"}
import SwiftUI
import TurnkeySwift

struct LoginView: View {
    @EnvironmentObject var turnkey: TurnkeyContext
    @State private var email = ""
    @State private var errorMessage: String?

    var body: some View {
        VStack(spacing: 12) {
            TextField("you@example.com", text: $email)
                .keyboardType(.emailAddress)
                .textInputAutocapitalization(.never)
                .autocorrectionDisabled()
                .frame(height: 48)
                .overlay(RoundedRectangle(cornerRadius: 8).stroke(.gray.opacity(0.3)))

            Button("Continue with email") {
                Task {
                    guard email.contains("@") else {
                        errorMessage = "Please enter a valid email"
                        return
                    }
                    do {
                        let result = try await turnkey.initOtp(contact: email, otpType: .email)
                        // Navigate to your OTP screen with `email` and `result.otpId`
                        // Example: OtpScreen(email: email, otpId: result.otpId)
                    } catch {
                        errorMessage = "Failed to initialize OTP"
                    }
                }
            }
            .disabled(!email.contains("@"))
        }
        .padding()
        .alert("Error", isPresented: .constant(errorMessage != nil)) {
            Button("OK") { errorMessage = nil }
        } message: {
            Text(errorMessage ?? "")
        }
    }
}
```

## Verify the OTP code

On your OTP screen, call `completeOtp(...)` with the user-entered code.\
This will verify the code and automatically complete login or signup as needed.

```swift OtpScreen.swift focus={2,5,8-9,11,16,33-38} theme={"system"}
import SwiftUI
import TurnkeySwift

struct OtpScreen: View {
    @EnvironmentObject var turnkey: TurnkeyContext
    @Environment(\.dismiss) private var dismiss

    let email: String
    let otpId: String

    @State private var code = ""
    @State private var errorMessage: String?

    var body: some View {
        VStack(spacing: 12) {
            TextField("Enter 6-digit code", text: $code)
                .keyboardType(.numberPad)
                .textContentType(.oneTimeCode)
                .multilineTextAlignment(.center)
                .frame(height: 48)
                .overlay(RoundedRectangle(cornerRadius: 8).stroke(.gray.opacity(0.3)))
                .onChange(of: code) { newValue in
                    code = String(newValue.prefix(6))
                }

            Button("Verify") {
                Task {
                    guard code.count == 6 else {
                        errorMessage = "Please enter a 6-digit code"
                        return
                    }
                    do {
                        _ = try await turnkey.completeOtp(
                            otpId: otpId,
                            otpCode: code,
                            contact: email,
                            otpType: .email
                        )
                        // Navigate to your main app upon success
                        dismiss()
                    } catch {
                        errorMessage = "Invalid code. Please try again."
                    }
                }
            }
            .disabled(code.count != 6)
        }
        .padding()
        .alert("Error", isPresented: .constant(errorMessage != nil)) {
            Button("OK") { errorMessage = nil }
        } message: {
            Text(errorMessage ?? "")
        }
    }
}
```

### SMS OTP

For SMS, pass an E.164-formatted phone number and use `otpType: .sms`:

```swift theme={"system"}
let result = try await turnkey.initOtp(contact: "+15551234567", otpType: .sms)
// Navigate to your OTP screen with `contact` (phone) and `result.otpId`
```

Tip: You can use a library like [PhoneNumberKit](https://github.com/PhoneNumberKit/PhoneNumberKit) to normalize user input to E.164.

### Notes

* Default OTP length is 6; if you've customized the OTP length in the dashboard, validate accordingly (up to 9 digits).
* To resend a code, call `initOtp(contact:otpType:)` again with the same contact.

***

## Next steps

<FeatureCard title="Passkeys" icon="lock-01" href="/solutions/embedded-wallets/integration-guide/swift/authentication/passkey" description="Learn how to add passkey login and signup to your app." />
