Skip to main content
Implementing proper security practices is crucial when working with the Dakota Platform API. Follow these guidelines to keep your integration secure.

API Key Management

Store Keys Securely

Never expose API keys in your code:
Bad Example
// ❌ Never do this
const apiKey = 'AHGlPZaxDSMz8Wf1l8VRH4ObdbHiKsWFWnmRyHtiwAc=';
Use environment variables instead:
cURL
# Use environment variable in shell scripts
curl -X GET https://api.platform.dakota.xyz/customers \
  -H "X-API-Key: $DAKOTA_API_KEY"
JavaScript
// ✅ Use environment variables
const apiKey = process.env.DAKOTA_API_KEY;
Python
import os

api_key = os.getenv('DAKOTA_API_KEY')
Go
package main

import "os"

func main() {
    apiKey := os.Getenv("DAKOTA_API_KEY")
}
Rust
use std::env;

fn main() {
    let api_key = env::var("DAKOTA_API_KEY")
        .expect("DAKOTA_API_KEY environment variable not set");
}
Java
public class DakotaConfig {
    public static void main(String[] args) {
        String apiKey = System.getenv("DAKOTA_API_KEY");
        if (apiKey == null) {
            throw new IllegalStateException("DAKOTA_API_KEY environment variable not set");
        }
    }
}

Environment Variables Setup

Set up your environment variables properly:
.env file
DAKOTA_API_KEY=AHGlPZaxDSMz8Wf1l8VRH4ObdbHiKsWFWnmRyHtiwAc=
Shell
export DAKOTA_API_KEY="AHGlPZaxDSMz8Wf1l8VRH4ObdbHiKsWFWnmRyHtiwAc="

Key Rotation

  • Rotate API keys regularly (recommended: every 90 days)
  • Create new keys before deactivating old ones to avoid downtime
  • Have a key rotation process documented and tested

Access Control

  • Limit API key access to only necessary team members
  • Use separate keys for different services or applications
  • Revoke unused or compromised keys immediately

Request Security

Always Use HTTPS

All API requests must use HTTPS. The Dakota Platform API will reject HTTP requests:
JavaScript
// ✅ Correct - HTTPS
const response = await fetch('https://api.platform.dakota.xyz/customers', {
  headers: { 'X-API-Key': process.env.DAKOTA_API_KEY }
});

// ❌ Wrong - HTTP will be rejected
const response = await fetch('http://api.platform.dakota.xyz/customers', {
  headers: { 'X-API-Key': process.env.DAKOTA_API_KEY }
});

Validate SSL Certificates

Ensure your HTTP client validates SSL certificates:
cURL
# ✅ Certificate validation enabled by default
curl -X GET https://api.platform.dakota.xyz/customers \
  -H "X-API-Key: $DAKOTA_API_KEY"

# ❌ Never disable certificate verification
# curl -k https://api.platform.dakota.xyz/customers  # DON'T DO THIS
Node.js
// ✅ Certificate validation enabled by default
const https = require('https');

// ❌ Never disable certificate validation
// process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0; // DON'T DO THIS
Python
import requests

# ✅ Certificate validation enabled by default
response = requests.get(
    'https://api.platform.dakota.xyz/customers',
    headers={'X-API-Key': api_key}
)

# ❌ Never disable certificate verification
# requests.get(url, verify=False)  # DON'T DO THIS
Go
package main

import (
    "crypto/tls"
    "net/http"
)

func main() {
    // ✅ Certificate validation enabled by default
    client := &http.Client{}
    
    // ❌ Never disable certificate verification
    // client := &http.Client{
    //     Transport: &http.Transport{
    //         TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // DON'T DO THIS
    //     },
    // }
}
Rust
use reqwest::Client;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // ✅ Certificate validation enabled by default
    let client = Client::new();
    
    // ❌ Never disable certificate verification
    // let client = Client::builder()
    //     .danger_accept_invalid_certs(true)  // DON'T DO THIS
    //     .build()?;
    
    Ok(())
}
Java
import java.net.http.HttpClient;
import javax.net.ssl.SSLContext;

