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

# Best Practices

> Best practices for using the Gather API effectively

## Overview

This guide covers best practices for using the Gather API to build robust, efficient integrations.

## Authentication

### Secure Credential Storage

* **Never commit API keys** to version control
* **Use environment variables** for credentials
* **Rotate keys regularly** for security
* **Use separate keys** for different environments (production, staging)

### Credential Format

Always use the correct format: `organizationId:apiKey`

```javascript theme={null}
// ✅ Correct
const apiKey = 'qapi_your-api-key-here';

// ❌ Incorrect
const apiKey = 'qapi_your-api-key-here';
```

## Request Handling

### Error Handling

Always handle errors gracefully:

```javascript theme={null}
try {
  const response = await fetch(url, options);
  
  if (!response.ok) {
    const error = await response.json();
    // Handle specific error codes
    if (response.status === 429) {
      // Handle rate limiting
    } else if (response.status === 400) {
      // Handle validation errors
    }
    throw new Error(error.error.message);
  }
  
  return await response.json();
} catch (error) {
  // Log and handle error
  console.error('API request failed:', error);
  throw error;
}
```

### Retry Logic

Implement retry logic for transient failures:

* Retry on 429 (rate limit) and 5xx errors
* Use exponential backoff
* Don't retry on 4xx client errors
* Set maximum retry attempts

### Timeouts

Set appropriate timeouts:

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

fetch(url, {
  ...options,
  signal: controller.signal
}).finally(() => clearTimeout(timeoutId));
```

## Rate Limiting

### Monitor Rate Limits

Check rate limit headers on every response:

```javascript theme={null}
const remaining = parseInt(response.headers.get('X-RateLimit-Remaining'));
const limit = parseInt(response.headers.get('X-RateLimit-Limit'));

if (remaining < 100) {
  // Slow down requests
}
```

### Optimize Request Frequency

* **Cache responses** when possible
* **Batch operations** when available
* **Use webhooks** instead of polling
* **Request only needed data**

## Data Management

### Use External IDs

Use `externalId` to maintain references:

```json theme={null}
{
  "firstName": "John",
  "lastName": "Doe",
  "email": "john@example.com",
  "externalId": "your-system-id-123"
}
```

### Phone Number Format

Always use E.164 format:

* ✅ `+1234567890`
* ❌ `123-456-7890`
* ❌ `(123) 456-7890`

### Idempotency

Make operations idempotent when possible:

* Use external IDs to prevent duplicates
* Check if resource exists before creating
* Handle duplicate creation gracefully

## Webhooks

### Verify Signatures

Always verify webhook signatures:

```javascript theme={null}
function verifySignature(payload, signature, secret) {
  // Verify signature before processing
  // ...
}
```

### Handle Duplicates

Webhooks may be delivered multiple times:

* Use event IDs or timestamps for deduplication
* Make webhook handlers idempotent
* Log all webhook events

### Respond Quickly

Respond to webhooks within 5 seconds:

* Process webhooks asynchronously
* Queue heavy operations
* Return 200 OK immediately

## Performance

### Connection Pooling

Reuse HTTP connections:

```javascript theme={null}
// Use a persistent HTTP client
const https = require('https');
const agent = new https.Agent({ keepAlive: true });
```

### Parallel Requests

Make independent requests in parallel:

```javascript theme={null}
const [questions, interviews, candidates] = await Promise.all([
  fetchQuestions(),
  fetchInterviews(),
  fetchCandidates()
]);
```

### Pagination

Handle pagination efficiently:

```javascript theme={null}
async function fetchAllPages(endpoint) {
  let page = 1;
  let allData = [];
  
  while (true) {
    const response = await fetch(`${endpoint}?page=${page}&limit=100`);
    const data = await response.json();
    
    allData.push(...data.data);
    
    if (page >= data.meta.pagination.totalPages) break;
    page++;
  }
  
  return allData;
}
```

## Security

### Input Validation

Validate input before sending to API:

```javascript theme={null}
function validateEmail(email) {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}

function validatePhone(phone) {
  return /^\+[1-9]\d{1,14}$/.test(phone);
}
```

### HTTPS Only

Always use HTTPS:

* ✅ `https://api.prod.qualifi.hr`
* ❌ `http://api.prod.qualifi.hr`

### Logging

Don't log sensitive data:

```javascript theme={null}
// ✅ Good
console.log('Request sent to:', endpoint);

// ❌ Bad
console.log('Credentials:', credentials);
```

## Testing

### Test Error Scenarios

Test your error handling:

* Invalid credentials
* Rate limiting
* Network failures
* Invalid data

### Use Staging Environment

Test in staging before production:

* Use `https://api.dev.qualifi.hr` for testing
* Verify all workflows
* Test error scenarios

## Monitoring

### Track Metrics

Monitor key metrics:

* Request success rate
* Error rates by type
* Response times
* Rate limit usage

### Logging

Log important events:

* API requests and responses
* Errors with request IDs
* Rate limit warnings
* Webhook events

## Related Resources

<CardGroup cols={2}>
  <Card title="Error Handling" icon="alert-triangle" href="/guides/error-handling">
    Learn about error handling patterns
  </Card>

  <Card title="Rate Limits" icon="gauge" href="/getting-started/rate-limits">
    Understand rate limits and throttling
  </Card>

  <Card title="Handling Webhooks" icon="webhook" href="/guides/handling-webhooks">
    Best practices for webhooks
  </Card>
</CardGroup>
