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

# Magic Link

> Send magic link for passwordless authentication

Send a magic link via email for passwordless authentication. Users can sign in by clicking the link in their email without entering a password.

<Note>
  This endpoint does not require authentication and can create new users if `create_user` is set to true.
</Note>

<RequestExample>
  ```bash cURL
  curl -X POST "http://localhost:8080/magiclink" \
    -H "Content-Type: application/json" \
    -d '{
      "email": "user@example.com",
      "create_user": true
    }'
  ```

  ```javascript JavaScript
  const response = await fetch('http://localhost:8080/magiclink', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      email: 'user@example.com',
      create_user: true
    }),
  });

  const result = await response.json();
  ```

  ```python Python
  import requests

  data = {
      "email": "user@example.com",
      "create_user": True
  }

  response = requests.post('http://localhost:8080/magiclink', json=data)
  result = response.json()
  ```

  ```go Go
  package main

  import (
      "bytes"
      "encoding/json"
      "net/http"
  )

  func main() {
      data := map[string]interface{}{
          "email":       "user@example.com",
          "create_user": true,
      }
      
      jsonData, _ := json.Marshal(data)
      
      resp, err := http.Post(
          "http://localhost:8080/magiclink",
          "application/json",
          bytes.NewBuffer(jsonData),
      )
      // Handle response...
  }
  ```
</RequestExample>

## Request Body

<ParamField body="email" type="string" required>
  Email address to send the magic link to.
</ParamField>

<ParamField body="create_user" type="boolean">
  Whether to create a new user if the email doesn't exist. Defaults to false.
</ParamField>

<ParamField body="redirect_to" type="string">
  URL to redirect to after successful authentication. If not provided, uses the default redirect URL.
</ParamField>

<ParamField body="data" type="object">
  Additional user metadata to store if creating a new user.
</ParamField>

<ParamField body="captcha_token" type="string">
  Captcha token for verification if captcha is enabled.
</ParamField>

## Response

<ResponseField name="message_id" type="string">
  Unique identifier for the sent magic link email (when available)
</ResponseField>

<ResponseExample>
  ```json 200 - Magic Link Sent
  {
    "message_id": "msg_1234567890abcdef"
  }
  ```
</ResponseExample>

## Error Responses

<ResponseExample>
  ```json 400 - Invalid Email
  {
    "code": 400,
    "msg": "Invalid email format",
    "details": "Please provide a valid email address"
  }
  ```

  ```json 404 - User Not Found
  {
    "code": 404,
    "msg": "User not found",
    "details": "No user found with this email address. Set create_user to true to create a new account."
  }
  ```

  ```json 429 - Rate Limited
  {
    "code": 429,
    "msg": "Too many requests",
    "details": "Please wait before requesting another magic link"
  }
  ```
</ResponseExample>

## Magic Link Flow

<Steps>
  <Step title="Request Magic Link">
    User enters their email address and requests a magic link
  </Step>

  <Step title="Email Sent">
    A magic link email is sent to the user's email address
  </Step>

  <Step title="User Clicks Link">
    User clicks the magic link in their email
  </Step>

  <Step title="Authentication">
    User is automatically authenticated and redirected to your application
  </Step>
</Steps>

## Magic Link Email Template

```html
<!DOCTYPE html>
<html>
<head>
  <title>Sign in to Strike</title>
</head>
<body>
  <div style="max-width: 600px; margin: 0 auto; padding: 20px;">
    <h1>Sign in to Strike</h1>
    
    <p>Hi there,</p>
    
    <p>Click the button below to sign in to your Strike account:</p>
    
    <a href="{{.MagicLinkURL}}" 
       style="background-color: #0D9373; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; display: inline-block;">
      Sign In
    </a>
    
    <p>Or copy and paste this link into your browser:</p>
    <p>{{.MagicLinkURL}}</p>
    
    <p>This link will expire in 1 hour for security reasons.</p>
    
    <p>If you didn't request this sign-in link, you can safely ignore this email.</p>
    
    <p>Best regards,<br>The Strike Team</p>
  </div>
</body>
</html>
```

## Implementation Examples

### React Magic Link Form

