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

# Generate Login Link

> Generate a new secure login link for client access to their Fifteenth account

## Overview

The Generate Login Link endpoint creates a new secure, time-limited login link for clients to access their Fifteenth account. This is useful when:

* The original login link has expired (7-day expiry)
* A client has lost their login link
* You need to provide additional login links to multiple users
* Security requires generating a fresh login link

<Note>
  Each login link is single-use for initial access but allows the client to set up permanent login credentials once used.
</Note>

## Request

### Path Parameters

<ParamField path="account_id" type="number" required>
  The unique Fifteenth account identifier to generate a login link for.

  **Format**: Numeric ID\
  **Example**: `12345`
</ParamField>

### Headers

<ParamField header="Authorization" type="string" required>
  Bearer token with your Partner API key
</ParamField>

<ParamField header="Content-Type" type="string" required>
  Must be `application/json`
</ParamField>

### Body Parameters

<ParamField body="user_email" type="string">
  Email address of the specific user to generate a login link for. If not provided, generates a link for the primary account holder.

  **Validation**: Must be an existing user on the account\
  **Example**: `john.doe@example.com`
</ParamField>

<ParamField body="expiry_hours" type="integer">
  Number of hours until the login link expires.

  **Range**: 1-168 hours (1 hour to 7 days)\
  **Default**: 168 hours (7 days)\
  **Example**: `48`
</ParamField>

<ParamField body="purpose" type="string">
  Purpose for generating the login link (for audit logging).

  **Options**:

  * `initial_access` - First time account access
  * `expired_link` - Replacing an expired link
  * `lost_credentials` - Client lost their login information
  * `additional_user` - Link for additional account user
  * `support_request` - Generated for customer support

  **Default**: `expired_link`
</ParamField>

<ParamField body="redirect_url" type="string">
  URL to redirect the client to after successful login. Must be a permitted domain for your partner account.

  **Format**: Valid HTTPS URL\
  **Example**: `https://yourplatform.com/tax-dashboard`
</ParamField>

<ParamField body="send_notification" type="boolean">
  Whether to send an email notification to the user with the new login link.

  **Default**: `true`
</ParamField>

<ParamField body="notification_settings" type="object">
  Email notification customization options.

  <Expandable title="notification_settings object">
    <ParamField body="notification_settings.partner_branded" type="boolean">
      Use partner branding in the notification email.
      **Default**: `true`
    </ParamField>

    <ParamField body="notification_settings.custom_subject" type="string">
      Custom email subject line.
      **Max**: 100 characters
      **Default**: "Your Fifteenth Account Access Link"
    </ParamField>

    <ParamField body="notification_settings.custom_message" type="string">
      Custom message to include in the email.
      **Max**: 500 characters
    </ParamField>

    <ParamField body="notification_settings.include_instructions" type="boolean">
      Include instructions for first-time users.
      **Default**: `true`
    </ParamField>
  </Expandable>
</ParamField>

## Response

### Success Response

<ResponseField name="login_link" type="string">
  The generated secure login link for the client.

  **Format**: `https://app.fifteenth.com/auth/partner-login/{token}`\
  **Security**: Token is cryptographically secure and single-use for initial access
</ResponseField>

<ResponseField name="user_email" type="string">
  Email address of the user this login link is for.
</ResponseField>

<ResponseField name="expires_at" type="string">
  ISO 8601 timestamp when the login link expires.
</ResponseField>

<ResponseField name="expires_in_hours" type="integer">
  Number of hours until the login link expires.
</ResponseField>

<ResponseField name="purpose" type="string">
  The purpose specified for generating this login link.
</ResponseField>

<ResponseField name="redirect_url" type="string">
  URL the client will be redirected to after login (if specified).
</ResponseField>

<ResponseField name="notification_sent" type="boolean">
  Whether an email notification was sent to the user.
</ResponseField>

<ResponseField name="previous_links_invalidated" type="integer">
  Number of previous login links that were invalidated when generating this new one.
</ResponseField>

<ResponseField name="created_at" type="string">
  ISO 8601 timestamp when the login link was generated.
</ResponseField>

<ResponseField name="account_id" type="number">
  The account ID this login link provides access to.
</ResponseField>

## Examples

### Basic Login Link Generation

