Skip to main content

Security

Protect Your API Key

  • Never expose in public source code
  • Use environment variables
  • Don’t include in logs

Use HTTPS

  • Always use HTTPS for requests
  • Verify SSL certificates
  • Don’t accept invalid certificates

Set Expiration

  • Configure expiration dates for keys
  • Rotate keys periodically
  • Revoke compromised keys immediately

Least Privilege Principle

  • Create keys with minimum necessary permissions
  • Use different keys for different environments
  • Delete unused keys

Error Handling

1

Always Check HTTP Status

Don’t assume success - check the status code in every request.
if (!response.ok) {
  const error = await response.json();
  console.error('Error:', error.error.message);
}
2

Implement Retry Logic

For temporary errors (5xx, 429), implement retry with exponential backoff.
const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s, 8s...
await new Promise(r => setTimeout(r, delay));
3

Use Appropriate Timeouts

Configure timeouts to avoid hanging requests.
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 30000);

const response = await fetch(url, {
  signal: controller.signal
});
4

Handle Specific Errors

Implement handlers for different error types.
switch (response.status) {
  case 401: handleAuthError(); break;
  case 404: handleNotFound(); break;
  case 429: handleRateLimit(); break;
}

Performance

  • Implement local cache when appropriate
  • Use filters to fetch only necessary data
  • Batch operations when possible
  • Always use limit and offset for large lists
  • Don’t try to fetch all data at once
  • Process data in batches
let offset = 0;
const limit = 50;

while (true) {
  const response = await fetch(
    `${url}?limit=${limit}&offset=${offset}`
  );
  const data = await response.json();

  if (data.length === 0) break;

  processData(data);
  offset += limit;
}
  • Monitor rate limit headers
  • Implement client-side throttling
  • Use queues for bulk operations
  • Record request latencies
  • Set up alerts for degradation
  • Use metrics to identify issues

Webhooks

Respond Quickly

Return 200 quickly and process in background. Webhooks have timeout.

Idempotency

Process events idempotently - the same event may be sent more than once.

Validate Payloads

Always validate the payload structure before processing.

Log Events

Record all received events for debugging and auditing.

Robust Client Example

class LeavoClient {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.baseUrl = 'https://api.leavo.ai';
  }

  async request(method, path, data = null, retries = 3) {
    for (let attempt = 0; attempt < retries; attempt++) {
      try {
        const response = await fetch(`${this.baseUrl}${path}`, {
          method,
          headers: {
            'Authorization': `Bearer ${this.apiKey}`,
            'Content-Type': 'application/json'
          },
          body: data ? JSON.stringify(data) : null,
          signal: AbortSignal.timeout(30000)
        });

        if (response.status === 429) {
          const retryAfter = response.headers.get('Retry-After') || 60;
          await this.sleep(retryAfter * 1000);
          continue;
        }

        if (response.status >= 500) {
          await this.sleep(Math.pow(2, attempt) * 1000);
          continue;
        }

        if (!response.ok) {
          const error = await response.json();
          throw new Error(error.error?.message || 'Request failed');
        }

        return response.status === 204 ? null : response.json();
      } catch (error) {
        if (attempt === retries - 1) throw error;
        await this.sleep(Math.pow(2, attempt) * 1000);
      }
    }
  }

  sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  // Convenience methods
  getLeads(params = {}) {
    const query = new URLSearchParams(params).toString();
    return this.request('GET', `/backend/leads?${query}`);
  }

  createLead(data) {
    return this.request('POST', '/backend/leads', data);
  }

  updateLead(id, data) {
    return this.request('PUT', `/backend/leads/${id}`, data);
  }

  deleteLead(id) {
    return this.request('DELETE', `/backend/leads/${id}`);
  }
}

// Usage
const client = new LeavoClient(process.env.LEAVO_API_KEY);
const leads = await client.getLeads({ limit: 50 });