Partner API Integration
The Partner API allows external systems to integrate with Levy Fleets, enabling programmatic access to vehicles, rides, customers, zones, and pricing data. Use webhooks to receive real-time notifications of fleet events.
Overview
The Partner API provides RESTful endpoints for managing your fleet programmatically. All requests are authenticated using API keys and rate-limited based on your configuration.
Key Features
- RESTful API - Standard HTTP methods and JSON responses
- API Key Authentication - Secure access with scoped permissions
- Rate Limiting - Configurable limits per minute, hour, and day
- Webhooks - Real-time event notifications with retry logic
- Request Logging - Full audit trail of API usage
Base URL
https://app.levyelectric.com/api/partner/v1
Getting Started
Creating an API Key
Navigate to Integrations
Go to Dashboard → Settings → Integrations.
Click Create API Key
Click New API Key button.
Configure Key Settings
- Enter a descriptive Name (e.g., "Production Integration")
- Select Permissions for this key
- Set Rate Limits if needed
- Optionally set an Expiration Date
Save and Copy Key
Click Create and immediately copy the secret key.
Save Your Secret Key
The API key secret is only shown once at creation. Store it securely - if lost, you'll need to regenerate the key.
API Key Format
API keys use the format: lf_pk_<32 random characters>
Example: lf_pk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
Only the first 12 characters (prefix) are stored for display. The full key is hashed using SHA-256 for secure storage.
Authentication
Request Headers
All API requests must include the API key header:
curl -X GET "https://app.levyelectric.com/api/partner/v1/vehicles" \
-H "X-Partner-Api-Key: lf_pk_your_api_key_here" \
-H "Content-Type: application/json"
Optional Headers
| Header | Description |
|---|---|
X-Subaccount-Id | Target a specific subaccount (for global keys) |
Content-Type | Always application/json |
Authentication Errors
| Status | Code | Description |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid API key |
| 403 | FORBIDDEN | Key lacks required permission |
Permissions
API keys are granted specific permissions that control what operations they can perform.
Available Permissions
| Permission | Description |
|---|---|
vehicles:read | List and view vehicles |
vehicles:write | Update vehicle status |
rides:read | List and view rides |
rides:write | Start, pause, resume, end rides |
customers:read | List and view customers |
customers:write | Block/unblock customers |
wallet:read | View customer wallet balance |
wallet:write | Credit/debit customer wallets |
zones:read | List and view zones |
pricing:read | View pricing configuration |
webhooks:read | List and view webhooks |
webhooks:write | Create, update, delete webhooks |
Default Permissions
New API keys receive read-only access by default:
vehicles:readrides:readcustomers:readzones:readpricing:read
Key Scopes
| Scope | Description |
|---|---|
subaccount | Access limited to one subaccount |
global | Access to multiple subaccounts (specify in allowed_subaccount_ids) |
Rate Limiting
API requests are rate-limited to prevent abuse and ensure fair usage.
Default Limits
| Window | Default Limit |
|---|---|
| Per Minute | 60 requests |
| Per Hour | 1,000 requests |
| Per Day | 10,000 requests |
Response Headers
Rate limit status is included in response headers:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 2025-01-15T10:31:00Z
Rate Limit Exceeded
When limits are exceeded, you'll receive a 429 response:
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded for minute window. Retry after 2025-01-15T10:31:00Z",
"details": {
"limit": 60,
"window": "minute",
"retry_after": 45
}
},
"meta": {
"request_id": "req_abc123",
"timestamp": "2025-01-15T10:30:15Z"
}
}
API Endpoints
Vehicles
List Vehicles
GET /api/partner/v1/vehicles
GET /api/partner/v1/vehicles?status=available&limit=50
Get Vehicle
GET /api/partner/v1/vehicles/{id}
Update Vehicle Status
PATCH /api/partner/v1/vehicles/{id}
{
"status": "maintenance"
}
Nearby Vehicles
GET /api/partner/v1/vehicles/nearby?lat=40.7128&lng=-74.0060&radius=500
Rides
List Rides
GET /api/partner/v1/rides
GET /api/partner/v1/rides?status=active
Get Ride
GET /api/partner/v1/rides/{id}
Start Ride (requires rides:write)
POST /api/partner/v1/rides
{
"customer_id": "uuid",
"vehicle_id": "uuid"
}
Pause Ride
POST /api/partner/v1/rides/{id}/pause
Resume Ride
POST /api/partner/v1/rides/{id}/resume
End Ride
POST /api/partner/v1/rides/{id}/end
{
"end_latitude": 40.7128,
"end_longitude": -74.0060
}
Customers
List Customers
GET /api/partner/v1/customers
GET /api/partner/v1/customers?email=user@example.com
Get Customer
GET /api/partner/v1/customers/{id}
Get Wallet Balance
GET /api/partner/v1/customers/{id}/wallet
Credit Wallet (requires wallet:write)
POST /api/partner/v1/customers/{id}/wallet
{
"action": "credit",
"amount_cents": 1000,
"description": "Promotional credit"
}
Zones
List Zones
GET /api/partner/v1/zones
GET /api/partner/v1/zones?type=parking
Pricing
Get Pricing
GET /api/partner/v1/pricing
Response Format
Success Response
{
"data": {
"id": "uuid",
"vehicle_number": "V001",
"status": "available"
},
"meta": {
"request_id": "req_abc123",
"timestamp": "2025-01-15T10:30:00Z"
}
}
List Response
{
"data": [
{ "id": "uuid-1", "vehicle_number": "V001" },
{ "id": "uuid-2", "vehicle_number": "V002" }
],
"pagination": {
"page": 1,
"limit": 50,
"total": 125,
"has_next": true,
"has_prev": false
},
"meta": {
"request_id": "req_abc123",
"timestamp": "2025-01-15T10:30:00Z"
}
}
Error Response
{
"error": {
"code": "NOT_FOUND",
"message": "Vehicle not found",
"details": {
"vehicle_id": "uuid"
}
},
"meta": {
"request_id": "req_abc123",
"timestamp": "2025-01-15T10:30:00Z"
}
}
Webhooks
Webhooks send real-time HTTP POST requests to your server when events occur.
Setting Up Webhooks
Navigate to Webhooks
Go to Dashboard → Settings → Integrations → Webhooks.
Create Webhook
Click New Webhook.
Configure Endpoint
- Enter your Webhook URL (HTTPS required)
- Select Events to subscribe to
- Add custom headers if needed
Save and Test
Save the webhook, then use Test to verify delivery.
Webhook Events
| Event | Trigger |
|---|---|
ride.started | Customer unlocks a vehicle |
ride.ended | Ride completes |
ride.paused | Ride is paused |
ride.resumed | Paused ride resumes |
customer.created | New customer registers |
customer.updated | Customer profile updated |
customer.blocked | Customer is blocked |
customer.unblocked | Customer is unblocked |
vehicle.status_changed | Vehicle status changes |
vehicle.battery_low | Battery drops below threshold |
vehicle.location_updated | Vehicle moves significantly |
Webhook Payload
{
"event": "ride.ended",
"event_id": "evt_abc123def456",
"timestamp": "2025-01-15T10:30:00Z",
"data": {
"ride_id": "uuid",
"customer_id": "uuid",
"customer_number": 12345,
"vehicle_id": "uuid",
"vehicle_number": "V001",
"start_time": "2025-01-15T10:00:00Z",
"end_time": "2025-01-15T10:30:00Z",
"duration_minutes": 30,
"distance_km": 5.2,
"pricing": {
"total_cents": 850,
"unlock_cents": 100,
"time_cents": 750,
"distance_cents": 0,
"discount_cents": 0
},
"payment_status": "paid",
"subaccount_id": "uuid"
}
}
Webhook Signature
Webhooks are signed using HMAC-SHA256 for security verification.
Signature Header:
X-Levy-Signature: t=1705312200,v1=abc123...
Verification (Node.js example):
const crypto = require('crypto')
function verifyWebhook(payload, signature, secret) {
const parts = signature.split(',')
const timestamp = parts.find(p => p.startsWith('t=')).substring(2)
const expectedSig = parts.find(p => p.startsWith('v1=')).substring(3)
// Check timestamp is recent (within 5 minutes)
const age = Math.floor(Date.now() / 1000) - parseInt(timestamp)
if (age > 300) return false
// Compute signature
const signedPayload = `${timestamp}.${JSON.stringify(payload)}`
const computedSig = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex')
return computedSig === expectedSig
}
Retry Logic
Failed webhook deliveries are automatically retried:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 1 second |
| 3 | 2 seconds |
| 4 | 4 seconds |
| 5 | 8 seconds |
After 5 failed attempts, the delivery is marked as failed.
API Key Management
Regenerating Keys
If a key is compromised:
- Go to Settings → Integrations
- Find the API key
- Click Regenerate
- Update your systems with the new key
- The old key is immediately invalidated
Disabling Keys
Temporarily disable a key without deleting it:
- Find the API key
- Toggle Active to off
- All requests with this key will return 401
Viewing Usage
Monitor API key activity:
- Last Used - When the key was last used
- Requests Today - Count of today's requests
- Requests This Week/Month - Historical usage
Best Practices
Security
- Store keys securely - Use environment variables, not code
- Use HTTPS - All API calls must use HTTPS
- Verify webhooks - Always validate webhook signatures
- Rotate keys - Regenerate keys periodically
- Minimum permissions - Only grant necessary permissions
Performance
- Use pagination - Request smaller pages for large lists
- Cache responses - Cache read-only data appropriately
- Handle rate limits - Implement exponential backoff
- Use webhooks - Prefer webhooks over polling
Error Handling
- Check response codes - Handle 4xx and 5xx errors
- Use request IDs - Log request_id for debugging
- Retry transient errors - Retry 5xx errors with backoff
- Monitor failures - Alert on repeated webhook failures
Troubleshooting
Authentication Failures
| Error | Solution |
|---|---|
| Missing header | Add X-Partner-Api-Key header |
| Invalid key | Verify key is correct and active |
| Expired key | Regenerate or remove expiration |
| Wrong permissions | Update key permissions |
Webhook Issues
| Issue | Solution |
|---|---|
| Not receiving events | Check webhook is active and URL is accessible |
| Signature verification fails | Verify you're using the correct secret |
| Timeout errors | Respond to webhooks within 30 seconds |
| Missing events | Check you've subscribed to the event type |
Rate Limit Issues
| Issue | Solution |
|---|---|
| Frequently hitting limits | Implement caching and reduce request frequency |
| Need higher limits | Contact support to discuss limit increases |
| Burst usage | Spread requests over time |
SDK Examples
Node.js
const axios = require('axios')
const api = axios.create({
baseURL: 'https://app.levyelectric.com/api/partner/v1',
headers: {
'X-Partner-Api-Key': process.env.LEVY_API_KEY,
'Content-Type': 'application/json'
}
})
// List available vehicles
const { data } = await api.get('/vehicles', {
params: { status: 'available' }
})
console.log(data.data) // Array of vehicles
Python
import requests
import os
API_KEY = os.environ['LEVY_API_KEY']
BASE_URL = 'https://app.levyelectric.com/api/partner/v1'
headers = {
'X-Partner-Api-Key': API_KEY,
'Content-Type': 'application/json'
}
# Get a specific vehicle
response = requests.get(
f'{BASE_URL}/vehicles/uuid-here',
headers=headers
)
vehicle = response.json()['data']
print(f"Vehicle {vehicle['vehicle_number']}: {vehicle['status']}")
Ready to Integrate
With the Partner API, you can build powerful integrations that connect Levy Fleets with your existing systems. Start with read-only access, test thoroughly, then enable write permissions for production use.