<CodeGroup>
  ```python Python theme={null}
  import requests

  account_id = 12345
  url = f"https://api.fifteenth.com/v1beta/accounts/{account_id}/login-link"

  headers = {
      "Authorization": "Bearer 15th_your_api_key_here",
      "Content-Type": "application/json"
  }

  # Generate login link with default settings
  response = requests.post(url, headers=headers, json={})
  link_data = response.json()

  print(f"Login Link: {link_data['login_link']}")
  print(f"Expires: {link_data['expires_at']}")
  print(f"Notification Sent: {link_data['notification_sent']}")
  ```

  ```javascript JavaScript theme={null}
  const accountId = 12345;
  const url = `https://api.fifteenth.com/v1beta/accounts/${accountId}/login-link`;

  const headers = {
      'Authorization': 'Bearer 15th_your_api_key_here',
      'Content-Type': 'application/json'
  };

  // Generate login link with default settings
  const response = await fetch(url, {
      method: 'POST',
      headers: headers,
      body: JSON.stringify({})
  });

  const linkData = await response.json();

  console.log(`Login Link: ${linkData.login_link}`);
  console.log(`Expires: ${linkData.expires_at}`);
  console.log(`Notification Sent: ${linkData.notification_sent}`);
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api.fifteenth.com/v1beta/accounts/12345/login-link" \
    -H "Authorization: Bearer 15th_your_api_key_here" \
    -H "Content-Type: application/json" \
    -d '{}'
  ```
</CodeGroup>

### Custom Login Link with Settings

<CodeGroup>
  ```python Python theme={null}
  data = {
      "user_email": "john.doe@example.com",
      "expiry_hours": 48,  # 2 days
      "purpose": "support_request",
      "redirect_url": "https://yourplatform.com/tax-dashboard",
      "send_notification": True,
      "notification_settings": {
          "partner_branded": True,
          "custom_subject": "New Access Link for Your Tax Account",
          "custom_message": "We've generated a new login link at your request. This link expires in 48 hours for security.",
          "include_instructions": True
      }
  }

  response = requests.post(url, headers=headers, json=data)
  link_data = response.json()

  print(f"Custom Login Link Generated:")
  print(f"- Link: {link_data['login_link']}")
  print(f"- Expires in: {link_data['expires_in_hours']} hours")
  print(f"- Redirect: {link_data['redirect_url']}")
  print(f"- Purpose: {link_data['purpose']}")
  ```

  ```javascript JavaScript theme={null}
  const data = {
      user_email: 'john.doe@example.com',
      expiry_hours: 48,  // 2 days
      purpose: 'support_request',
      redirect_url: 'https://yourplatform.com/tax-dashboard',
      send_notification: true,
      notification_settings: {
          partner_branded: true,
          custom_subject: 'New Access Link for Your Tax Account',
          custom_message: "We've generated a new login link at your request. This link expires in 48 hours for security.",
          include_instructions: true
      }
  };

  const response = await fetch(url, {
      method: 'POST',
      headers: headers,
      body: JSON.stringify(data)
  });

  const linkData = await response.json();

  console.log('Custom Login Link Generated:');
  console.log(`- Link: ${linkData.login_link}`);
  console.log(`- Expires in: ${linkData.expires_in_hours} hours`);
  console.log(`- Redirect: ${linkData.redirect_url}`);
  console.log(`- Purpose: ${linkData.purpose}`);
  ```
</CodeGroup>

### Generate for Specific User

<CodeGroup>
  ```python Python theme={null}
  # Generate login link for a specific user (like an accountant)
  data = {
      "user_email": "accountant@clientcompany.com",
      "expiry_hours": 24,  # 1 day for security
      "purpose": "additional_user",
      "send_notification": True,
      "notification_settings": {
          "custom_message": "Your client has granted you access to their Fifteenth account for the 2024 tax preparation."
      }
  }

  response = requests.post(url, headers=headers, json=data)
  link_data = response.json()

  print(f"Generated link for: {link_data['user_email']}")
  print(f"Link expires: {link_data['expires_at']}")
  ```

  ```javascript JavaScript theme={null}
  // Generate login link for a specific user (like an accountant)
  const data = {
      user_email: 'accountant@clientcompany.com',
      expiry_hours: 24,  // 1 day for security
      purpose: 'additional_user',
      send_notification: true,
      notification_settings: {
          custom_message: 'Your client has granted you access to their Fifteenth account for the 2024 tax preparation.'
      }
  };

  const response = await fetch(url, {
      method: 'POST',
      headers: headers,
      body: JSON.stringify(data)
  });

  const linkData = await response.json();

  console.log(`Generated link for: ${linkData.user_email}`);
  console.log(`Link expires: ${linkData.expires_at}`);
  ```
</CodeGroup>

### Silent Link Generation (No Email)

<CodeGroup>
  ```python Python theme={null}
  # Generate link without sending email (for manual delivery)
  data = {
      "expiry_hours": 72,  # 3 days
      "purpose": "manual_delivery",
      "send_notification": False
  }

  response = requests.post(url, headers=headers, json=data)
  link_data = response.json()

  print(f"Login Link (for manual delivery): {link_data['login_link']}")
  print(f"No notification sent: {not link_data['notification_sent']}")

  # You would now deliver this link through your secure channel
  secure_delivery_system.send_link(
      recipient=client_email,
      link=link_data['login_link'],
      expires_at=link_data['expires_at']
  )
  ```

  ```javascript JavaScript theme={null}
  // Generate link without sending email (for manual delivery)
  const data = {
      expiry_hours: 72,  // 3 days
      purpose: 'manual_delivery',
      send_notification: false
  };

  const response = await fetch(url, {
      method: 'POST',
      headers: headers,
      body: JSON.stringify(data)
  });

  const linkData = await response.json();

  console.log(`Login Link (for manual delivery): ${linkData.login_link}`);
  console.log(`No notification sent: ${!linkData.notification_sent}`);

  // You would now deliver this link through your secure channel
  secureDeliverySystem.sendLink({
      recipient: clientEmail,
      link: linkData.login_link,
      expiresAt: linkData.expires_at
  });
  ```
</CodeGroup>

## Response Examples

### Basic Link Generation Response

```json Response theme={null}
{
  "login_link": "https://app.fifteenth.com/auth/partner-login/tk_A7xM9pQ2vK8xLqR4S1E6B3F",
  "user_email": "john.doe@example.com",
  "expires_at": "2024-01-22T10:30:00Z",
  "expires_in_hours": 168,
  "purpose": "expired_link",
  "redirect_url": null,
  "notification_sent": true,
  "previous_links_invalidated": 1,
  "created_at": "2024-01-15T10:30:00Z",
  "account_id": "12345"
}
```

### Custom Link Generation Response

```json Response (Custom Settings) theme={null}
{
  "login_link": "https://app.fifteenth.com/auth/partner-login/tk_M4pQ7vN9xR2mK5wL8S1E6B",
  "user_email": "john.doe@example.com",
  "expires_at": "2024-01-17T10:30:00Z",
  "expires_in_hours": 48,
  "purpose": "support_request",
  "redirect_url": "https://yourplatform.com/tax-dashboard",
  "notification_sent": true,
  "previous_links_invalidated": 2,
  "created_at": "2024-01-15T10:30:00Z",
  "account_id": 12345
}
```

## Error Responses

<ResponseField name="404 Not Found" type="object">
  Account not found or user email not associated with account.

  ```json theme={null}
  {
    "error": {
      "code": "ACCOUNT_NOT_FOUND",
      "message": "Account not found",
      "details": {
        "account_id": "12345"
      }
    }
  }
  ```

  ```json theme={null}
  {
    "error": {
      "code": "USER_NOT_FOUND",
      "message": "User email not found on this account",
      "details": {
        "user_email": "nonexistent@example.com",
        "account_id": "12345"
      }
    }
  }
  ```
</ResponseField>

<ResponseField name="400 Bad Request" type="object">
  Invalid request parameters.

  ```json theme={null}
  {
    "error": {
      "code": "INVALID_EXPIRY",
      "message": "Expiry hours must be between 1 and 168",
      "details": {
        "provided_hours": 200,
        "min_hours": 1,
        "max_hours": 168
      }
    }
  }
  ```

  ```json theme={null}
  {
    "error": {
      "code": "INVALID_REDIRECT_URL",
      "message": "Redirect URL is not permitted for your partner account",
      "details": {
        "provided_url": "https://untrusted.com/dashboard",
        "permitted_domains": ["yourplatform.com", "yourapp.net"]
      }
    }
  }
  ```
</ResponseField>

<ResponseField name="403 Forbidden" type="object">
  Account access denied or suspended.

  ```json theme={null}
  {
    "error": {
      "code": "ACCOUNT_SUSPENDED",
      "message": "Cannot generate login links for suspended accounts",
      "details": {
        "account_id": "12345",
        "status": "suspended",
        "contact_support": true
      }
    }
  }
  ```
</ResponseField>

<ResponseField name="429 Too Many Requests" type="object">
  Too many login link generation requests.

  ```json theme={null}
  {
    "error": {
      "code": "RATE_LIMIT_EXCEEDED",
      "message": "Too many login link generation requests",
      "details": {
        "limit": 10,
        "window_minutes": 60,
        "reset_at": "2024-01-15T11:30:00Z"
      }
    }
  }
  ```
</ResponseField>

## Security Considerations

### Link Security Features

<Warning>
  Login links provide full account access. Handle them with the same security as passwords:
</Warning>

* **Single-use for initial access**: After first use, clients set up permanent credentials
* **Time-limited**: Links automatically expire (default 7 days, max 7 days)
* **Cryptographically secure**: Uses secure random token generation
* **Invalidation**: Previous links are automatically invalidated when new ones are generated
* **Audit logging**: All link generation and usage is logged for security

### Best Practices

<AccordionGroup>
  <Accordion title="Expiry Time Selection">
    Choose appropriate expiry times based on urgency:

    * **Immediate support**: 1-4 hours
    * **Normal client access**: 24-48 hours
    * **Account setup**: 7 days (default)
    * **High security situations**: 1 hour minimum
  </Accordion>

  <Accordion title="Notification Security">
    When sending notifications:

    * Always use partner branding for legitimacy
    * Include expiry information in emails
    * Warn clients not to share links
    * Use custom messages to provide context
  </Accordion>

  <Accordion title="Manual Delivery">
    For sensitive situations:

    * Set `send_notification: false`
    * Deliver links through secure channels
    * Use shorter expiry times
    * Track delivery method for your records
  </Accordion>

  <Accordion title="Rate Limiting">
    Respect rate limits to prevent abuse:

    * Max 10 links per account per hour
    * Use purpose field for audit trails
    * Monitor link generation patterns
    * Implement client-side rate limiting
  </Accordion>
</AccordionGroup>

### Monitoring and Alerts

Monitor login link usage for security:

```python theme={null}
def monitor_login_link_security(account_id, link_data):
    """Monitor login link generation for security patterns"""
    
    # Alert on rapid link generation
    if link_data['previous_links_invalidated'] > 3:
        security_alert(
            f"Multiple login links generated rapidly for account {account_id}",
            severity="medium"
        )
    
    # Alert on short expiry links (potential support issue)
    if link_data['expires_in_hours'] < 4:
        operational_alert(
            f"Short-expiry login link generated: {link_data['expires_in_hours']} hours",
            context=link_data['purpose']
        )
    
    # Log for audit trail
    audit_log({
        'event': 'login_link_generated',
        'account_id': account_id,
        'purpose': link_data['purpose'],
        'expiry': link_data['expires_at'],
        'notification_sent': link_data['notification_sent']
    })