public class SecurityExample {
    public static void main(String[] args) {
        // ✅ Certificate validation enabled by default
        HttpClient client = HttpClient.newHttpClient();
        
        // ❌ Never disable certificate verification
        // Don't create custom trust managers that accept all certificates
    }
}

Request Logging Security

When logging requests for debugging, never log sensitive headers:
cURL
# When logging cURL commands, use environment variables
echo "curl -X GET https://api.platform.dakota.xyz/customers \\
  -H \"X-API-Key: \$DAKOTA_API_KEY\""

# Never log actual API keys
# echo "curl -H \"X-API-Key: AHGlPZ...\""  # DON'T DO THIS
JavaScript
function logRequest(url, headers, body) {
  const safeHeaders = { ...headers };
  
  // Remove sensitive headers from logs
  delete safeHeaders['X-API-Key'];
  delete safeHeaders['Authorization'];
  
  console.log('API Request:', {
    url,
    headers: safeHeaders,
    body
  });
}
Python
import logging

def log_request(url, headers, body):
    safe_headers = headers.copy()
    
    # Remove sensitive headers from logs
    safe_headers.pop('X-API-Key', None)
    safe_headers.pop('Authorization', None)
    
    logging.info(f'API Request: {url}, Headers: {safe_headers}')
Go
package main

import (
    "log"
    "net/http"
)

func logRequest(req *http.Request) {
    safeHeaders := make(map[string][]string)
    
    // Copy headers except sensitive ones
    for k, v := range req.Header {
        if k != "X-Api-Key" && k != "Authorization" {
            safeHeaders[k] = v
        }
    }
    
    log.Printf("API Request: %s %s, Headers: %v", req.Method, req.URL, safeHeaders)
}
Rust
use std::collections::HashMap;
use log::info;

fn log_request(url: &str, headers: &HashMap<String, String>, body: &str) {
    let mut safe_headers = headers.clone();
    
    // Remove sensitive headers from logs
    safe_headers.remove("X-API-Key");
    safe_headers.remove("Authorization");
    
    info!("API Request: {}, Headers: {:?}", url, safe_headers);
}
Java
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;

public class RequestLogger {
    private static final Logger logger = Logger.getLogger(RequestLogger.class.getName());
    
    public static void logRequest(String url, Map<String, String> headers, String body) {
        Map<String, String> safeHeaders = new HashMap<>(headers);
        
        // Remove sensitive headers from logs
        safeHeaders.remove("X-API-Key");
        safeHeaders.remove("Authorization");
        
        logger.info(String.format("API Request: %s, Headers: %s", url, safeHeaders));
    }
}

Production Environment

Base URL

Always use the production base URL (Sandbox coming soon):
https://api.platform.dakota.xyz

Network Security

  • Use private networks or VPNs when possible
  • Monitor network traffic for unusual patterns

Error Handling

Implement secure error handling that doesn’t expose sensitive information:
cURL
#!/bin/bash
# Secure error handling in shell scripts
make_secure_request() {
    local endpoint="$1"
    local response
    
    response=$(curl -s -w "%{http_code}" \
        -H "X-API-Key: $DAKOTA_API_KEY" \
        -H "Content-Type: application/json" \
        "https://api.platform.dakota.xyz$endpoint")
    
    local http_code="${response: -3}"
    local body="${response%???}"
    
    if [[ "$http_code" -ge 400 ]]; then
        # Log full error details internally
        echo "API Error: $http_code" >&2
        
        # Return generic error to user
        echo "API request failed" >&2
        return 1
    fi
    
    echo "$body"
}
JavaScript
async function makeSecureRequest(endpoint) {
  try {
    const response = await fetch(`https://api.platform.dakota.xyz${endpoint}`, {
      headers: {
        'X-API-Key': process.env.DAKOTA_API_KEY,
        'Content-Type': 'application/json'
      }
    });
    
    if (!response.ok) {
      // Log full error details internally
      console.error('API Error:', response.status, response.statusText);
      
      // Return generic error to client
      throw new Error('API request failed');
    }
    
    return await response.json();
  } catch (error) {
    // Log error details internally
    console.error('Request failed:', error.message);
    
    // Don't expose internal error details
    throw new Error('Service temporarily unavailable');
  }
}
Python
import requests
import logging
from typing import Dict, Any

