> ## Documentation Index
> Fetch the complete documentation index at: https://docs.leavo.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Best Practices

> Recommendations for using the API efficiently and securely

## Security

<CardGroup cols={2}>
  <Card title="Protect Your API Key" icon="key">
    * Never expose in public source code
    * Use environment variables
    * Don't include in logs
  </Card>

  <Card title="Use HTTPS" icon="lock">
    * Always use HTTPS for requests
    * Verify SSL certificates
    * Don't accept invalid certificates
  </Card>

  <Card title="Set Expiration" icon="clock">
    * Configure expiration dates for keys
    * Rotate keys periodically
    * Revoke compromised keys immediately
  </Card>

  <Card title="Least Privilege Principle" icon="shield">
    * Create keys with minimum necessary permissions
    * Use different keys for different environments
    * Delete unused keys
  </Card>
</CardGroup>

## Error Handling

<Steps>
  <Step title="Always Check HTTP Status">
    Don't assume success - check the status code in every request.

    ```javascript theme={null}
    if (!response.ok) {
      const error = await response.json();
      console.error('Error:', error.error.message);
    }
    ```
  </Step>

  <Step title="Implement Retry Logic">
    For temporary errors (5xx, 429), implement retry with exponential backoff.

    ```javascript theme={null}
    const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s, 8s...
    await new Promise(r => setTimeout(r, delay));
    ```
  </Step>

  <Step title="Use Appropriate Timeouts">
    Configure timeouts to avoid hanging requests.

    ```javascript theme={null}
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), 30000);

    const response = await fetch(url, {
      signal: controller.signal
    });
    ```
  </Step>

  <Step title="Handle Specific Errors">
    Implement handlers for different error types.

    ```javascript theme={null}
    switch (response.status) {
      case 401: handleAuthError(); break;
      case 404: handleNotFound(); break;
      case 429: handleRateLimit(); break;
    }
    ```
  </Step>
</Steps>

## Performance

<AccordionGroup>
  <Accordion title="Avoid Unnecessary Requests" icon="bolt">
    * Implement local cache when appropriate
    * Use filters to fetch only necessary data
    * Batch operations when possible
  </Accordion>

  <Accordion title="Use Pagination" icon="list">
    * Always use `limit` and `offset` for large lists
    * Don't try to fetch all data at once
    * Process data in batches

    ```javascript theme={null}
    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;
    }
    ```
  </Accordion>

  <Accordion title="Respect Rate Limits" icon="gauge">
    * Monitor rate limit headers
    * Implement client-side throttling
    * Use queues for bulk operations
  </Accordion>

  <Accordion title="Monitor Response Times" icon="clock">
    * Record request latencies
    * Set up alerts for degradation
    * Use metrics to identify issues
  </Accordion>
</AccordionGroup>

## Webhooks

<CardGroup cols={2}>
  <Card title="Respond Quickly" icon="gauge-high">
    Return 200 quickly and process in background. Webhooks have timeout.
  </Card>

  <Card title="Idempotency" icon="clone">
    Process events idempotently - the same event may be sent more than once.
  </Card>

  <Card title="Validate Payloads" icon="check-double">
    Always validate the payload structure before processing.
  </Card>

  <Card title="Log Events" icon="file-lines">
    Record all received events for debugging and auditing.
  </Card>
</CardGroup>

## Robust Client Example

<CodeGroup>
  ```javascript JavaScript theme={null}
  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 });
  ```

  ```python Python theme={null}
  import os
  import time
  import requests
  from typing import Optional, Dict, Any

  class LeavoClient:
      def __init__(self, api_key: str):
          self.api_key = api_key
          self.base_url = 'https://api.leavo.ai'
          self.session = requests.Session()
          self.session.headers.update({
              'Authorization': f'Bearer {api_key}',
              'Content-Type': 'application/json'
          })

      def request(
          self,
          method: str,
          path: str,
          data: Optional[Dict] = None,
          retries: int = 3
      ) -> Any:
          for attempt in range(retries):
              try:
                  response = self.session.request(
                      method,
                      f'{self.base_url}{path}',
                      json=data,
                      timeout=30
                  )

                  if response.status_code == 429:
                      retry_after = int(response.headers.get('Retry-After', 60))
                      time.sleep(retry_after)
                      continue

                  if response.status_code >= 500:
                      time.sleep(2 ** attempt)
                      continue

                  response.raise_for_status()

                  return response.json() if response.status_code != 204 else None

              except requests.RequestException as e:
                  if attempt == retries - 1:
                      raise
                  time.sleep(2 ** attempt)

      # Convenience methods
      def get_leads(self, **params):
          return self.request('GET', '/backend/leads', params=params)

      def create_lead(self, data: Dict):
          return self.request('POST', '/backend/leads', data=data)

      def update_lead(self, lead_id: str, data: Dict):
          return self.request('PUT', f'/backend/leads/{lead_id}', data=data)

      def delete_lead(self, lead_id: str):
          return self.request('DELETE', f'/backend/leads/{lead_id}')


  # Usage
  client = LeavoClient(os.environ['LEAVO_API_KEY'])
  leads = client.get_leads(limit=50)
  ```
</CodeGroup>
