API ReferenceAuthentication

Authentication

Ledly provides two authentication methods: Bearer tokens for user-facing APIs and API keys for vendor integrations.


Authentication Methods

Bearer Token (User API)

For user-facing applications and administrative operations, use JWT bearer tokens obtained through the login endpoint.

Base URL: https://api.ledly.io/api

Use Cases:

  • Dashboard and admin operations
  • User management
  • Organization settings
  • CRM configuration
  • Analytics and reporting

API Key (Vendor API)

For vendor integrations and automated lead submissions, use API keys that authenticate vendors submitting leads.

Base URL: https://api.ledly.io/api/v1

Use Cases:

  • Lead ingestion from external sources
  • Vendor portal access
  • Automated lead submissions
  • Third-party integrations

User Authentication (Bearer Token)

Login

Authenticate a user and receive a JWT token.

Endpoint: POST /api/auth/login

Request:

curl -X POST https://api.ledly.io/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "password": "your_secure_password"
  }'

Response (200 OK):

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "id": "user_123",
    "email": "[email protected]",
    "first_name": "John",
    "last_name": "Doe",
    "role": "admin",
    "organization_id": "org_456"
  },
  "expires_at": "2025-12-26T10:30:00Z"
}

Error Response (401 Unauthorized):

{
  "error": {
    "code": "INVALID_CREDENTIALS",
    "message": "Invalid email or password"
  }
}

Using Bearer Tokens

Include the token in the Authorization header for all subsequent requests:

curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  https://api.ledly.io/api/leads

Token Expiration:

Tokens expire after 24 hours. When a token expires, you’ll receive a 401 Unauthorized response with code TOKEN_EXPIRED. Authenticate again to get a new token.


Registration

Create a new user account.

Endpoint: POST /api/auth/register

Request:

{
  "email": "[email protected]",
  "password": "SecurePassword123!",
  "first_name": "Jane",
  "last_name": "Smith",
  "organization_name": "Acme University"
}

Response (201 Created):

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "id": "user_789",
    "email": "[email protected]",
    "first_name": "Jane",
    "last_name": "Smith",
    "role": "owner",
    "organization_id": "org_999"
  }
}

Validation Requirements:

FieldRequirements
emailValid email format, not already registered
passwordMinimum 8 characters, at least one uppercase, one lowercase, one number
first_nameRequired, 2-50 characters
last_nameRequired, 2-50 characters
organization_nameRequired, 2-100 characters

Password Reset

Request a password reset email.

Endpoint: POST /api/auth/forgot-password

Request:

{
  "email": "[email protected]"
}

Response (200 OK):

{
  "message": "Password reset email sent"
}

For security, this endpoint always returns success even if the email doesn’t exist in the system.


Reset Password with Token

Complete the password reset using the token from the email.

Endpoint: POST /api/auth/reset-password

Request:

{
  "token": "reset_token_from_email",
  "new_password": "NewSecurePassword123!"
}

Response (200 OK):

{
  "message": "Password reset successful"
}

Refresh Token

Extend your session by refreshing the token.

Endpoint: POST /api/auth/refresh

Request:

curl -X POST https://api.ledly.io/api/auth/refresh \
  -H "Authorization: Bearer your_current_token"

Response (200 OK):

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expires_at": "2025-12-27T10:30:00Z"
}

Logout

Invalidate the current token.

Endpoint: POST /api/auth/logout

Request:

curl -X POST https://api.ledly.io/api/auth/logout \
  -H "Authorization: Bearer your_token"

Response (204 No Content)


API Key Authentication (Vendor API)

Generating API Keys

Log in and go to SettingsAPI Keys

Create New Key

Click Generate New API Key

Configure Key

  • Enter a descriptive name (e.g., “Landing Page Integration”)
  • Select permissions (read, write, or both)
  • Set optional IP restrictions
  • Set optional rate limit overrides

Save Securely

Copy the API key immediately - it will only be shown once

🚫

Store API keys securely. They cannot be retrieved after creation - only regenerated.


Using API Keys

Include the API key in the Authorization header:

curl -H "Authorization: Bearer vk_live_abc123xyz..." \
  https://api.ledly.io/api/leads/inbound

Key Format:

vk_test_...    (Sandbox/test environment)
vk_live_...    (Production environment)

Managing API Keys

List API Keys

Endpoint: GET /api/api-keys

Response:

