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

# Security Guidelines

> Best practices for securing your Monei integration

## Overview

Security is paramount when building financial applications. This guide covers essential security practices for your Monei integration.

**What you'll learn:**

* API key management
* Authentication best practices
* Data protection
* Rate limiting
* Common security threats
* Incident response

***

## API Key Security

Your API key is the gateway to your Monei account. Protect it at all costs.

<CardGroup cols={2}>
  <Card title="Never Expose Keys" icon="eye-slash">
    Never commit API keys to version control or expose them client-side
  </Card>

  <Card title="Use Environment Variables" icon="file-code">
    Store keys in environment variables, not in code
  </Card>

  <Card title="Rotate Regularly" icon="rotate">
    Rotate API keys periodically (every 90 days recommended)
  </Card>

  <Card title="Separate Environments" icon="layer-group">
    Use different keys for development, staging, and production
  </Card>
</CardGroup>

***

## Storing API Keys

### ✅ **CORRECT - Environment Variables**

<CodeGroup>
  ```javascript Node.js theme={null}
  // .env file (DO NOT COMMIT)
  MONEI_API_KEY=your_api_key_here
  MONEI_ENVIRONMENT=production

  // Load from environment
  require('dotenv').config();

  const monei = new MoneiSDK({
    apiKey: process.env.MONEI_API_KEY,
  });
  ```

  ```python Python theme={null}
  # .env file (DO NOT COMMIT)
  MONEI_API_KEY=your_api_key_here
  MONEI_ENVIRONMENT=production

  # Load from environment
  import os
  from dotenv import load_dotenv

  load_dotenv()

  monei = MoneiClient(
      api_key=os.getenv('MONEI_API_KEY')
  )
  ```

  ```bash .gitignore theme={null}
  # Always add .env to .gitignore
  .env
  .env.local
  .env.production
  ```
</CodeGroup>

### ❌ **WRONG - Hardcoded in Code**

<CodeGroup>
  ```javascript NEVER DO THIS theme={null}
  // ❌ NEVER hardcode API keys
  const monei = new MoneiSDK({
    apiKey: 'sk_live_abc123xyz456...',  // NEVER!
  });
  ```

  ```python NEVER DO THIS theme={null}
  # ❌ NEVER hardcode API keys
  monei = MoneiClient(
      api_key='sk_live_abc123xyz456...'  # NEVER!
  )
  ```
</CodeGroup>

***

## Environment Separation

Use different API keys for different environments:

| Environment     | Key Type      | Purpose                       |
| --------------- | ------------- | ----------------------------- |
| **Development** | `sk_test_...` | Local development and testing |
| **Staging**     | `sk_test_...` | Pre-production testing        |
| **Production**  | `sk_live_...` | Live production environment   |

<CodeGroup>
  ```javascript Node.js theme={null}
  // config.js
  const config = {
    development: {
      apiKey: process.env.MONEI_DEV_API_KEY,
      baseUrl: 'https://api.dev.monei.cc'
    },
    production: {
      apiKey: process.env.MONEI_PROD_API_KEY,
      baseUrl: 'https://api.monei.cc'
    }
  };

  const env = process.env.NODE_ENV || 'development';
  const monei = new MoneiSDK(config[env]);
  ```

  ```python Python theme={null}
  # config.py
  import os

  ENVIRONMENTS = {
      'development': {
          'api_key': os.getenv('MONEI_DEV_API_KEY'),
          'base_url': 'https://api.dev.monei.cc'
      },
      'production': {
          'api_key': os.getenv('MONEI_PROD_API_KEY'),
          'base_url': 'https://api.monei.cc'
      }
  }

  env = os.getenv('ENVIRONMENT', 'development')
  config = ENVIRONMENTS[env]

  monei = MoneiClient(
      api_key=config['api_key'],
      base_url=config['base_url']
  )
  ```
</CodeGroup>

***

## Request Authentication

All API requests must include your API key in the `x-api-key` header:

<CodeGroup>
  ```javascript Node.js theme={null}
  // Using SDK (handles automatically)
  const monei = new MoneiSDK({
    apiKey: process.env.MONEI_API_KEY,
  });

  // Manual request
  const response = await fetch('https://api.monei.cc/api/v1/wallet/me', {
    headers: {
      'x-api-key': process.env.MONEI_API_KEY,
      'Content-Type': 'application/json'
    }
  });
  ```

  ```python Python theme={null}
  # Using SDK (handles automatically)
  monei = MoneiClient(api_key=os.getenv('MONEI_API_KEY'))

  # Manual request
  import requests

  response = requests.get(
      'https://api.monei.cc/api/v1/wallet/me',
      headers={
          'x-api-key': os.getenv('MONEI_API_KEY'),
          'Content-Type': 'application/json'
      }
  )
  ```

  ```bash cURL theme={null}
  curl https://api.monei.cc/api/v1/wallet/me \
    -H "x-api-key: YOUR_API_KEY" \
    -H "Content-Type: application/json"
  ```
</CodeGroup>

***

## Data Protection

### Sensitive Data Handling

<AccordionGroup>
  <Accordion icon="lock" title="Encrypt Sensitive Data">
    **What to encrypt:**

    * User personal information (PII)
    * Transaction details
    * Bank account numbers
    * Phone numbers

    **Best practices:**

    * Use AES-256 encryption at rest
    * Use TLS 1.3 for data in transit
    * Encrypt database backups
    * Never log sensitive data
  </Accordion>

  <Accordion icon="database" title="Secure Storage">
    **Database security:**

    * Enable encryption at rest
    * Use strong passwords
    * Limit database access
    * Regular security audits

    **Example:**

    ```javascript theme={null}
    // ❌ NEVER store plaintext sensitive data
    const user = {
      name: 'John Doe',
      accountNumber: '0123456789'  // ❌ Don't do this
    };

    // ✅ Hash or encrypt sensitive fields
    const user = {
      name: 'John Doe',
      accountNumberHash: encrypt('0123456789', encryptionKey)  // ✅ Better
    };
    ```
  </Accordion>

  <Accordion icon="eye-slash" title="Minimize Data Collection">
    **Collect only what you need:**

    * Don't store card CVVs
    * Don't store full card numbers
    * Minimize PII collection
    * Delete data when no longer needed

    **Data retention:**

    * Transaction logs: 7 years (compliance)
    * Temporary data: Delete after use
    * Inactive accounts: Archive after 1 year
  </Accordion>

  <Accordion icon="user-shield" title="Access Control">
    **Principle of least privilege:**

    * Limit API key permissions
    * Use role-based access control (RBAC)
    * Audit access logs regularly
    * Implement multi-factor authentication (MFA)
  </Accordion>
</AccordionGroup>

***

## Rate Limiting

Monei implements rate limiting to prevent abuse:

| Endpoint Type        | Rate Limit    | Window   |
| -------------------- | ------------- | -------- |
| **Read Operations**  | 1000 requests | 1 minute |
| **Write Operations** | 100 requests  | 1 minute |
| **Authentication**   | 10 requests   | 1 minute |
| **Webhooks**         | 500 requests  | 1 minute |

### Handle Rate Limits

<CodeGroup>
  ```javascript Node.js theme={null}
  async function makeRequestWithRetry(apiCall, maxRetries = 3) {
    for (let i = 0; i < maxRetries; i++) {
      try {
        return await apiCall();
      } catch (error) {
        // Check for rate limit error
        if (error.statusCode === 429) {
          const retryAfter = error.headers['retry-after'] || 60;
          console.log(`Rate limited. Retrying after ${retryAfter}s...`);
          
          // Wait before retry
          await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
          continue;
        }
        
        throw error;
      }
    }
    
    throw new Error('Max retries exceeded');
  }

  // Usage
  const wallet = await makeRequestWithRetry(() => monei.wallet.me());
  ```

  ```python Python theme={null}
  import time

  def make_request_with_retry(api_call, max_retries=3):
      for i in range(max_retries):
          try:
              return api_call()
          except Exception as error:
              # Check for rate limit
              if hasattr(error, 'status_code') and error.status_code == 429:
                  retry_after = error.headers.get('retry-after', 60)
                  print(f'Rate limited. Retrying after {retry_after}s...')
                  
                  time.sleep(int(retry_after))
                  continue
              
              raise error
      
      raise Exception('Max retries exceeded')

  # Usage
  wallet = make_request_with_retry(lambda: monei.wallet.me())
  ```
</CodeGroup>

***

## Common Security Threats