```jsx
import { useState } from 'react';

function MagicLinkForm() {
  const [email, setEmail] = useState('');
  const [loading, setLoading] = useState(false);
  const [sent, setSent] = useState(false);
  const [error, setError] = useState('');
  const [createUser, setCreateUser] = useState(true);

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

    try {
      const response = await fetch('/api/auth/magiclink', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          email,
          create_user: createUser,
          redirect_to: window.location.origin + '/dashboard'
        }),
      });

      if (!response.ok) {
        const errorData = await response.json();
        throw new Error(errorData.msg || 'Failed to send magic link');
      }

      setSent(true);
      
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  if (sent) {
    return (
      <div className="magic-link-success">
        <h2>Check your email!</h2>
        <p>
          We've sent a magic link to <strong>{email}</strong>
        </p>
        <p>
          Click the link in your email to sign in instantly.
        </p>
        <p>
          <small>
            Didn't receive the email? Check your spam folder or{' '}
            <button 
              onClick={() => setSent(false)}
              className="link-button"
            >
              try again
            </button>
          </small>
        </p>
      </div>
    );
  }

  return (
    <form onSubmit={handleSubmit} className="magic-link-form">
      <h2>Sign in with Magic Link</h2>
      <p>
        Enter your email address and we'll send you a link to sign in instantly.
      </p>
      
      {error && (
        <div className="error-message">
          {error}
        </div>
      )}

      <div className="form-group">
        <label htmlFor="email">Email Address</label>
        <input
          type="email"
          id="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          placeholder="Enter your email"
          required
        />
      </div>

      <div className="form-group">
        <label className="checkbox-label">
          <input
            type="checkbox"
            checked={createUser}
            onChange={(e) => setCreateUser(e.target.checked)}
          />
          Create account if it doesn't exist
        </label>
      </div>

      <button type="submit" disabled={loading || !email}>
        {loading ? 'Sending...' : 'Send Magic Link'}
      </button>

      <p>
        Prefer a password? <a href="/login">Sign in with password</a>
      </p>
    </form>
  );
}

export default MagicLinkForm;
```

### Magic Link with User Creation

```jsx
import { useState } from 'react';

function MagicLinkSignup() {
  const [formData, setFormData] = useState({
    email: '',
    firstName: '',
    lastName: ''
  });
  const [loading, setLoading] = useState(false);
  const [sent, setSent] = useState(false);
  const [error, setError] = useState('');

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

    try {
      const response = await fetch('/api/auth/magiclink', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          email: formData.email,
          create_user: true,
          data: {
            first_name: formData.firstName,
            last_name: formData.lastName
          },
          redirect_to: window.location.origin + '/welcome'
        }),
      });

      if (!response.ok) {
        const errorData = await response.json();
        throw new Error(errorData.msg || 'Failed to send magic link');
      }

      setSent(true);
      
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  const handleInputChange = (field, value) => {
    setFormData(prev => ({
      ...prev,
      [field]: value
    }));
  };

  if (sent) {
    return (
      <div className="signup-success">
        <h2>Welcome to Strike!</h2>
        <p>
          We've sent a magic link to <strong>{formData.email}</strong>
        </p>
        <p>
          Click the link to complete your account setup and sign in.
        </p>
      </div>
    );
  }

  return (
    <form onSubmit={handleSubmit} className="magic-signup-form">
      <h2>Join Strike</h2>
      <p>
        Create your account and sign in instantly with a magic link.
      </p>
      
      {error && (
        <div className="error-message">
          {error}
        </div>
      )}

      <div className="form-row">
        <div className="form-group">
          <label htmlFor="firstName">First Name</label>
          <input
            type="text"
            id="firstName"
            value={formData.firstName}
            onChange={(e) => handleInputChange('firstName', e.target.value)}
            required
          />
        </div>
        
        <div className="form-group">
          <label htmlFor="lastName">Last Name</label>
          <input
            type="text"
            id="lastName"
            value={formData.lastName}
            onChange={(e) => handleInputChange('lastName', e.target.value)}
            required
          />
        </div>
      </div>

      <div className="form-group">
        <label htmlFor="email">Email Address</label>
        <input
          type="email"
          id="email"
          value={formData.email}
          onChange={(e) => handleInputChange('email', e.target.value)}
          placeholder="Enter your email"
          required
        />
      </div>

      <button type="submit" disabled={loading}>
        {loading ? 'Creating Account...' : 'Create Account & Send Magic Link'}
      </button>

      <p>
        Already have an account? <a href="/login">Sign in</a>
      </p>
    </form>
  );
}

export default MagicLinkSignup;
```

### Node.js Backend Handler

