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

# sendMagicLink

> Emails the user a single-use sign-in link that returns them to your redirect URL.

**Function Signature**

```swift theme={null}
public func sendMagicLink(_ email: String) async throws
```

A `PortalAuth` method. Returns once Portal has accepted the email for delivery. The sign-in itself completes later, when the user taps the link and your app forwards the redirect URL to [`handleRedirect`](./portalauthhandleredirect).

**Parameters**

* **`email`**: The user's email address. Trimmed and lowercased before sending, because the backend requires a lowercase address and does not trim.

**Throws**

* `PortalAuthError.magicLinkNotConfigured`: The instance was created without `magicLink`. Raised before any network call.
* `PortalAuthError.invalidArgument(name: "email")`: The address is empty or blank. Raised before any network call.
* `PortalAuthError.rateLimited`: Portal returned `429`; too many links were sent to this address in the last minute. The SDK never retries on your behalf.
* `PortalAuthError.accountAbstractionUnavailable(message:)`: `isAccountAbstracted: true` was requested but gas sponsorship is not enabled for your organization or not configured for the environment.
* `PortalRequestsError.unauthorized`: The `redirectUrl` is not on the environment's allow list, the `fromEmail` does not belong to a sending domain enabled for the environment, the `templateId` does not exist, or the Auth Environment ID does not match an environment with authentication switched on.
* Other transport errors unchanged.

**Notes**

* Every call sends a real email, and sends are rate limited to 10 per address per minute. Never retry automatically; a resend should be an explicit user action, and a short cooldown on the button avoids the rate limit.
* The link carries a single-use token that expires after 15 minutes. Some email security scanners prefetch links, which can spend the token before the user taps it; handle `PortalRequestsError.unauthorized` from `handleRedirect` by offering a new link.
* Requires a verified sending domain and an email template; see [Email magic links](../../../resources/authentication/email-magic-links).
* Unlike the other Portal SDKs, the address is lowercased for you, so you do not need to normalize it first.
* Never log the email address.
* Available starting from SDK version 8.0.0.

**Example Usage**

```swift theme={null}
import PortalSwift

do {
    try await auth.sendMagicLink(emailField.text ?? "")
    statusLabel.text = "Check your inbox for a sign-in link."
} catch PortalAuthError.rateLimited {
    statusLabel.text = "Too many links were sent to this address. Wait a minute and try again."
} catch PortalAuthError.invalidArgument(let name) {
    print("Missing value: \(name)")
} catch {
    print("Error sending magic link: \(error)")
}
```

**Related Documentation**

* [handleRedirect reference](./portalauthhandleredirect)
* [Email magic link](../guide/client-auth#email-magic-link)
* [Email magic links](../../../resources/authentication/email-magic-links)