```

## Usage Patterns

### Automated Link Replacement

```python theme={null}
def replace_expired_links():
    """Automatically replace expired links for active accounts"""
    
    expired_accounts = get_accounts_with_expired_links()
    
    for account in expired_accounts:
        try:
            # Generate new link
            new_link = generate_login_link(account['id'], {
                'purpose': 'automated_renewal',
                'expiry_hours': 168,  # 7 days
                'send_notification': True,
                'notification_settings': {
                    'custom_message': 'Your previous login link has expired. Here is your new access link.'
                }
            })
            
            print(f"Renewed link for account {account['id']}")
            
        except Exception as e:
            print(f"Failed to renew link for {account['id']}: {e}")
            
        # Respect rate limits
        time.sleep(1)
```

### Support Ticket Integration

```python theme={null}
def generate_support_link(ticket_id, account_id, agent_name):
    """Generate login link for support ticket resolution"""
    
    link_data = generate_login_link(account_id, {
        'purpose': 'support_request',
        'expiry_hours': 4,  # Short expiry for security
        'send_notification': True,
        'notification_settings': {
            'custom_subject': f'Support Access Link - Ticket #{ticket_id}',
            'custom_message': f'Our support team has generated a secure access link to help resolve your inquiry. This link expires in 4 hours.',
            'partner_branded': True
        },
    })
    
    # Update support ticket with link info
    update_ticket(ticket_id, {
        'login_link_generated': True,
        'link_expires_at': link_data['expires_at'],
        'agent': agent_name
    })
    
    return link_data
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Retrieve Account" icon="user" href="/endpoints/accounts/retrieve">
    Check account status and user information
  </Card>

  <Card title="Add Users" icon="users" href="/endpoints/accounts/add-users">
    Add additional users who need login links
  </Card>

  <Card title="Account Activity" icon="clock-rotate-left" href="/endpoints/accounts/activity">
    Monitor login link usage and account activity
  </Card>

  <Card title="Authentication" icon="key" href="/authentication">
    Learn about API security and authentication
  </Card>
</CardGroup>
