Skip to content

Integrations

Comprehensive guide to integrating Studio Platform with external services, including third-party APIs, webhooks, and custom integrations.

🔌 Integration Overview

Integration Architecture

Studio Platform provides multiple integration options to connect with external services, enabling seamless workflow automation and data exchange.

graph TD
    A[Studio Platform] --> B[API Gateway]
    B --> C[Webhook System]
    B --> D[External APIs]
    B --> E[Third-Party Services]

    C --> F[Incoming Webhooks]
    C --> G[Outgoing Webhooks]

    D --> H[REST APIs]
    D --> I[GraphQL APIs]
    D --> J[SOAP APIs]

    E --> K[Google Workspace]
    E --> L[Microsoft 365]
    E --> M[Slack]
    E --> N[Jira]
    E --> O[FleetDM]
    E --> P[Prowler]
    E --> Q[n8n]

    R[Custom Integrations] --> B
    S[Plugin System] --> B
    T[API Extensions] --> B

Integration Types

Integration Type Description Use Case Complexity
API Integration Direct API calls Data synchronization Low
Webhook Integration Event-based notifications Real-time updates Medium
OAuth Integration Secure authentication Third-party access Medium
File Integration File exchange Document management Low
Database Integration Direct database access Data migration High
Custom Integration Custom development Specific requirements High

🌐 Third-Party Integrations

Google Workspace Integration

Configuration

Google Workspace Setup:

# google-workspace.yml
integration:
  name: google-workspace
  type: oauth2
  version: 1.0.0

authentication:
  provider: google
  client_id: ${GOOGLE_CLIENT_ID}
  client_secret: ${GOOGLE_CLIENT_SECRET}
  scopes:
    - https://www.googleapis.com/auth/drive
    - https://www.googleapis.com/auth/calendar
    - https://www.googleapis.com/auth/gmail.readonly
    - https://www.googleapis.com/auth/spreadsheets

services:
  drive:
    enabled: true
    folder_id: ${GOOGLE_DRIVE_FOLDER_ID}
    auto_sync: true
    sync_interval: 3600

  calendar:
    enabled: true
    calendar_id: ${GOOGLE_CALENDAR_ID}
    auto_create_events: true

  gmail:
    enabled: true
    label: "Studio Compliance"
    auto_process: true

  sheets:
    enabled: true
    spreadsheet_id: ${GOOGLE_SHEET_ID}
    auto_export: true

API Integration

Google Drive Integration:

// Google Drive API integration
const { google } = require('googleapis');
const drive = google.drive('v3');

class GoogleDriveIntegration {
  constructor(auth) {
    this.auth = auth;
    this.drive = drive;
  }

