> ## 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.

# Invite User

> Send user invitation via email (admin only)

Send user invitations via email with customizable invitation links and user data. This endpoint allows administrators to invite new users to join the platform with pre-configured roles and metadata.

<Warning>
  This endpoint requires service role authentication. Only use the service role key on your backend servers, never in client-side code.
</Warning>

<RequestExample>
  ```bash cURL
  curl -X POST "http://localhost:8080/admin/invite" \
    -H "Authorization: Bearer service_role_key_here" \
    -H "Content-Type: application/json" \
    -d '{
      "email": "newuser@example.com",
      "data": {
        "first_name": "John",
        "last_name": "Doe",
        "role": "team_member"
      },
      "redirect_to": "https://yourapp.com/welcome"
    }'
  ```

  ```javascript JavaScript
  const response = await fetch('http://localhost:8080/admin/invite', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer service_role_key_here',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      email: 'newuser@example.com',
      data: {
        first_name: 'John',
        last_name: 'Doe',
        role: 'team_member'
      },
      redirect_to: 'https://yourapp.com/welcome'
    }),
  });

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

  ```python Python
  import requests

  headers = {
      'Authorization': 'Bearer service_role_key_here',
      'Content-Type': 'application/json'
  }

  data = {
      "email": "newuser@example.com",
      "data": {
          "first_name": "John",
          "last_name": "Doe",
          "role": "team_member"
      },
      "redirect_to": "https://yourapp.com/welcome"
  }

  response = requests.post(
      'http://localhost:8080/admin/invite',
      headers=headers,
      json=data
  )
  invite_result = response.json()
  ```

  ```go Go
  package main

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

  func main() {
      data := map[string]interface{}{
          "email": "newuser@example.com",
          "data": map[string]interface{}{
              "first_name": "John",
              "last_name":  "Doe",
              "role":       "team_member",
          },
          "redirect_to": "https://yourapp.com/welcome",
      }
      
      jsonData, _ := json.Marshal(data)
      
      req, _ := http.NewRequest("POST", 
          "http://localhost:8080/admin/invite", 
          bytes.NewBuffer(jsonData))
      req.Header.Set("Authorization", "Bearer service_role_key_here")
      req.Header.Set("Content-Type", "application/json")
      
      client := &http.Client{}
      resp, err := client.Do(req)
      // Handle response...
  }
  ```
</RequestExample>

## Request Body

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

<ParamField body="data" type="object">
  User metadata to pre-populate when the invitation is accepted.

  **Common fields:**

  * `first_name` - User's first name
  * `last_name` - User's last name
  * `role` - User's role in the system
  * `team_id` - Team or organization ID
  * `department` - User's department
</ParamField>

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

<ParamField body="options" type="object">
  Additional invitation options.

  **Available options:**

  * `captcha_token` - Captcha token for verification
  * `data` - Additional app metadata
</ParamField>

## Response

<ResponseField name="user" type="object">
  Created user object with invitation status
</ResponseField>

<ResponseField name="message_id" type="string">
  Unique identifier for the sent invitation email
</ResponseField>

<ResponseExample>
  ```json 200 - Invitation Sent Successfully
  {
    "user": {
      "id": "123e4567-e89b-12d3-a456-426614174000",
      "aud": "authenticated",
      "role": "authenticated",
      "email": "newuser@example.com",
      "phone": null,
      "email_confirmed_at": null,
      "phone_confirmed_at": null,
      "invited_at": "2023-01-01T00:00:00Z",
      "confirmation_sent_at": "2023-01-01T00:00:00Z",
      "app_metadata": {
        "provider": "email",
        "providers": ["email"]
      },
      "user_metadata": {
        "first_name": "John",
        "last_name": "Doe",
        "role": "team_member"
      },
      "created_at": "2023-01-01T00:00:00Z",
      "updated_at": "2023-01-01T00:00:00Z"
    },
    "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 401 - Unauthorized
  {
    "code": 401,
    "msg": "Invalid service role key",
    "details": "Please provide a valid service role key"
  }
  ```

  ```json 409 - User Already Exists
  {
    "code": 409,
    "msg": "User already exists",
    "details": "A user with this email address already exists"
  }
  ```

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

## Invitation Email Template

The invitation email includes:

```html
<!DOCTYPE html>
<html>
<head>
  <title>You're Invited to Join Strike</title>
</head>
<body>
  <div style="max-width: 600px; margin: 0 auto; padding: 20px;">
    <h1>You're Invited!</h1>
    
    <p>Hi {{.FirstName}},</p>
    
    <p>You've been invited to join Strike as a {{.Role}}.</p>
    
    <a href="{{.InvitationURL}}" 
       style="background-color: #0D9373; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; display: inline-block;">
      Accept Invitation
    </a>
    
    <p>Or copy and paste this link into your browser:</p>
    <p>{{.InvitationURL}}</p>
    
    <p>This invitation will expire in 7 days.</p>
    
    <p>Welcome to the team!</p>
    
    <p>Best regards,<br>The Strike Team</p>
  </div>
</body>
</html>
```

## Implementation Examples

### React Admin Invitation Form

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

function AdminInviteForm({ onInviteSent }) {
  const [formData, setFormData] = useState({
    email: '',
    firstName: '',
    lastName: '',
    role: 'user',
    teamId: '',
    department: ''
  });
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState('');
  const [success, setSuccess] = useState(false);

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

    try {
      const response = await fetch('/api/admin/invite', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${process.env.REACT_APP_SERVICE_ROLE_KEY}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          email: formData.email,
          data: {
            first_name: formData.firstName,
            last_name: formData.lastName,
            role: formData.role,
            team_id: formData.teamId,
            department: formData.department
          },
          redirect_to: 'https://yourapp.com/welcome'
        }),
      });

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

      const result = await response.json();
      setSuccess(true);
      onInviteSent?.(result);
      
      // Reset form
      setFormData({
        email: '',
        firstName: '',
        lastName: '',
        role: 'user',
        teamId: '',
        department: ''
      });
      
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

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

  return (
    <div className="admin-invite-form">
      <h2>Invite New User</h2>
      
      {error && (
        <div className="error-message">
          {error}
        </div>
      )}

      {success && (
        <div className="success-message">
          ✅ Invitation sent successfully!
        </div>
      )}

      <form onSubmit={handleSubmit} className="invite-form">
        <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="user@example.com"
            required
          />
        </div>

        <div className="form-row">
          <div className="form-group">
            <label htmlFor="role">Role</label>
            <select
              id="role"
              value={formData.role}
              onChange={(e) => handleInputChange('role', e.target.value)}
            >
              <option value="user">User</option>
              <option value="team_member">Team Member</option>
              <option value="team_lead">Team Lead</option>
              <option value="admin">Admin</option>
            </select>
          </div>
          
          <div className="form-group">
            <label htmlFor="department">Department</label>
            <input
              type="text"
              id="department"
              value={formData.department}
              onChange={(e) => handleInputChange('department', e.target.value)}
              placeholder="Engineering"
            />
          </div>
        </div>

        <div className="form-group">
          <label htmlFor="teamId">Team ID (optional)</label>
          <input
            type="text"
            id="teamId"
            value={formData.teamId}
            onChange={(e) => handleInputChange('teamId', e.target.value)}
            placeholder="team_123"
          />
        </div>

        <button type="submit" disabled={loading}>
          {loading ? 'Sending Invitation...' : 'Send Invitation'}
        </button>
      </form>
    </div>
  );
}

export default AdminInviteForm;
```

### Bulk User Invitation

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

function BulkInviteForm() {
  const [csvData, setCsvData] = useState('');
  const [loading, setLoading] = useState(false);
  const [results, setResults] = useState([]);

  const handleBulkInvite = async (e) => {
    e.preventDefault();
    setLoading(true);
    setResults([]);

    // Parse CSV data
    const lines = csvData.trim().split('\n');
    const headers = lines[0].split(',').map(h => h.trim());
    const invitations = [];

    for (let i = 1; i < lines.length; i++) {
      const values = lines[i].split(',').map(v => v.trim());
      const invitation = {};
      
      headers.forEach((header, index) => {
        invitation[header] = values[index];
      });
      
      invitations.push(invitation);
    }

    // Send invitations
    const inviteResults = [];
    
    for (const invitation of invitations) {
      try {
        const response = await fetch('/api/admin/invite', {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${process.env.REACT_APP_SERVICE_ROLE_KEY}`,
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            email: invitation.email,
            data: {
              first_name: invitation.first_name,
              last_name: invitation.last_name,
              role: invitation.role || 'user',
              department: invitation.department
            },
            redirect_to: 'https://yourapp.com/welcome'
          }),
        });

        if (response.ok) {
          const result = await response.json();
          inviteResults.push({
            email: invitation.email,
            success: true,
            userId: result.user.id
          });
        } else {
          const errorData = await response.json();
          inviteResults.push({
            email: invitation.email,
            success: false,
            error: errorData.msg
          });
        }
      } catch (err) {
        inviteResults.push({
          email: invitation.email,
          success: false,
          error: err.message
        });
      }
    }

    setResults(inviteResults);
    setLoading(false);
  };

  return (
    <div className="bulk-invite-form">
      <h2>Bulk User Invitation</h2>
      
      <div className="csv-instructions">
        <h3>CSV Format</h3>
        <p>Use the following format for your CSV data:</p>
        <pre>
email,first_name,last_name,role,department
john@example.com,John,Doe,team_member,Engineering
jane@example.com,Jane,Smith,admin,Marketing
        </pre>
      </div>

      <form onSubmit={handleBulkInvite} className="bulk-form">
        <div className="form-group">
          <label htmlFor="csvData">CSV Data</label>
          <textarea
            id="csvData"
            value={csvData}
            onChange={(e) => setCsvData(e.target.value)}
            placeholder="email,first_name,last_name,role,department&#10;john@example.com,John,Doe,team_member,Engineering"
            rows={10}
            required
          />
        </div>

        <button type="submit" disabled={loading}>
          {loading ? 'Sending Invitations...' : 'Send All Invitations'}
        </button>
      </form>

      {results.length > 0 && (
        <div className="results">
          <h3>Invitation Results</h3>
          <div className="results-summary">
            <span className="success-count">
              ✅ {results.filter(r => r.success).length} successful
            </span>
            <span className="error-count">
              ❌ {results.filter(r => !r.success).length} failed
            </span>
          </div>
          
          <div className="results-list">
            {results.map((result, index) => (
              <div key={index} className={`result-item ${result.success ? 'success' : 'error'}`}>
                <span className="email">{result.email}</span>
                {result.success ? (
                  <span className="status">✅ Invited (ID: {result.userId})</span>
                ) : (
                  <span className="error">❌ {result.error}</span>
                )}
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

export default BulkInviteForm;
```

### Node.js Backend Handler

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

const router = express.Router();

// Middleware to verify service role
const verifyServiceRole = (req, res, next) => {
  const authHeader = req.headers.authorization;
  const token = authHeader?.split(' ')[1];

  if (!token || token !== process.env.SERVICE_ROLE_KEY) {
    return res.status(401).json({
      code: 401,
      msg: 'Invalid service role key',
      details: 'Please provide a valid service role key'
    });
  }

  next();
};

router.post('/admin/invite', [
  verifyServiceRole,
  body('email').isEmail().normalizeEmail(),
  body('data').optional().isObject(),
  body('redirect_to').optional().isURL()
], 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, data, redirect_to, options } = req.body;

    // Call Strike Auth Service
    const response = await fetch(`${process.env.AUTH_SERVICE_URL}/admin/invite`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.SERVICE_ROLE_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        email,
        data,
        redirect_to: redirect_to || `${process.env.APP_URL}/welcome`,
        options
      }),
    });

    const result = await response.json();

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

    // Log invitation
    console.log(`User invited: ${email}, role: ${data?.role || 'user'}`);

    res.json(result);

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

module.exports = router;
```

## Use Cases

### Team Member Invitation

Invite new team members with specific roles:

```json
{
  "email": "developer@example.com",
  "data": {
    "first_name": "Alex",
    "last_name": "Johnson",
    "role": "developer",
    "team_id": "engineering_team",
    "department": "Engineering",
    "start_date": "2023-02-01"
  },
  "redirect_to": "https://yourapp.com/onboarding"
}
```

### Client Invitation

Invite external clients with limited access:

```json
{
  "email": "client@company.com",
  "data": {
    "first_name": "Sarah",
    "last_name": "Wilson",
    "role": "client",
    "company": "Wilson Corp",
    "access_level": "read_only"
  },
  "redirect_to": "https://yourapp.com/client-portal"
}
```

### Admin Invitation

Invite administrators with elevated privileges:

```json
{
  "email": "admin@example.com",
  "data": {
    "first_name": "Michael",
    "last_name": "Brown",
    "role": "admin",
    "permissions": ["user_management", "system_config"],
    "department": "IT"
  },
  "redirect_to": "https://yourapp.com/admin-setup"
}
```

## Security Features

* **Service Role Authentication**: Requires valid service role key
* **Email Validation**: Validates email format and uniqueness
* **Rate Limiting**: Prevents invitation spam
* **Invitation Expiration**: Invitations expire after 7 days
* **Audit Logging**: Logs all invitation activities

## Best Practices

<AccordionGroup>
  <Accordion title="Invitation Management">
    * Include meaningful user data in invitations
    * Use appropriate redirect URLs for different user types
    * Set reasonable expiration times for invitations
    * Track invitation status and follow up if needed
  </Accordion>

  <Accordion title="User Experience">
    * Provide clear invitation emails with next steps
    * Include contact information for support
    * Customize invitation content based on user role
    * Handle expired invitations gracefully
  </Accordion>

  <Accordion title="Security">
    * Validate all user data before processing
    * Implement proper access controls for invitation sending
    * Monitor for suspicious invitation patterns
    * Use secure redirect URLs (HTTPS only)
  </Accordion>
</AccordionGroup>

## Testing

### Unit Tests

```javascript
describe('POST /admin/invite', () => {
  test('should send invitation to new user', async () => {
    const inviteData = {
      email: 'newuser@example.com',
      data: {
        first_name: 'John',
        last_name: 'Doe',
        role: 'team_member'
      }
    };

    const response = await request(app)
      .post('/admin/invite')
      .set('Authorization', 'Bearer valid_service_role_key')
      .send(inviteData)
      .expect(200);

    expect(response.body.user.email).toBe('newuser@example.com');
    expect(response.body.user.user_metadata.first_name).toBe('John');
    expect(response.body.message_id).toBeDefined();
  });

  test('should reject invitation for existing user', async () => {
    await request(app)
      .post('/admin/invite')
      .set('Authorization', 'Bearer valid_service_role_key')
      .send({
        email: 'existing@example.com',
        data: { first_name: 'Test' }
      })
      .expect(409);
  });

  test('should reject invalid service role', async () => {
    await request(app)
      .post('/admin/invite')
      .set('Authorization', 'Bearer invalid_key')
      .send({
        email: 'test@example.com',
        data: { first_name: 'Test' }
      })
      .expect(401);
  });
});
```

## Related Endpoints

<CardGroup cols={2}>
  <Card title="Create User" icon="user-plus" href="/api-reference/admin/create-user">
    Create user accounts directly
  </Card>

  <Card title="Generate Link" icon="link" href="/api-reference/admin/generate-link">
    Generate custom invitation links
  </Card>

  <Card title="Update User" icon="user-edit" href="/api-reference/admin/update-user">
    Update user information
  </Card>

  <Card title="Verify Invitation" icon="check" href="/api-reference/authentication/verify-get">
    Accept user invitations
  </Card>
</CardGroup>
