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

# Security Best Practices

> Keep your integration secure

Follow these security best practices when integrating with the meetergo API.

## API Key Security

### Storage

<Check>
  **Use environment variables** - Never hardcode API keys
</Check>

```bash theme={null}
# .env (never commit this file)
MEETERGO_API_KEY=ak_live:uuid:secret
```

```javascript theme={null}
// Access via environment
const apiKey = process.env.MEETERGO_API_KEY;
```

<Check>
  **Use secrets managers** for production deployments
</Check>

* AWS Secrets Manager
* Google Secret Manager
* HashiCorp Vault
* Azure Key Vault

<Warning>
  **Never commit API keys** to version control
</Warning>

Add to `.gitignore`:

```
.env
.env.local
.env.*.local
```

### Key Rotation

API keys expire after 1-90 days. Implement rotation:

1. **Create new key** before the old one expires
2. **Deploy** with the new key
3. Old key automatically expires

```javascript theme={null}
// Track key expiration
const keyCreatedAt = new Date('2024-01-15');
const expiresAt = new Date(keyCreatedAt);
expiresAt.setDate(expiresAt.getDate() + 90);

const daysUntilExpiry = Math.floor((expiresAt - Date.now()) / (1000 * 60 * 60 * 24));

if (daysUntilExpiry < 7) {
  console.warn(`API key expires in ${daysUntilExpiry} days!`);
}
```

### Key Isolation

Use separate API keys for:

| Environment | Key Name      |
| ----------- | ------------- |
| Development | `Development` |
| Staging     | `Staging`     |
| Production  | `Production`  |

This limits blast radius if a key is compromised.

## Webhook Security

### Use HTTPS

Always use HTTPS endpoints for webhooks:

```javascript theme={null}
// Good
const endpoint = 'https://your-server.com/webhooks/meetergo';

// Bad - never use HTTP in production
const endpoint = 'http://your-server.com/webhooks/meetergo';
```

### Validate Payloads

Verify webhook payloads before processing:

```javascript theme={null}
app.post('/webhooks/meetergo', (req, res) => {
  const { event, data } = req.body;

  // Validate required fields
  if (!event || !data) {
    console.error('Invalid webhook payload');
    return res.status(400).send('Invalid payload');
  }

  // Validate event type
  const validEvents = ['booking_created', 'booking_cancelled', 'booking_rescheduled', 'new_employee'];
  if (!validEvents.includes(event)) {
    console.error('Unknown event type:', event);
    return res.status(400).send('Unknown event');
  }

  // Process the webhook
  res.status(200).send('OK');
});
```

### Prevent Replay Attacks

Use timestamps and idempotency:

```javascript theme={null}
const processedEvents = new Map();
const MAX_AGE_MS = 5 * 60 * 1000; // 5 minutes

app.post('/webhooks/meetergo', (req, res) => {
  const { event, data } = req.body;
  const eventKey = `${event}:${data.id}:${data.updatedAt}`;

  // Check for replays
  if (processedEvents.has(eventKey)) {
    console.log('Duplicate event, skipping');
    return res.status(200).send('OK');
  }

  // Check timestamp (if available)
  const eventTime = new Date(data.updatedAt || data.createdAt);
  if (Date.now() - eventTime.getTime() > MAX_AGE_MS) {
    console.warn('Old event received, possible replay');
  }

  processedEvents.set(eventKey, Date.now());
  res.status(200).send('OK');

  // Clean up old entries periodically
  for (const [key, timestamp] of processedEvents) {
    if (Date.now() - timestamp > MAX_AGE_MS) {
      processedEvents.delete(key);
    }
  }
});
```

## Data Handling

### Minimize Data Exposure

Only request and store data you need:

```javascript theme={null}
// Store only necessary fields
function storeBooking(webhookData) {
  return {
    id: webhookData.id,
    start: webhookData.start,
    end: webhookData.end,
    attendeeEmail: webhookData.attendees[0]?.email,
    // Don't store sensitive fields you don't need
  };
}
```

### Sanitize User Input

Validate and sanitize data before using:

```javascript theme={null}
function sanitizeAttendee(attendee) {
  return {
    email: validateEmail(attendee.email),
    firstname: sanitizeString(attendee.firstname, 100),
    lastname: sanitizeString(attendee.lastname, 100),
    phone: sanitizePhone(attendee.phone),
  };
}

function validateEmail(email) {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  if (!email || !emailRegex.test(email)) {
    throw new Error('Invalid email');
  }
  return email.toLowerCase().trim();
}

function sanitizeString(str, maxLength) {
  if (!str) return '';
  return str.slice(0, maxLength).trim();
}
```

### Protect PII

Handle personally identifiable information carefully:

* Encrypt at rest
* Encrypt in transit (HTTPS)
* Implement access controls
* Log access to sensitive data
* Follow data retention policies

## Logging

### Do Log

* Request timestamps
* Response status codes
* Error messages (without sensitive data)
* Rate limit warnings

### Don't Log

* API keys
* Full request bodies with PII
* Webhook payloads with customer data

```javascript theme={null}
// Good logging
console.log({
  timestamp: new Date().toISOString(),
  endpoint: '/v4/booking',
  method: 'POST',
  statusCode: 201,
  durationMs: 150
});

// Bad - exposes API key
console.log({
  headers: request.headers // Contains Authorization header!
});

// Bad - exposes customer data
console.log({
  body: request.body // Contains email, phone, etc.
});
```

## Server Security

### Keep Dependencies Updated

Regularly update your dependencies:

```bash theme={null}
# Check for vulnerabilities
npm audit

# Update packages
npm update
```

### Use Security Headers

Protect your webhook endpoints:

```javascript theme={null}
const helmet = require('helmet');
app.use(helmet());

// Or manually:
app.use((req, res, next) => {
  res.setHeader('X-Content-Type-Options', 'nosniff');
  res.setHeader('X-Frame-Options', 'DENY');
  res.setHeader('X-XSS-Protection', '1; mode=block');
  next();
});
```

### Rate Limit Your Endpoints

Protect against abuse:

```javascript theme={null}
const rateLimit = require('express-rate-limit');

const webhookLimiter = rateLimit({
  windowMs: 60 * 1000, // 1 minute
  max: 100, // limit each IP
  message: 'Too many requests'
});

app.use('/webhooks', webhookLimiter);
```

## Incident Response

If you suspect a key is compromised:

1. **Immediately revoke** the key in the dashboard
2. **Create a new key** and update your application
3. **Review logs** for unauthorized access
4. **Audit** what data may have been accessed
5. **Notify** affected users if required

## Security Checklist

<Check>
  API keys stored in environment variables or secrets manager
</Check>

<Check>
  API keys never committed to version control
</Check>

<Check>
  Separate keys for development and production
</Check>

<Check>
  Key rotation before expiration
</Check>

<Check>
  HTTPS for all webhook endpoints
</Check>

<Check>
  Webhook payload validation
</Check>

<Check>
  No sensitive data in logs
</Check>

<Check>
  Dependencies regularly updated
</Check>

<Check>
  Security headers configured
</Check>

<Check>
  Rate limiting on your endpoints
</Check>
