Skip to main content
POST
/
v1beta
/
accounts
/
{account_id}
/
login-link
Generate Login Link
curl --request POST \
  --url https://api.fifteenth.com/v1beta/accounts/{account_id}/login-link \
  --header 'Authorization: <authorization>' \
  --header 'Content-Type: <content-type>' \
  --data '
{
  "user_email": "<string>",
  "expiry_hours": 123,
  "purpose": "<string>",
  "redirect_url": "<string>",
  "send_notification": true,
  "notification_settings": {
    "notification_settings.partner_branded": true,
    "notification_settings.custom_subject": "<string>",
    "notification_settings.custom_message": "<string>",
    "notification_settings.include_instructions": true
  }
}
'
{
  "login_link": "<string>",
  "user_email": "<string>",
  "expires_at": "<string>",
  "expires_in_hours": 123,
  "purpose": "<string>",
  "redirect_url": "<string>",
  "notification_sent": true,
  "previous_links_invalidated": 123,
  "created_at": "<string>",
  "account_id": 123,
  "404 Not Found": {},
  "400 Bad Request": {},
  "403 Forbidden": {},
  "429 Too Many Requests": {}
}

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
Each login link is single-use for initial access but allows the client to set up permanent login credentials once used.

Request

Path Parameters

account_id
number
required
The unique Fifteenth account identifier to generate a login link for.Format: Numeric ID
Example: 12345

Headers

Authorization
string
required
Bearer token with your Partner API key
Content-Type
string
required
Must be application/json

Body Parameters

user_email
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
expiry_hours
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
purpose
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
redirect_url
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
send_notification
boolean
Whether to send an email notification to the user with the new login link.Default: true
notification_settings
object
Email notification customization options.

Response

Success Response

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
user_email
string
Email address of the user this login link is for.
expires_at
string
ISO 8601 timestamp when the login link expires.
expires_in_hours
integer
Number of hours until the login link expires.
purpose
string
The purpose specified for generating this login link.
redirect_url
string
URL the client will be redirected to after login (if specified).
notification_sent
boolean
Whether an email notification was sent to the user.
Number of previous login links that were invalidated when generating this new one.
created_at
string
ISO 8601 timestamp when the login link was generated.
account_id
number
The account ID this login link provides access to.

Examples

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']}")
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']}")

Generate for Specific User

# 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']}")
# 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']
)

Response Examples

Response
{
  "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"
}
Response (Custom Settings)
{
  "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

404 Not Found
object
Account not found or user email not associated with account.
{
  "error": {
    "code": "ACCOUNT_NOT_FOUND",
    "message": "Account not found",
    "details": {
      "account_id": "12345"
    }
  }
}
{
  "error": {
    "code": "USER_NOT_FOUND",
    "message": "User email not found on this account",
    "details": {
      "user_email": "nonexistent@example.com",
      "account_id": "12345"
    }
  }
}
400 Bad Request
object
Invalid request parameters.
{
  "error": {
    "code": "INVALID_EXPIRY",
    "message": "Expiry hours must be between 1 and 168",
    "details": {
      "provided_hours": 200,
      "min_hours": 1,
      "max_hours": 168
    }
  }
}
{
  "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"]
    }
  }
}
403 Forbidden
object
Account access denied or suspended.
{
  "error": {
    "code": "ACCOUNT_SUSPENDED",
    "message": "Cannot generate login links for suspended accounts",
    "details": {
      "account_id": "12345",
      "status": "suspended",
      "contact_support": true
    }
  }
}
429 Too Many Requests
object
Too many login link generation requests.
{
  "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"
    }
  }
}

Security Considerations

Login links provide full account access. Handle them with the same security as passwords:
  • 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

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
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
For sensitive situations:
  • Set send_notification: false
  • Deliver links through secure channels
  • Use shorter expiry times
  • Track delivery method for your records
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

Monitoring and Alerts

Monitor login link usage for security:
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

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

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

Retrieve Account

Check account status and user information

Add Users

Add additional users who need login links

Account Activity

Monitor login link usage and account activity

Authentication

Learn about API security and authentication