```javascript
const express = require('express');
const { body, validationResult } = require('express-validator');

const router = express.Router();

router.post('/magiclink', [
  body('email').isEmail().normalizeEmail(),
  body('create_user').optional().isBoolean(),
  body('data').optional().isObject()
], async (req, res) => {
  try {
    // Check validation errors
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({
        code: 400,
        msg: 'Invalid request data',
        details: errors.array()
      });
    }

    const { email, create_user, redirect_to, data, captcha_token } = req.body;

    // Call Strike Auth Service
    const response = await fetch(`${process.env.AUTH_SERVICE_URL}/magiclink`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        email,
        create_user: create_user || false,
        redirect_to: redirect_to || `${process.env.APP_URL}/dashboard`,
        data,
        captcha_token
      }),
    });

    const result = await response.json();

    if (!response.ok) {
      return res.status(response.status).json(result);
    }

    // Log magic link request
    console.log(`Magic link sent to: ${email}, create_user: ${create_user}`);

    res.json(result);

  } catch (error) {
    console.error('Magic link error:', error);
    res.status(500).json({
      code: 500,
      msg: 'Internal server error',
      details: 'Please try again later'
    });
  }
});

module.exports = router;
```

## Use Cases

### Passwordless Login

Perfect for users who prefer not to remember passwords:

```json
{
  "email": "user@example.com",
  "create_user": false,
  "redirect_to": "https://yourapp.com/dashboard"
}
```

### Quick Signup

Streamlined user registration without password requirements:

```json
{
  "email": "newuser@example.com",
  "create_user": true,
  "data": {
    "first_name": "John",
    "last_name": "Doe",
    "marketing_consent": true
  },
  "redirect_to": "https://yourapp.com/welcome"
}
```

### Account Recovery

Alternative to password reset for users who prefer magic links:

```json
{
  "email": "user@example.com",
  "create_user": false,
  "redirect_to": "https://yourapp.com/account-recovered"
}
```

## Security Features

* **Token Expiration**: Magic links expire after 1 hour
* **Single Use**: Links can only be used once
* **Rate Limiting**: Prevents spam and abuse
* **Secure Tokens**: Cryptographically secure random tokens
* **Email Verification**: Inherent email verification through link click

## Rate Limiting

This endpoint is rate limited to prevent abuse:

| Limit Type | Limit       | Window     |
| ---------- | ----------- | ---------- |
| Per Email  | 3 requests  | 5 minutes  |
| Per IP     | 10 requests | 10 minutes |

## Best Practices

<AccordionGroup>
  <Accordion title="User Experience">
    * Provide clear instructions about checking email
    * Include troubleshooting tips for email delivery issues
    * Offer alternative sign-in methods
    * Show loading states during email sending
    * Implement resend functionality with cooldown
  </Accordion>

  <Accordion title="Security">
    * Use secure, random tokens with sufficient entropy
    * Implement proper token expiration (1 hour recommended)
    * Log magic link requests for security monitoring
    * Validate email addresses before sending
    * Implement rate limiting to prevent abuse
  </Accordion>

  <Accordion title="Email Delivery">
    * Use reputable email service providers
    * Implement proper email authentication (SPF, DKIM, DMARC)
    * Monitor delivery rates and bounce rates
    * Provide clear sender identification
    * Include plain text version of emails
  </Accordion>
</AccordionGroup>

## Testing

### Unit Tests

```javascript
describe('POST /magiclink', () => {
  test('should send magic link for existing user', async () => {
    const response = await request(app)
      .post('/magiclink')
      .send({
        email: 'existing@example.com',
        create_user: false
      })
      .expect(200);

    expect(response.body.message_id).toBeDefined();
  });

  test('should create user and send magic link', async () => {
    const response = await request(app)
      .post('/magiclink')
      .send({
        email: 'new@example.com',
        create_user: true,
        data: {
          first_name: 'Test',
          last_name: 'User'
        }
      })
      .expect(200);

    expect(response.body.message_id).toBeDefined();
  });

  test('should reject invalid email', async () => {
    await request(app)
      .post('/magiclink')
      .send({
        email: 'invalid-email',
        create_user: true
      })
      .expect(400);
  });

  test('should return 404 for non-existent user when create_user is false', async () => {
    await request(app)
      .post('/magiclink')
      .send({
        email: 'nonexistent@example.com',
        create_user: false
      })
      .expect(404);
  });
});
```

## Related Endpoints

<CardGroup cols={2}>
  <Card title="Verify Magic Link" icon="check" href="/api-reference/authentication/verify-get">
    Complete magic link authentication
  </Card>

  <Card title="Resend Magic Link" icon="paper-plane" href="/api-reference/authentication/resend">
    Resend magic link email
  </Card>

  <Card title="User Signup" icon="user-plus" href="/api-reference/authentication/signup">
    Traditional email/password registration
  </Card>

  <Card title="User Login" icon="sign-in" href="/api-reference/authentication/login">
    Password-based authentication
  </Card>
</CardGroup>