def make_secure_request(endpoint: str) -> Dict[Any, Any]:
    try:
        response = requests.get(
            f'https://api.platform.dakota.xyz{endpoint}',
            headers={
                'X-API-Key': os.getenv('DAKOTA_API_KEY'),
                'Content-Type': 'application/json'
            },
            timeout=30
        )
        
        if not response.ok:
            # Log full error details internally
            logging.error(f'API Error: {response.status_code} {response.reason}')
            
            # Return generic error to client
            raise Exception('API request failed')
        
        return response.json()
    except requests.exceptions.RequestException as e:
        # Log error details internally
        logging.error(f'Request failed: {str(e)}')
        
        # Don't expose internal error details
        raise Exception('Service temporarily unavailable')
Go
package main

import (
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "os"
)

func makeSecureRequest(endpoint string) (map[string]interface{}, error) {
    client := &http.Client{}
    req, err := http.NewRequest("GET", "https://api.platform.dakota.xyz"+endpoint, nil)
    if err != nil {
        log.Printf("Request creation failed: %v", err)
        return nil, fmt.Errorf("service temporarily unavailable")
    }
    
    req.Header.Set("X-API-Key", os.Getenv("DAKOTA_API_KEY"))
    req.Header.Set("Content-Type", "application/json")
    
    resp, err := client.Do(req)
    if err != nil {
        // Log error details internally
        log.Printf("Request failed: %v", err)
        
        // Don't expose internal error details
        return nil, fmt.Errorf("service temporarily unavailable")
    }
    defer resp.Body.Close()
    
    if resp.StatusCode >= 400 {
        // Log full error details internally
        log.Printf("API Error: %d %s", resp.StatusCode, resp.Status)
        
        // Return generic error to client
        return nil, fmt.Errorf("API request failed")
    }
    
    var result map[string]interface{}
    if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
        log.Printf("JSON decode failed: %v", err)
        return nil, fmt.Errorf("service temporarily unavailable")
    }
    
    return result, nil
}
Rust
use reqwest::header::{HeaderMap, HeaderValue};
use serde_json::Value;
use std::env;
use log::error;

async fn make_secure_request(endpoint: &str) -> Result<Value, Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    
    let mut headers = HeaderMap::new();
    let api_key = env::var("DAKOTA_API_KEY")
        .map_err(|_| "API key not configured")?;
    headers.insert("X-API-Key", HeaderValue::from_str(&api_key)?);
    headers.insert("Content-Type", HeaderValue::from_static("application/json"));
    
    let url = format!("https://api.platform.dakota.xyz{}", endpoint);
    
    match client.get(&url).headers(headers).send().await {
        Ok(response) => {
            if !response.status().is_success() {
                // Log full error details internally
                error!("API Error: {} {}", response.status(), response.status().canonical_reason().unwrap_or("Unknown"));
                
                // Return generic error to client
                return Err("API request failed".into());
            }
            
            match response.json::<Value>().await {
                Ok(data) => Ok(data),
                Err(e) => {
                    error!("JSON decode failed: {}", e);
                    Err("Service temporarily unavailable".into())
                }
            }
        }
        Err(e) => {
            // Log error details internally
            error!("Request failed: {}", e);
            
            // Don't expose internal error details
            Err("Service temporarily unavailable".into())
        }
    }
}
Java
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.logging.Logger;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public class SecureApiClient {
    private static final Logger logger = Logger.getLogger(SecureApiClient.class.getName());
    private final HttpClient client;
    private final ObjectMapper mapper;
    
    public SecureApiClient() {
        this.client = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(30))
            .build();
        this.mapper = new ObjectMapper();
    }
    
    public JsonNode makeSecureRequest(String endpoint) throws Exception {
        String apiKey = System.getenv("DAKOTA_API_KEY");
        if (apiKey == null) {
            throw new IllegalStateException("API key not configured");
        }
        
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.platform.dakota.xyz" + endpoint))
            .header("X-API-Key", apiKey)
            .header("Content-Type", "application/json")
            .GET()
            .build();
        
        try {
            HttpResponse<String> response = client.send(request, 
                HttpResponse.BodyHandlers.ofString());
            
            if (response.statusCode() >= 400) {
                // Log full error details internally
                logger.severe("API Error: " + response.statusCode());
                
                // Return generic error to client
                throw new Exception("API request failed");
            }
            
            return mapper.readTree(response.body());
        } catch (IOException | InterruptedException e) {
            // Log error details internally
            logger.severe("Request failed: " + e.getMessage());
            
            // Don't expose internal error details
            throw new Exception("Service temporarily unavailable");
        }
    }
}