<Tabs>
  <Tab title="API Key Exposure">
    **Threat:** API key leaked in public repository or client-side code

    **Prevention:**

    * Never commit `.env` files
    * Add `.env` to `.gitignore`
    * Use environment variables
    * Never expose keys in frontend
    * Scan repositories for leaked keys

    **If compromised:**

    1. Immediately rotate API key
    2. Revoke old key
    3. Audit all transactions
    4. Check for unauthorized access
    5. Update all environments
  </Tab>

  <Tab title="Man-in-the-Middle">
    **Threat:** Attacker intercepts API requests

    **Prevention:**

    * Always use HTTPS
    * Validate SSL certificates
    * Use TLS 1.3
    * Implement certificate pinning (mobile apps)

    **Example:**

    ```javascript theme={null}
    // ✅ Always use HTTPS
    const apiUrl = 'https://api.monei.cc';

    // ❌ Never use HTTP
    const apiUrl = 'http://api.monei.cc';  // NEVER!
    ```
  </Tab>

  <Tab title="Replay Attacks">
    **Threat:** Attacker re-sends captured requests

    **Prevention:**

    * Use unique request IDs
    * Implement request timestamps
    * Short-lived tokens
    * Idempotency keys

    **Example:**

    ```javascript theme={null}
    // Generate unique reference per request
    const reference = `TXN-${Date.now()}-${crypto.randomUUID()}`;

    const payment = await monei.bills.pay({
      billerId: 'mtn-ng',
      customerId: '08012345678',
      amount: 1000,
      reference: reference  // Unique per transaction
    });
    ```
  </Tab>

  <Tab title="SQL Injection">
    **Threat:** Malicious SQL in input data

    **Prevention:**

    * Use parameterized queries
    * Validate all inputs
    * Use ORMs
    * Never concatenate SQL

    **Example:**

    ```javascript theme={null}
    // ❌ NEVER do this
    const query = `SELECT * FROM users WHERE id = ${userId}`;

    // ✅ Use parameterized queries
    const query = 'SELECT * FROM users WHERE id = ?';
    db.query(query, [userId]);
    ```
  </Tab>
</Tabs>

***

## Input Validation

Always validate and sanitize user inputs:

<CodeGroup>
  ```javascript Node.js theme={null}
  // Validation helpers
  function validatePhoneNumber(phone) {
    // Nigerian phone: 11 digits starting with 0
    const cleaned = phone.replace(/\s+/g, '');
    if (!/^0[789][01]\d{8}$/.test(cleaned)) {
      throw new Error('Invalid phone number format');
    }
    return cleaned;
  }

  function validateAmount(amount, min = 0, max = Infinity) {
    const num = parseFloat(amount);
    
    if (isNaN(num)) {
      throw new Error('Amount must be a number');
    }
    
    if (num < min) {
      throw new Error(`Amount must be at least ${min}`);
    }
    
    if (num > max) {
      throw new Error(`Amount cannot exceed ${max}`);
    }
    
    return num;
  }

  function sanitizeNarration(narration) {
    // Remove potentially dangerous characters
    return narration
      .replace(/[<>]/g, '')  // Remove HTML tags
      .replace(/['"]/g, '')  // Remove quotes
      .trim()
      .substring(0, 200);    // Limit length
  }

  // Usage
  try {
    const phone = validatePhoneNumber(userInput.phone);
    const amount = validateAmount(userInput.amount, 50, 50000);
    const narration = sanitizeNarration(userInput.narration);
    
    // Safe to use
    const payment = await monei.bills.pay({
      billerId: 'mtn-ng',
      customerId: phone,
      amount: amount,
      narration: narration
    });
  } catch (error) {
    console.error('Validation error:', error.message);
  }
  ```

  ```python Python theme={null}
  import re

  def validate_phone_number(phone):
      # Nigerian phone
      cleaned = re.sub(r'\s+', '', phone)
      if not re.match(r'^0[789][01]\d{8}$', cleaned):
          raise ValueError('Invalid phone number')
      return cleaned

  def validate_amount(amount, min_val=0, max_val=float('inf')):
      try:
          num = float(amount)
      except ValueError:
          raise ValueError('Amount must be a number')
      
      if num < min_val:
          raise ValueError(f'Amount must be at least {min_val}')
      
      if num > max_val:
          raise ValueError(f'Amount cannot exceed {max_val}')
      
      return num

  def sanitize_narration(narration):
      # Remove dangerous characters
      cleaned = re.sub(r'[<>\'"]', '', narration)
      return cleaned.strip()[:200]

  # Usage
  try:
      phone = validate_phone_number(user_input['phone'])
      amount = validate_amount(user_input['amount'], 50, 50000)
      narration = sanitize_narration(user_input['narration'])
      
      # Safe to use
      payment = monei.bills.pay(
          biller_id='mtn-ng',
          customer_id=phone,
          amount=amount,
          narration=narration
      )
  except ValueError as error:
      print(f'Validation error: {error}')
  ```