  async uploadEvidence(evidence) {
    try {
      const fileMetadata = {
        name: evidence.file_name,
        parents: [process.env.GOOGLE_DRIVE_FOLDER_ID]
      };

      const media = {
        mimeType: evidence.content_type,
        body: fs.createReadStream(evidence.file_path)
      };

      const response = await this.drive.files.create({
        resource: fileMetadata,
        media: media,
        fields: 'id, name, webViewLink, webContentLink'
      });

      return {
        success: true,
        data: {
          id: response.data.id,
          name: response.data.name,
          link: response.data.webViewLink,
          download_link: response.data.webContentLink
        }
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }

  async searchFiles(query) {
    try {
      const response = await this.drive.files.list({
        q: query,
        fields: 'files(id, name, webViewLink, createdTime)'
      });

      return response.data.files;
    } catch (error) {
      throw new Error(`Google Drive search failed: ${error.message}`);
    }
  }
}

Google Calendar Integration:

// Google Calendar API integration
const { google } = require('googleapis');
const calendar = google.calendar('v3');

class GoogleCalendarIntegration {
  constructor(auth) {
    this.auth = auth;
    this.calendar = calendar;
  }

  async createEvent(project, milestone) {
    try {
      const event = {
        summary: `${project.name} - ${milestone.name}`,
        description: milestone.description,
        start: {
          dateTime: milestone.due_date.toISOString(),
          timeZone: 'UTC'
        },
        end: {
          dateTime: milestone.due_date.add(1, 'hour').toISOString(),
          timezone: 'UTC'
        },
        attendees: project.team_members.map(member => ({
          email: member.email
        })),
        reminders: {
          useDefault: false,
          overrides: [
            { method: 'email', minutes: 24 * 60 },
            { method: 'popup', minutes: 10 }
          ]
        }
      };

      const response = await this.calendar.events.insert({
        calendarId: process.env.GOOGLE_CALENDAR_ID,
        resource: event
      });

      return {
        success: true,
        data: {
          id: response.data.id,
          link: response.data.htmlLink
        }
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }
}

Microsoft 365 Integration

Configuration

Microsoft 365 Setup:

# microsoft-365.yml
integration:
  name: microsoft-365
  type: oauth2
  version: 1.0.0

authentication:
  provider: microsoft
  client_id: ${MICROSOFT_CLIENT_ID}
  client_secret: ${MICROSOFT_CLIENT_SECRET}
  tenant_id: ${MICROSOFT_TENANT_ID}
  scopes:
    - https://graph.microsoft.com/Files.ReadWrite
    - https://graph.microsoft.com/Calendars.ReadWrite
    - https://graph.microsoft.com/Mail.Read
    - https://graph.microsoft.com/Sites.ReadWrite.All

services:
  onedrive:
    enabled: true
    folder_path: /Studio/Compliance
    auto_sync: true
    sync_interval: 3600

  outlook:
    enabled: true
    folder: "Compliance"
    auto_process: true

  sharepoint:
    enabled: true
    site_id: ${SHAREPOINT_SITE_ID}
    document_library: "Compliance Documents"

API Integration

OneDrive Integration:

// Microsoft OneDrive API integration
const { Client } = require('@microsoft/microsoft-graph-client');

class OneDriveIntegration {
  constructor(authProvider) {
    this.client = Client.initWithMiddleware({ authProvider });
  }

  async uploadEvidence(evidence) {
    try {
      const fileContent = fs.readFileSync(evidence.file_path);

      const response = await this.client
        .api(`/me/drive/root:/Studio/Compliance/${evidence.file_name}:/content`)
        .put(fileContent);

      return {
        success: true,
        data: {
          id: response.id,
          name: response.name,
          link: response.webUrl,
          download_link: response['@microsoft.graph.downloadUrl']
        }
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }

  async searchFiles(query) {
    try {
      const response = await this.client
        .api(`/me/drive/search(q='${query}')`)
        .get();

      return response.value;
    } catch (error) {
      throw new Error(`OneDrive search failed: ${error.message}`);
    }
  }
}

Slack Integration

Configuration

Slack Setup:

# slack.yml
integration:
  name: slack
  type: webhook
  version: 1.0.0

authentication:
  bot_token: ${SLACK_BOT_TOKEN}
  signing_secret: ${SLACK_SIGNING_SECRET}

channels:
  compliance: "#compliance"
  alerts: "#compliance-alerts"
  reports: "#compliance-reports"

webhooks:
  incoming: ${SLACK_WEBHOOK_URL}
  outgoing: ${SLACK_OUTGOING_WEBHOOK_URL}

features:
  notifications: true
  commands: true
  interactive: true

API Integration

Slack Bot Integration:

// Slack API integration
const { WebClient } = require('@slack/web-api');

class SlackIntegration {
  constructor(token) {
    this.client = new WebClient(token);
  }

  async sendNotification(channel, message, attachments = []) {
    try {
      const response = await this.client.chat.postMessage({
        channel: channel,
        text: message,
        attachments: attachments
      });

      return {
        success: true,
        data: {
          ts: response.ts,
          channel: response.channel
        }
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }

  async sendComplianceAlert(project, alert) {
    const attachments = [
      {
        color: alert.severity === 'high' ? 'danger' : 'warning',
        title: `Compliance Alert: ${alert.type}`,
        text: alert.message,
        fields: [
          {
            title: 'Project',
            value: project.name,
            short: true
          },
          {
            title: 'Control',
            value: alert.control_number,
            short: true
          },
          {
            title: 'Severity',
            value: alert.severity,
            short: true
          }
        ],
        actions: [
          {
            type: 'button',
            text: 'View Details',
            url: `${process.env.STUDIO_URL}/projects/${project.id}`
          }
        ]
      }
    ];

    return await this.sendNotification('#compliance-alerts', '', attachments);
  }

  async sendProjectReport(project, report) {
    const attachments = [
      {
        color: 'good',
        title: `Compliance Report: ${project.name}`,
        text: `Overall Score: ${report.score}%`,
        fields: [
          {
            title: 'Framework',
            value: project.framework,
            short: true
          },
          {
            title: 'Status',
            value: project.status,
            short: true
          },
          {
            title: 'Controls Complete',
            value: `${report.controls_complete}/${report.controls_total}`,
            short: true
          },
          {
            title: 'Evidence Count',
            value: report.evidence_count,
            short: true
          }
        ],
        actions: [
          {
            type: 'button',
            text: 'View Report',
            url: report.link
          }
        ]
      }
    ];

    return await this.sendNotification('#compliance-reports', '', attachments);
  }
}

Jira Integration

Configuration

Jira Setup:

# jira.yml
integration:
  name: jira
  type: rest
  version: 1.0.0

authentication:
  type: basic
  username: ${JIRA_USERNAME}
  api_token: ${JIRA_API_TOKEN}
  base_url: ${JIRA_BASE_URL}

project:
  key: STUDIO
  name: Studio Compliance
  issue_types:
    - task
    - bug
    - improvement

mappings:
  evidence_review:
    issue_type: task
    priority: medium
    assignee: compliance_team

  compliance_gap:
    issue_type: task
    priority: high
    assignee: security_team

  risk_assessment:
    issue_type: task
    priority: high
    assignee: risk_team

API Integration

Jira API Integration:

// Jira API integration
const axios = require('axios');

class JiraIntegration {
  constructor(config) {
    this.baseURL = config.base_url;
    this.auth = {
      username: config.username,
      api_token: config.api_token
    };
  }

  async createIssue(type, summary, description, assignee = null) {
    try {
      const issueData = {
        fields: {
          project: {
            key: 'STUDIO'
          },
          summary: summary,
          description: description,
          issuetype: {
            name: type
          }
        }
      };

      if (assignee) {
        issueData.fields.assignee = {
          name: assignee
        };
      }

      const response = await axios.post(
        `${this.baseURL}/rest/api/2/issue`,
        issueData,
        {
          auth: {
            username: this.auth.username,
            password: this.auth.api_token
          },
          headers: {
            'Content-Type': 'application/json'
          }
        }
      );

      return {
        success: true,
        data: {
          id: response.data.id,
          key: response.data.key,
          url: `${this.baseURL}/browse/${response.data.key}`
        }
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }

  async createEvidenceReviewIssue(evidence) {
    const summary = `Evidence Review: ${evidence.title}`;
    const description = `
      Evidence ID: ${evidence.id}
      Project: ${evidence.project_name}
      Control: ${evidence.control_number}
      Uploaded by: ${evidence.uploader_name}

      Please review the evidence for compliance requirements.

      Evidence Link: ${evidence.link}
    `;

    return await this.createIssue('Task', summary, description, 'compliance_team');
  }

  async createComplianceGapIssue(gap) {
    const summary = `Compliance Gap: ${gap.control_number}`;
    const description = `
      Gap ID: ${gap.id}
      Project: ${gap.project_name}
      Control: ${gap.control_number}
      Severity: ${gap.severity}

      Description: ${gap.description}
      Recommendations: ${gap.recommendations}

      Due Date: ${gap.due_date}
    `;

    return await this.createIssue('Task', summary, description, 'security_team');
  }
}

🔗 Webhooks

Webhook Configuration

Incoming Webhooks

Webhook Setup:

# webhooks.yml
webhooks:
  incoming:
    slack:
      url: /webhooks/slack
      secret: ${SLACK_WEBHOOK_SECRET}
      events:
        - message
        - reaction_added
        - file_share

    jira:
      url: /webhooks/jira
      secret: ${JIRA_WEBHOOK_SECRET}
      events:
        - issue_created
        - issue_updated
        - comment_created

    github:
      url: /webhooks/github
      secret: ${GITHUB_WEBHOOK_SECRET}
      events:
        - push
        - pull_request
        - issue_comment

Webhook Handler

Webhook Handler Implementation:

// Webhook handler
const crypto = require('crypto');
const express = require('express');

class WebhookHandler {
  constructor() {
    this.handlers = new Map();
    this.setupHandlers();
  }

  setupHandlers() {
    // Slack webhook handler
    this.handlers.set('slack', async (req, res) => {
      const signature = req.headers['x-slack-signature'];
      const timestamp = req.headers['x-slack-request-timestamp'];
      const body = JSON.stringify(req.body);

      const expectedSignature = 'v0=' + crypto
        .createHmac('sha256', process.env.SLACK_WEBHOOK_SECRET)
        .update(`v0:${timestamp}:${body}`)
        .digest('hex');

      if (signature !== expectedSignature) {
        return res.status(401).json({ error: 'Invalid signature' });
      }

      // Handle Slack event
      const { type, event } = req.body;

      switch (type) {
        case 'url_verification':
          return res.json({ challenge: req.body.challenge });

        case 'event_callback':
          await this.handleSlackEvent(event);
          return res.json({ status: 'ok' });

        default:
          return res.json({ status: 'ok' });
      }
    });

    // Jira webhook handler
    this.handlers.set('jira', async (req, res) => {
      const { webhookEvent } = req.body;

      switch (webhookEvent) {
        case 'jira:issue_created':
          await this.handleJiraIssueCreated(req.body.issue);
          break;

        case 'jira:issue_updated':
          await this.handleJiraIssueUpdated(req.body.issue);
          break;

        default:
          break;
      }

      return res.json({ status: 'ok' });
    });
  }

  async handleSlackEvent(event) {
    switch (event.type) {
      case 'message':
        await this.handleSlackMessage(event);
        break;

      case 'file_share':
        await this.handleSlackFileShare(event);
        break;

      default:
        break;
    }
  }

  async handleSlackMessage(event) {
    // Handle Slack message
    const { text, user, channel, ts } = event;

    // Process message and respond if needed
    if (text.includes('compliance score')) {
      // Get compliance score and send response
      const score = await this.getComplianceScore();
      await this.sendSlackResponse(channel, `Current compliance score: ${score}%`, ts);
    }
  }

  async handleJiraIssueCreated(issue) {
    // Handle Jira issue creation
    const { key, fields } = issue;

    // Create notification in Studio Platform
    await this.createNotification({
      type: 'jira_issue_created',
      title: `Jira Issue Created: ${key}`,
      description: fields.summary,
      link: `${process.env.JIRA_BASE_URL}/browse/${key}`
    });
  }
}

// Express webhook routes
const app = express();
const webhookHandler = new WebhookHandler();

app.use(express.json());

// Slack webhook
app.post('/webhooks/slack', webhookHandler.handlers.get('slack'));

// Jira webhook
app.post('/webhooks/jira', webhookHandler.handlers.get('jira'));

Outgoing Webhooks

Webhook Events

Event Configuration:

# webhook-events.yml
events:
  evidence_uploaded:
    name: Evidence Uploaded
    description: Triggered when evidence is uploaded
    payload:
      evidence_id: string
      project_id: string
      control_id: string
      uploader_id: string
      file_name: string
      uploaded_at: datetime

  compliance_score_updated:
    name: Compliance Score Updated
    description: Triggered when compliance score changes
    payload:
      project_id: string
      old_score: integer
      new_score: integer
      framework: string
      updated_at: datetime

  project_created:
    name: Project Created
    description: Triggered when a new project is created
    payload:
      project_id: string
      project_name: string
      framework: string
      created_by: string
      created_at: datetime

Webhook Sender

Webhook Implementation:

// Webhook sender
const axios = require('axios');

class WebhookSender {
  constructor() {
    this.webhooks = new Map();
    this.loadWebhooks();
  }

  async loadWebhooks() {
    // Load webhooks from database
    const webhooks = await WebhookModel.findAll({ where: { active: true } });

    webhooks.forEach(webhook => {
      this.webhooks.set(webhook.id, webhook);
    });
  }

  async sendEvent(eventType, payload) {
    const relevantWebhooks = Array.from(this.webhooks.values())
      .filter(webhook => webhook.events.includes(eventType));

    for (const webhook of relevantWebhooks) {
      try {
        await this.sendWebhook(webhook, eventType, payload);
      } catch (error) {
        console.error(`Failed to send webhook ${webhook.id}:`, error);
      }
    }
  }

  async sendWebhook(webhook, eventType, payload) {
    const data = {
      event: eventType,
      timestamp: new Date().toISOString(),
      payload: payload
    };

    const signature = this.generateSignature(data, webhook.secret);

    const response = await axios.post(webhook.url, data, {
      headers: {
        'Content-Type': 'application/json',
        'X-Studio-Signature': signature,
        'X-Studio-Event': eventType
      },
      timeout: 10000
    });

    // Log webhook delivery
    await this.logWebhookDelivery(webhook.id, eventType, response.status);
  }

  generateSignature(data, secret) {
    return crypto
      .createHmac('sha256', secret)
      .update(JSON.stringify(data))
      .digest('hex');
  }

  async logWebhookDelivery(webhookId, eventType, status) {
    await WebhookDeliveryLog.create({
      webhook_id: webhookId,
      event_type: eventType,
      status: status,
      delivered_at: new Date()
    });
  }
}

// Usage in application
const webhookSender = new WebhookSender();

// Send webhook when evidence is uploaded
app.post('/api/v1/evidence', async (req, res) => {
  const evidence = await Evidence.create(req.body);

  // Send webhook
  await webhookSender.sendEvent('evidence_uploaded', {
    evidence_id: evidence.id,
    project_id: evidence.project_id,
    control_id: evidence.control_id,
    uploader_id: evidence.uploaded_by,
    file_name: evidence.file_name,
    uploaded_at: evidence.uploaded_at
  });

  res.json({ success: true, data: evidence });
});

🔧 Custom Integrations

Plugin System

Plugin Architecture

Plugin Interface:

// Plugin interface
class PluginInterface {
  constructor(config) {
    this.config = config;
    this.name = config.name;
    this.version = config.version;
  }

  async initialize() {
    // Initialize plugin
    throw new Error('initialize() must be implemented');
  }

  async execute(data) {
    // Execute plugin logic
    throw new Error('execute() must be implemented');
  }

  async cleanup() {
    // Cleanup resources
    throw new Error('cleanup() must be implemented');
  }
}

// Example custom plugin
class CustomEvidenceProcessor extends PluginInterface {
  constructor(config) {
    super(config);
    this.apiEndpoint = config.api_endpoint;
    this.apiKey = config.api_key;
  }

  async initialize() {
    // Initialize plugin
    console.log(`Initializing ${this.name} plugin`);

    // Validate configuration
    if (!this.apiEndpoint || !this.apiKey) {
      throw new Error('API endpoint and key are required');
    }
  }

  async execute(evidence) {
    // Process evidence with external API
    try {
      const response = await axios.post(
        this.apiEndpoint,
        {
          file: evidence.file_path,
          metadata: evidence.metadata
        },
        {
          headers: {
            'Authorization': `Bearer ${this.apiKey}`,
            'Content-Type': 'application/json'
          }
        }
      );

      return {
        success: true,
        data: response.data
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }

  async cleanup() {
    // Cleanup resources
    console.log(`Cleaning up ${this.name} plugin`);
  }
}

Plugin Manager

Plugin Manager Implementation:

// Plugin manager
class PluginManager {
  constructor() {
    this.plugins = new Map();
    this.pluginConfigs = new Map();
  }

  async loadPlugin(pluginName, config) {
    try {
      // Load plugin class
      const PluginClass = require(`./plugins/${pluginName}`);

      // Create plugin instance
      const plugin = new PluginClass(config);

      // Initialize plugin
      await plugin.initialize();

      // Store plugin
      this.plugins.set(pluginName, plugin);
      this.pluginConfigs.set(pluginName, config);

      console.log(`Plugin ${pluginName} loaded successfully`);
    } catch (error) {
      console.error(`Failed to load plugin ${pluginName}:`, error);
      throw error;
    }
  }

  async executePlugin(pluginName, data) {
    const plugin = this.plugins.get(pluginName);

    if (!plugin) {
      throw new Error(`Plugin ${pluginName} not found`);
    }

    return await plugin.execute(data);
  }

  async unloadPlugin(pluginName) {
    const plugin = this.plugins.get(pluginName);

    if (plugin) {
      await plugin.cleanup();
      this.plugins.delete(pluginName);
      this.pluginConfigs.delete(pluginName);

      console.log(`Plugin ${pluginName} unloaded successfully`);
    }
  }

  getLoadedPlugins() {
    return Array.from(this.plugins.keys());
  }

  getPluginConfig(pluginName) {
    return this.pluginConfigs.get(pluginName);
  }
}

// Usage
const pluginManager = new PluginManager();

// Load custom plugin
await pluginManager.loadPlugin('custom-processor', {
  name: 'Custom Evidence Processor',
  version: '1.0.0',
  api_endpoint: 'https://api.example.com/process',
  api_key: process.env.CUSTOM_API_KEY
});

// Execute plugin
const result = await pluginManager.executePlugin('custom-processor', evidence);

✅ Integration Best Practices

Development Best Practices

API Integration

  • Authentication - Use secure authentication methods
  • Error Handling - Handle errors gracefully
  • Rate Limiting - Respect rate limits
  • Retry Logic - Implement retry logic with exponential backoff
  • Logging - Log all integration activities

Webhook Integration

  • Security - Use webhook signatures
  • Idempotency - Make webhook handlers idempotent
  • Error Handling - Handle webhook failures gracefully
  • Retry Logic - Implement retry logic for failed webhooks
  • Monitoring - Monitor webhook delivery

Common Integration Mistakes

Avoid These Mistakes: - Not handling authentication properly - Not implementing error handling - Not respecting rate limits - Not implementing retry logic - Not monitoring integration health

Follow These Best Practices: - Use secure authentication methods - Handle errors gracefully and appropriately - Respect rate limits and implement backoff - Implement retry logic with exponential backoff - Monitor integration health and performance


!!! tip Start Simple Begin with simple integrations and gradually add complexity as needed. Use existing libraries and frameworks when possible.

!!! note Security First Always prioritize security in integrations. Use secure authentication methods and validate all incoming data.

!!! question Need Help? Check our Integration Support for integration assistance, or join our developer community.