Webhook Security

Verify Webhook Signatures

Always verify webhook signatures to ensure they come from Dakota Platform:
cURL
#!/bin/bash
# Webhook signature verification in shell (example for testing)
verify_webhook_signature() {
    local payload="$1"
    local signature="$2"
    local secret="$WEBHOOK_SECRET"
    
    expected_signature=$(echo -n "$payload" | openssl dgst -sha256 -hmac "$secret" -hex | cut -d' ' -f2)
    
    if [[ "$signature" == "$expected_signature" ]]; then
        echo "Signature valid"
        return 0
    else
        echo "Invalid signature" >&2
        return 1
    fi
}
JavaScript
const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload, 'utf8')
    .digest('hex');
    
  return crypto.timingSafeEqual(
    Buffer.from(signature, 'hex'),
    Buffer.from(expectedSignature, 'hex')
  );
}

// In your webhook handler
app.post('/webhooks/dakota', (req, res) => {
  const signature = req.headers['x-dakota-signature'];
  const payload = JSON.stringify(req.body);
  
  if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }
  
  // Process webhook safely
  processWebhook(req.body);
  res.status(200).send('OK');
});
Python
import hmac
import hashlib
import json
from flask import Flask, request

def verify_webhook_signature(payload: str, signature: str, secret: str) -> bool:
    expected_signature = hmac.new(
        secret.encode('utf-8'),
        payload.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()
    
    return hmac.compare_digest(signature, expected_signature)

app = Flask(__name__)

@app.route('/webhooks/dakota', methods=['POST'])
def handle_webhook():
    signature = request.headers.get('X-Dakota-Signature')
    payload = request.get_data(as_text=True)
    
    if not verify_webhook_signature(payload, signature, os.getenv('WEBHOOK_SECRET')):
        return 'Invalid signature', 401
    
    # Process webhook safely
    webhook_data = request.get_json()
    process_webhook(webhook_data)
    return 'OK', 200
Go
package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "os"
)

func verifyWebhookSignature(payload, signature, secret string) bool {
    h := hmac.New(sha256.New, []byte(secret))
    h.Write([]byte(payload))
    expectedSignature := hex.EncodeToString(h.Sum(nil))
    
    return hmac.Equal([]byte(signature), []byte(expectedSignature))
}

func webhookHandler(w http.ResponseWriter, r *http.Request) {
    signature := r.Header.Get("X-Dakota-Signature")
    
    body, err := io.ReadAll(r.Body)
    if err != nil {
        http.Error(w, "Error reading body", http.StatusBadRequest)
        return
    }
    
    if !verifyWebhookSignature(string(body), signature, os.Getenv("WEBHOOK_SECRET")) {
        http.Error(w, "Invalid signature", http.StatusUnauthorized)
        return
    }
    
    // Process webhook safely
    var webhookData map[string]interface{}
    if err := json.Unmarshal(body, &webhookData); err != nil {
        http.Error(w, "Invalid JSON", http.StatusBadRequest)
        return
    }
    
    processWebhook(webhookData)
    w.WriteHeader(http.StatusOK)
    fmt.Fprint(w, "OK")
}
Rust
use hmac::{Hmac, Mac};
use sha2::Sha256;
use hex;
use std::env;
use warp::Filter;

type HmacSha256 = Hmac<Sha256>;

fn verify_webhook_signature(payload: &str, signature: &str, secret: &str) -> bool {
    let mut mac = HmacSha256::new_from_slice(secret.as_bytes())
        .expect("HMAC can take key of any size");
    mac.update(payload.as_bytes());
    
    let expected_signature = hex::encode(mac.finalize().into_bytes());
    
    // Use constant-time comparison
    expected_signature == signature
}