</CodeGroup>

***

## Logging Best Practices

<AccordionGroup>
  <Accordion icon="list" title="What to Log">
    **DO log:**

    * API requests (without sensitive data)
    * Response status codes
    * Error messages
    * Transaction references
    * User actions
    * System events

    **Example:**

    ```javascript theme={null}
    logger.info('Payment initiated', {
      reference: payment.reference,
      amount: payment.amount,
      currency: payment.currency,
      timestamp: new Date().toISOString()
    });
    ```
  </Accordion>

  <Accordion icon="eye-slash" title="What NOT to Log">
    **NEVER log:**

    * API keys
    * Passwords
    * Card numbers
    * CVVs
    * PINs
    * Full bank account numbers
    * Personal identification numbers

    **Example:**

    ```javascript theme={null}
    // ❌ NEVER log sensitive data
    logger.info('Payment', {
      cardNumber: '4242424242424242',  // NEVER!
      cvv: '123'                       // NEVER!
    });

    // ✅ Log safely
    logger.info('Payment', {
      cardLast4: '4242',  // ✅ OK
      reference: 'TXN-123'
    });
    ```
  </Accordion>

  <Accordion icon="shield-check" title="Secure Log Storage">
    **Best practices:**

    * Encrypt logs at rest
    * Restrict log access
    * Rotate logs regularly
    * Centralized logging
    * Log retention policies

    **Retention:**

    * Application logs: 30 days
    * Transaction logs: 7 years
    * Error logs: 90 days
    * Audit logs: 1 year
  </Accordion>
</AccordionGroup>

***

## Incident Response

What to do if security is compromised:

<Steps>
  <Step title="Identify the Breach">
    * Detect unauthorized access
    * Check logs for anomalies
    * Identify affected systems
  </Step>

  <Step title="Contain the Damage">
    * Rotate API keys immediately
    * Revoke compromised credentials
    * Block suspicious IPs
    * Isolate affected systems
  </Step>

  <Step title="Assess the Impact">
    * Review transaction logs
    * Check for unauthorized transactions
    * Identify affected users
    * Document all findings
  </Step>

  <Step title="Notify Stakeholders">
    * Contact Monei support
    * Notify affected users
    * Report to authorities (if required)
    * Update security team
  </Step>

  <Step title="Remediate">
    * Fix security vulnerabilities
    * Update security policies
    * Implement additional controls
    * Train team on prevention
  </Step>

  <Step title="Monitor">
    * Enhanced monitoring
    * Regular security audits
    * Penetration testing
    * Continuous improvement
  </Step>
</Steps>

***

## Security Checklist

<CardGroup cols={2}>
  <Card title="API Keys" icon="check">
    ✅ Stored in environment variables\
    ✅ Never committed to git\
    ✅ Rotated every 90 days\
    ✅ Separate keys per environment
  </Card>

  <Card title="Data Protection" icon="check">
    ✅ Encryption at rest\
    ✅ TLS 1.3 in transit\
    ✅ No sensitive data in logs\
    ✅ Regular backups
  </Card>

  <Card title="Access Control" icon="check">
    ✅ Role-based access\
    ✅ Least privilege principle\
    ✅ MFA enabled\
    ✅ Regular access audits
  </Card>

  <Card title="Monitoring" icon="check">
    ✅ Real-time alerts\
    ✅ Transaction monitoring\
    ✅ Error tracking\
    ✅ Audit logs
  </Card>
</CardGroup>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Webhooks" icon="webhook" href="/security/webhooks">
    Secure webhook implementation
  </Card>

  <Card title="Best Practices" icon="shield-check" href="/security/best-practices">
    Additional security best practices
  </Card>

  <Card title="Testing" icon="vial" href="/testing/sandbox">
    Test in sandbox environment
  </Card>

  <Card title="Error Handling" icon="triangle-exclamation" href="/core-concepts/error-handling">
    Handle errors securely
  </Card>
</CardGroup>