{
  "data": [
    {
      "id": "key_123",
      "name": "Landing Page Integration",
      "key_preview": "vk_live_abc...xyz",
      "created_at": "2024-12-01T10:00:00Z",
      "last_used_at": "2024-12-25T09:15:00Z",
      "permissions": ["write"],
      "ip_restrictions": ["203.0.113.0/24"],
      "enabled": true
    }
  ]
}

Revoke API Key

Endpoint: DELETE /api/api-keys/:id

Response (204 No Content)

Rotate API Key

Endpoint: POST /api/api-keys/:id/rotate

Response (200 OK):

{
  "new_key": "vk_live_new123...",
  "message": "API key rotated successfully. Old key will remain valid for 24 hours."
}
⚠️

After rotating, both old and new keys work for 24 hours to allow for graceful migration.


OAuth Integration

For third-party applications, Ledly supports OAuth 2.0 authentication.

Authorization Flow

Redirect to Authorization

Send users to:

https://app.ledly.io/oauth/authorize?
  client_id=your_client_id&
  redirect_uri=https://yourapp.com/callback&
  response_type=code&
  scope=leads:read leads:write

Receive Authorization Code

User authorizes and is redirected to your callback URL with a code:

https://yourapp.com/callback?code=auth_code_123

Exchange Code for Token

curl -X POST https://api.ledly.io/api/oauth/token \
  -d "grant_type=authorization_code" \
  -d "client_id=your_client_id" \
  -d "client_secret=your_client_secret" \
  -d "code=auth_code_123" \
  -d "redirect_uri=https://yourapp.com/callback"

Receive Access Token

{
  "access_token": "oauth_access_token",
  "refresh_token": "oauth_refresh_token",
  "token_type": "Bearer",
  "expires_in": 3600
}

Available Scopes

ScopeDescription
leads:readRead lead data
leads:writeCreate and update leads
vendors:readRead vendor configuration
vendors:writeManage vendors
analytics:readAccess analytics data
webhooks:readRead webhook configuration
webhooks:writeManage webhooks
organization:readRead organization settings
organization:writeUpdate organization settings

Security Best Practices

Token Storage

Never:

  • Store tokens in localStorage (vulnerable to XSS)
  • Include tokens in URLs
  • Commit tokens to version control
  • Log tokens in application logs

Do:

  • Store tokens in httpOnly cookies
  • Use environment variables for API keys
  • Rotate keys regularly (every 90 days)
  • Use different keys for development and production

IP Restrictions

Limit API key access to specific IP addresses:

{
  "name": "Production Server",
  "ip_restrictions": [
    "203.0.113.0/24",
    "198.51.100.42"
  ]
}

Rate Limiting

Monitor rate limit headers to avoid throttling:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1703505600

When rate limited (429 status):

{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Too many requests",
    "retry_after": 60
  }
}

Error Codes

CodeHTTP StatusDescription
INVALID_CREDENTIALS401Incorrect email or password
TOKEN_EXPIRED401JWT token has expired
TOKEN_INVALID401JWT token is malformed or invalid
API_KEY_INVALID401API key not found or disabled
API_KEY_REVOKED401API key has been revoked
INSUFFICIENT_PERMISSIONS403API key lacks required permissions
IP_RESTRICTED403Request from unauthorized IP address
RATE_LIMIT_EXCEEDED429Too many requests
EMAIL_ALREADY_REGISTERED409Email already exists
WEAK_PASSWORD400Password doesn’t meet requirements
INVALID_RESET_TOKEN400Password reset token invalid or expired

Testing Authentication

Sandbox Environment

Test authentication without affecting production:

Login: https://sandbox.api.ledly.io/api/auth/login
API Keys: Use vk_test_... prefix

Example: Complete Auth Flow

// 1. Login
const loginResponse = await fetch('https://api.ledly.io/api/auth/login', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    email: '[email protected]',
    password: 'password123'
  })
});
 
const { token } = await loginResponse.json();
 
// 2. Use token for authenticated requests
const leadsResponse = await fetch('https://api.ledly.io/api/leads', {
  headers: {
    'Authorization': `Bearer ${token}`
  }
});
 
const leads = await leadsResponse.json();
 
// 3. Refresh token before expiration
const refreshResponse = await fetch('https://api.ledly.io/api/auth/refresh', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`
  }
});
 
const { token: newToken } = await refreshResponse.json();

Need Help?