#[tokio::main]
async fn main() {
    let webhook_route = warp::path!("webhooks" / "dakota")
        .and(warp::post())
        .and(warp::header::<String>("x-dakota-signature"))
        .and(warp::body::bytes())
        .and_then(handle_webhook);
    
    warp::serve(webhook_route)
        .run(([127, 0, 0, 1], 3000))
        .await;
}

async fn handle_webhook(
    signature: String,
    body: bytes::Bytes,
) -> Result<impl warp::Reply, warp::Rejection> {
    let payload = String::from_utf8_lossy(&body);
    let secret = env::var("WEBHOOK_SECRET")
        .map_err(|_| warp::reject::custom("Missing webhook secret"))?;
    
    if !verify_webhook_signature(&payload, &signature, &secret) {
        return Ok(warp::reply::with_status(
            "Invalid signature",
            warp::http::StatusCode::UNAUTHORIZED,
        ));
    }
    
    // Process webhook safely
    let webhook_data: serde_json::Value = serde_json::from_str(&payload)
        .map_err(|_| warp::reject::custom("Invalid JSON"))?;
    
    process_webhook(webhook_data).await;
    Ok(warp::reply::with_status("OK", warp::http::StatusCode::OK))
}
Java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.util.Arrays;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class WebhookHandler extends HttpServlet {
    
    private boolean verifyWebhookSignature(String payload, String signature, String secret) {
        try {
            Mac mac = Mac.getInstance("HmacSHA256");
            SecretKeySpec keySpec = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
            mac.init(keySpec);
            
            byte[] expectedBytes = mac.doFinal(payload.getBytes("UTF-8"));
            String expectedSignature = bytesToHex(expectedBytes);
            
            // Use constant-time comparison
            return MessageDigest.isEqual(
                signature.getBytes(),
                expectedSignature.getBytes()
            );
        } catch (Exception e) {
            return false;
        }
    }
    
    private String bytesToHex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte b : bytes) {
            result.append(String.format("%02x", b));
        }
        return result.toString();
    }
    
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) {
        try {
            String signature = request.getHeader("X-Dakota-Signature");
            String payload = request.getReader().lines()
                .collect(java.util.stream.Collectors.joining("\n"));
            
            String secret = System.getenv("WEBHOOK_SECRET");
            
            if (!verifyWebhookSignature(payload, signature, secret)) {
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                response.getWriter().write("Invalid signature");
                return;
            }
            
            // Process webhook safely
            processWebhook(payload);
            response.setStatus(HttpServletResponse.SC_OK);
            response.getWriter().write("OK");
        } catch (Exception e) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
        }
    }
}

Webhook Endpoint Security

  • Use HTTPS for all webhook endpoints
  • Implement request size limits
  • Add rate limiting to webhook endpoints
  • Validate webhook payload structure

Monitoring and Alerting

Security Monitoring

Set up monitoring for:
  • Unusual API usage patterns
  • Failed authentication attempts
  • Requests from unexpected IP addresses
  • High error rates that might indicate attacks

Logging Security Events

Log security-relevant events:
cURL
#!/bin/bash
# Security event logging in shell scripts
log_security_event() {
    local event="$1"
    local endpoint="$2"
    local ip_address="$3"
    
    echo "$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ) | SECURITY | $event | endpoint=$endpoint ip=$ip_address source=dakota-shell-client" >> /var/log/dakota-security.log
}

# Example usage
log_security_event "api_key_used" "/customers" "$CLIENT_IP"
JavaScript
function logSecurityEvent(event, details) {
  console.log(JSON.stringify({
    timestamp: new Date().toISOString(),
    event: event,
    details: details,
    source: 'dakota-api-client'
  }));
}

// Example usage
logSecurityEvent('api_key_used', {
  endpoint: '/customers',
  ip_address: req.ip,
  user_agent: req.get('User-Agent')
});
Python
import json
import logging
from datetime import datetime, timezone

def log_security_event(event: str, details: dict):
    security_log = {
        'timestamp': datetime.now(timezone.utc).isoformat(),
        'event': event,
        'details': details,
        'source': 'dakota-api-client'
    }
    
    # Use structured logging
    logging.info(json.dumps(security_log))

