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

# Magic Links

> Passwordless authentication via email

Magic links provide passwordless authentication by sending a secure login link to the user's email.

## Send Magic Link

```javascript theme={null}
await gately.sendMagicLink(email, options)
```

### Parameters

| Parameter            | Type   | Required | Description                 |
| -------------------- | ------ | -------- | --------------------------- |
| `email`              | string | Yes      | User's email address        |
| `options.redirectTo` | string | No       | URL to redirect after login |

### Example

```javascript theme={null}
import { GatelyClient } from '@gately/sdk'

const gately = new GatelyClient('YOUR_PROJECT_ID')

async function handleMagicLink(email) {
  try {
    await gately.sendMagicLink(email, {
      redirectTo: '/dashboard'
    })
    
    // Show success message
    showMessage('Check your email for a login link!')
  } catch (error) {
    console.error('Failed to send magic link:', error.message)
  }
}
```

## React Component

```jsx theme={null}
import { useState } from 'react'
import { useGately } from '@gately/sdk'

function MagicLinkForm() {
  const { sendMagicLink } = useGately()
  const [email, setEmail] = useState('')
  const [sent, setSent] = useState(false)
  const [error, setError] = useState('')

  const handleSubmit = async (e) => {
    e.preventDefault()
    setError('')

    try {
      await sendMagicLink(email, { redirectTo: '/dashboard' })
      setSent(true)
    } catch (err) {
      setError(err.message)
    }
  }

  if (sent) {
    return (
      <div className="success">
        <h2>Check your email</h2>
        <p>We sent a login link to {email}</p>
        <button onClick={() => setSent(false)}>
          Use a different email
        </button>
      </div>
    )
  }

  return (
    <form onSubmit={handleSubmit}>
      {error && <p className="error">{error}</p>}
      
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Enter your email"
        required
      />
      
      <button type="submit">
        Send Magic Link
      </button>
    </form>
  )
}
```

## How It Works

1. User enters their email
2. SDK sends request to Gately API
3. Gately sends email with secure link
4. User clicks link in email
5. Link redirects to your app with auth token
6. SDK automatically logs user in

```mermaid theme={null}
sequenceDiagram
    participant User
    participant Your App
    participant Gately
    participant Email
    
    User->>Your App: Enter email
    Your App->>Gately: sendMagicLink(email)
    Gately->>Email: Send login link
    Email->>User: Receive email
    User->>Email: Click link
    Email->>Gately: Verify token
    Gately->>Your App: Redirect with session
    Your App->>User: Logged in!
```

## Email Template

The magic link email includes:

* Your project name/branding
* Login button with secure link
* Link expiry notice (default: 1 hour)
* Security notice

Customize the email template in **Settings > Email > Templates**.

## Link Expiry

Magic links expire after 1 hour by default. If a user clicks an expired link:

* They see an error message
* They're prompted to request a new link

## Security

Magic links are:

* Single-use (invalidated after first use)
* Time-limited (expire after 1 hour)
* Cryptographically signed
* Tied to specific email address

## New vs Existing Users

| Scenario      | Behavior                    |
| ------------- | --------------------------- |
| Existing user | Logs in to existing account |
| New user      | Creates account and logs in |

```javascript theme={null}
// Works for both new and existing users
await gately.sendMagicLink('user@example.com')
```

## Error Handling

```javascript theme={null}
try {
  await gately.sendMagicLink(email)
} catch (error) {
  switch (error.code) {
    case 'INVALID_EMAIL':
      showError('Please enter a valid email address')
      break
    case 'RATE_LIMITED':
      showError('Too many requests. Please wait a moment.')
      break
    default:
      showError('Failed to send login link. Please try again.')
  }
}
```

## Rate Limiting

Magic link requests are rate limited to prevent abuse:

* 3 requests per email per 10 minutes
* 10 requests per IP per 10 minutes

## Combining with Password Login

Offer both options for flexibility:

```jsx theme={null}
function LoginOptions() {
  const [mode, setMode] = useState('password') // or 'magic'

  return (
    <div>
      <div className="tabs">
        <button 
          className={mode === 'password' ? 'active' : ''}
          onClick={() => setMode('password')}
        >
          Password
        </button>
        <button 
          className={mode === 'magic' ? 'active' : ''}
          onClick={() => setMode('magic')}
        >
          Magic Link
        </button>
      </div>

      {mode === 'password' ? (
        <PasswordLoginForm />
      ) : (
        <MagicLinkForm />
      )}
    </div>
  )
}
```
