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/leadsToken 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:
| Field | Requirements |
|---|---|
email | Valid email format, not already registered |
password | Minimum 8 characters, at least one uppercase, one lowercase, one number |
first_name | Required, 2-50 characters |
last_name | Required, 2-50 characters |
organization_name | Required, 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
Navigate to Settings
Log in and go to Settings → API 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/inboundKey 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:writeReceive Authorization Code
User authorizes and is redirected to your callback URL with a code:
https://yourapp.com/callback?code=auth_code_123Exchange 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
| Scope | Description |
|---|---|
leads:read | Read lead data |
leads:write | Create and update leads |
vendors:read | Read vendor configuration |
vendors:write | Manage vendors |
analytics:read | Access analytics data |
webhooks:read | Read webhook configuration |
webhooks:write | Manage webhooks |
organization:read | Read organization settings |
organization:write | Update 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: 1703505600When rate limited (429 status):
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests",
"retry_after": 60
}
}Error Codes
| Code | HTTP Status | Description |
|---|---|---|
INVALID_CREDENTIALS | 401 | Incorrect email or password |
TOKEN_EXPIRED | 401 | JWT token has expired |
TOKEN_INVALID | 401 | JWT token is malformed or invalid |
API_KEY_INVALID | 401 | API key not found or disabled |
API_KEY_REVOKED | 401 | API key has been revoked |
INSUFFICIENT_PERMISSIONS | 403 | API key lacks required permissions |
IP_RESTRICTED | 403 | Request from unauthorized IP address |
RATE_LIMIT_EXCEEDED | 429 | Too many requests |
EMAIL_ALREADY_REGISTERED | 409 | Email already exists |
WEAK_PASSWORD | 400 | Password doesn’t meet requirements |
INVALID_RESET_TOKEN | 400 | Password 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_... prefixExample: 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?
- Check our Getting Started guide for setup instructions
- Review API key management for vendor integration
- Contact support at [email protected] for authentication issues