# Example usage
log_security_event('api_key_used', {
    'endpoint': '/customers',
    'ip_address': request.remote_addr,
    'user_agent': request.headers.get('User-Agent')
})
Go
package main

import (
    "encoding/json"
    "log"
    "net/http"
    "time"
)

type SecurityEvent struct {
    Timestamp string                 `json:"timestamp"`
    Event     string                 `json:"event"`
    Details   map[string]interface{} `json:"details"`
    Source    string                 `json:"source"`
}

func logSecurityEvent(event string, details map[string]interface{}) {
    securityEvent := SecurityEvent{
        Timestamp: time.Now().UTC().Format(time.RFC3339),
        Event:     event,
        Details:   details,
        Source:    "dakota-api-client",
    }
    
    eventJSON, err := json.Marshal(securityEvent)
    if err != nil {
        log.Printf("Error marshaling security event: %v", err)
        return
    }
    
    log.Println(string(eventJSON))
}

// Example usage
func handleRequest(w http.ResponseWriter, r *http.Request) {
    details := map[string]interface{}{
        "endpoint":   r.URL.Path,
        "ip_address": r.RemoteAddr,
        "user_agent": r.UserAgent(),
    }
    
    logSecurityEvent("api_key_used", details)
}
Rust
use serde_json::{json, Value};
use chrono::{DateTime, Utc};
use log::info;

fn log_security_event(event: &str, details: Value) {
    let security_event = json!({
        "timestamp": Utc::now().to_rfc3339(),
        "event": event,
        "details": details,
        "source": "dakota-api-client"
    });
    
    info!("{}", security_event.to_string());
}

// Example usage
fn handle_request(req: &HttpRequest) {
    let details = json!({
        "endpoint": req.uri().path(),
        "ip_address": req.connection_info().remote_addr(),
        "user_agent": req.headers().get("user-agent")
            .and_then(|h| h.to_str().ok())
            .unwrap_or("unknown")
    });
    
    log_security_event("api_key_used", details);
}
Java
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.time.Instant;
import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest;

public class SecurityLogger {
    private static final Logger logger = Logger.getLogger(SecurityLogger.class.getName());
    private static final ObjectMapper mapper = new ObjectMapper();
    
    public static void logSecurityEvent(String event, ObjectNode details) {
        try {
            ObjectNode securityEvent = mapper.createObjectNode();
            securityEvent.put("timestamp", Instant.now().toString());
            securityEvent.put("event", event);
            securityEvent.set("details", details);
            securityEvent.put("source", "dakota-api-client");
            
            logger.info(securityEvent.toString());
        } catch (Exception e) {
            logger.severe("Error logging security event: " + e.getMessage());
        }
    }
    
    // Example usage
    public static void handleRequest(HttpServletRequest request) {
        ObjectNode details = mapper.createObjectNode();
        details.put("endpoint", request.getRequestURI());
        details.put("ip_address", request.getRemoteAddr());
        details.put("user_agent", request.getHeader("User-Agent"));
        
        logSecurityEvent("api_key_used", details);
    }
}

Alert Setup

Configure alerts for:
  • Multiple consecutive API authentication failures
  • Requests from new or suspicious IP addresses
  • Unusual request volume patterns
  • Webhook signature validation failures

Data Protection

Sensitive Data Handling

  • Never log sensitive customer data
  • Use data encryption at rest for stored API responses
  • Implement data retention policies

Security Checklist

Before going to production, verify:
  • API keys stored as environment variables
  • No sensitive data in code or logs
  • HTTPS used for all requests
  • SSL certificate validation enabled
  • Webhook signatures verified
  • Error handling doesn’t expose sensitive info
  • Request/response logging excludes sensitive headers
  • Monitoring and alerting configured
  • Data retention policies implemented
  • Security testing completed

Incident Response

If you suspect a security incident:
  1. Immediately rotate your API keys
  2. Review logs for suspicious activity
  3. Contact Dakota Platform support with incident details
  4. Document the incident and response
  5. Update security measures to prevent recurrence