<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem:  Unizo</title>
    <description>The latest articles on Forem by  Unizo (@unizo_ai).</description>
    <link>https://forem.com/unizo_ai</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3620611%2F00bc1799-309b-4dcd-807d-478815333e8b.png</url>
      <title>Forem:  Unizo</title>
      <link>https://forem.com/unizo_ai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/unizo_ai"/>
    <language>en</language>
    <item>
      <title>What I Learned Building a Multi-Tenant Integration Layer (The Hard Way)</title>
      <dc:creator> Unizo</dc:creator>
      <pubDate>Tue, 20 Jan 2026 06:55:41 +0000</pubDate>
      <link>https://forem.com/unizo_ai/what-i-learned-building-a-multi-tenant-integration-layer-the-hard-way-350d</link>
      <guid>https://forem.com/unizo_ai/what-i-learned-building-a-multi-tenant-integration-layer-the-hard-way-350d</guid>
      <description>&lt;p&gt;You’ve been asked to build integrations for your platform. Seems straightforward: call some APIs, normalize the data, display it in the UI. A few weeks of work, tops. &lt;/p&gt;

&lt;p&gt;Except it’s not a few weeks. And it’s not straightforward. &lt;/p&gt;

&lt;p&gt;I’ve spent few years building integration infrastructure for security platforms. Here’s everything I wish someone had told me before I started. &lt;/p&gt;

&lt;p&gt;The Gap Between POC and Production &lt;/p&gt;

&lt;p&gt;A proof-of-concept integration is easy. Read the docs, make some calls, parse the response. Done in a day. &lt;/p&gt;

&lt;p&gt;Production is a different beast. Here’s the actual checklist: &lt;/p&gt;

&lt;p&gt;Authentication Hell &lt;/p&gt;

&lt;p&gt;Every vendor does auth differently: &lt;/p&gt;

&lt;h1&gt;
  
  
  Vendor A: OAuth 2.0 with refresh
&lt;/h1&gt;

&lt;p&gt;headers = {"Authorization": f"Bearer {access_token}"} &lt;/p&gt;

&lt;h1&gt;
  
  
  Vendor B: API key in header
&lt;/h1&gt;

&lt;p&gt;headers = {"X-API-Key": api_key} &lt;/p&gt;

&lt;h1&gt;
  
  
  Vendor C: API key as query param (yes, really)
&lt;/h1&gt;

&lt;p&gt;url = f"{base_url}/endpoint?api_key={api_key}" &lt;/p&gt;

&lt;h1&gt;
  
  
  Vendor D: Custom signature with timestamp
&lt;/h1&gt;

&lt;p&gt;signature = hmac.new(secret, f"{timestamp}{method}{path}".encode(), 'sha256') &lt;br&gt;
headers = {"X-Signature": signature.hexdigest(), "X-Timestamp": timestamp} &lt;/p&gt;

&lt;p&gt;And you need to handle token refresh without interrupting syncs. Plus store credentials securely for hundreds of customer connections. Plus handle IP allowlisting for vendors that require it. &lt;/p&gt;

&lt;p&gt;Rate Limiting is Harder Than You Think &lt;/p&gt;

&lt;p&gt;Every API has rate limits. The fun part is they’re all different: &lt;/p&gt;

&lt;h1&gt;
  
  
  The nice vendor: returns 429 with retry-after
&lt;/h1&gt;

&lt;p&gt;if response.status_code == 429: &lt;br&gt;
    retry_after = int(response.headers.get('Retry-After', 60)) &lt;br&gt;
    time.sleep(retry_after) &lt;/p&gt;

&lt;h1&gt;
  
  
  The less nice vendor: just returns 500 when you hit the limit
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Good luck figuring out why
&lt;/h1&gt;

&lt;h1&gt;
  
  
  The enterprise vendor: different limits per endpoint
&lt;/h1&gt;

&lt;h1&gt;
  
  
  /users: 100 req/min
&lt;/h1&gt;

&lt;h1&gt;
  
  
  /alerts: 10 req/min
&lt;/h1&gt;

&lt;h1&gt;
  
  
  /export: 1 req/hour
&lt;/h1&gt;

&lt;p&gt;When you’re pulling data for hundreds of tenants, rate limits become a constant constraint. You need: - Per-tenant rate limit tracking - Intelligent request queuing - Exponential backoff with jitter - Circuit breakers so one failing integration doesn’t cascade &lt;/p&gt;

&lt;p&gt;Pagination Nightmares &lt;/p&gt;

&lt;h1&gt;
  
  
  Offset pagination (simple but inefficient)
&lt;/h1&gt;

&lt;p&gt;for offset in range(0, total, page_size): &lt;br&gt;
    response = client.get(f"/items?offset={offset}&amp;amp;limit={page_size}") &lt;/p&gt;

&lt;h1&gt;
  
  
  Cursor pagination (better, but cursors expire)
&lt;/h1&gt;

&lt;p&gt;cursor = None &lt;br&gt;
while True: &lt;br&gt;
    response = client.get(f"/items?cursor={cursor}&amp;amp;limit={page_size}") &lt;br&gt;
    cursor = response.json().get('next_cursor') &lt;br&gt;
    if not cursor: &lt;br&gt;
        break &lt;/p&gt;

&lt;h1&gt;
  
  
  Link header pagination (RFC 5988)
&lt;/h1&gt;

&lt;p&gt;while url: &lt;br&gt;
    response = client.get(url) &lt;br&gt;
    url = response.links.get('next', {}).get('url') &lt;/p&gt;

&lt;h1&gt;
  
  
  The vendor that changes pagination between API versions
&lt;/h1&gt;

&lt;h1&gt;
  
  
  and doesn't document it
&lt;/h1&gt;

&lt;p&gt;The Normalization Problem &lt;/p&gt;

&lt;p&gt;This is where it gets really fun. Here’s the same concept. a security alert, across three vendors: &lt;/p&gt;

&lt;p&gt;// Illustrative shapes (actual field names vary by vendor and API version) &lt;/p&gt;

&lt;p&gt;// CrowdStrike: calls it a "detection" (via /detects/ endpoints) &lt;br&gt;
{ &lt;br&gt;
  "detection_id": "ldt:abc123...", &lt;br&gt;
  "max_severity": 4, &lt;br&gt;
  "created_timestamp": "2024-01-15T10:30:00Z", &lt;br&gt;
  "device": { "hostname": "..." } &lt;br&gt;
} &lt;/p&gt;

&lt;p&gt;// SentinelOne: calls it a "threat" (via /threats endpoint) &lt;br&gt;
{ &lt;br&gt;
  "id": "123456789", &lt;br&gt;
  "threatInfo": { &lt;br&gt;
    "classification": "Malware", &lt;br&gt;
    "confidenceLevel": "high" &lt;br&gt;
  }, &lt;br&gt;
  "agentRealtimeInfo": { "agentComputerName": "..." }, &lt;br&gt;
  "createdAt": "2024-01-15T10:30:00.000Z" &lt;br&gt;
} &lt;/p&gt;

&lt;p&gt;// Microsoft Defender: calls it an "alert" (incidents are collections of alerts) &lt;br&gt;
{ &lt;br&gt;
  "alertId": "da637292082891366787_1234567890", &lt;br&gt;
  "severity": "high", &lt;br&gt;
  "createdDateTime": "2024-01-15T10:30:00.0000000Z", &lt;br&gt;
  "evidence": [{ "deviceDnsName": "..." }] &lt;br&gt;
} &lt;/p&gt;

&lt;p&gt;Three different structures for the same concept. Different field names, different severity formats (number vs string), different timestamp formats, different nesting structures. &lt;/p&gt;

&lt;p&gt;You need to map all of these to a single normalized schema: &lt;/p&gt;

&lt;h1&gt;
  
  
  Your normalized alert schema
&lt;/h1&gt;

&lt;p&gt;@dataclass &lt;br&gt;
class NormalizedAlert: &lt;br&gt;
    id: str &lt;br&gt;
    severity: str  # "critical", "high", "medium", "low" &lt;br&gt;
    timestamp: datetime &lt;br&gt;
    hostname: str &lt;br&gt;
    source: str &lt;br&gt;
    raw_data: dict &lt;/p&gt;

&lt;p&gt;Multiply this by 40 vendors across 8 security categories. That’s a lot of mapping logic. &lt;/p&gt;

&lt;p&gt;The Multi-Tenant Complexity &lt;/p&gt;

&lt;p&gt;All of the above gets exponentially harder with multiple tenants. &lt;/p&gt;

&lt;p&gt;Tenant Isolation &lt;/p&gt;

&lt;p&gt;This is the one that keeps me up at night: &lt;/p&gt;

&lt;h1&gt;
  
  
  WRONG: Shared cache without tenant scoping
&lt;/h1&gt;

&lt;p&gt;cache.set("crowdstrike_detections", detections) &lt;/p&gt;

&lt;h1&gt;
  
  
  RIGHT: Tenant-scoped everything
&lt;/h1&gt;

&lt;p&gt;cache.set(f"tenant:{tenant_id}:crowdstrike:detections", detections) &lt;/p&gt;

&lt;h1&gt;
  
  
  WRONG: Logging raw data
&lt;/h1&gt;

&lt;p&gt;logger.error(f"API failed: {response.json()}") &lt;/p&gt;

&lt;h1&gt;
  
  
  RIGHT: Scrubbed logging
&lt;/h1&gt;

&lt;p&gt;logger.error(f"API failed for tenant {tenant_id}: {response.status_code}") &lt;/p&gt;

&lt;p&gt;Shared rate limit pools, shared caches, shared logs; any of these can leak data between tenants if you’re not careful. &lt;/p&gt;

&lt;p&gt;Credential Storage &lt;/p&gt;

&lt;p&gt;You’re storing API credentials for hundreds of connections. This is a high-value target: &lt;/p&gt;

&lt;h1&gt;
  
  
  Minimum requirements:
&lt;/h1&gt;

&lt;h1&gt;
  
  
  - Encrypted at rest (AES-256 or better)
&lt;/h1&gt;

&lt;h1&gt;
  
  
  - Encrypted in transit (TLS 1.2+)
&lt;/h1&gt;

&lt;h1&gt;
  
  
  - Access controls (which service can access which creds)
&lt;/h1&gt;

&lt;h1&gt;
  
  
  - Audit logging (who accessed what, when)
&lt;/h1&gt;

&lt;h1&gt;
  
  
  - Key rotation support
&lt;/h1&gt;

&lt;h1&gt;
  
  
  - HSM/KMS integration for key management
&lt;/h1&gt;

&lt;p&gt;If you’re building a security product (like a GRC platform), your credential storage needs to pass auditor scrutiny. &lt;/p&gt;

&lt;p&gt;Scaling &lt;/p&gt;

&lt;p&gt;One customer with one integration is manageable. A hundred customers with ten integrations each is a thousand concurrent connections: &lt;/p&gt;

&lt;p&gt;Tenants: 100 &lt;br&gt;
Integrations per tenant: 10 &lt;br&gt;
Sync frequency: every 15 minutes &lt;br&gt;
API calls per sync: ~50 &lt;/p&gt;

&lt;p&gt;= 1,000 integrations &lt;br&gt;
= 4,000 sync jobs per hour &lt;br&gt;
= 200,000 API calls per hour &lt;/p&gt;

&lt;p&gt;Your architecture needs to handle this without falling over. Queue-based processing, worker pools, connection pooling, database optimization. It just adds up. &lt;/p&gt;

&lt;p&gt;The Maintenance Burden &lt;/p&gt;

&lt;p&gt;Here’s the part nobody warns you about: building is maybe 30% of the work. Maintenance is 70%. &lt;/p&gt;

&lt;p&gt;API Versioning &lt;/p&gt;

&lt;h1&gt;
  
  
  Your code, working fine
&lt;/h1&gt;

&lt;p&gt;response = client.get("/v1/detections") &lt;/p&gt;

&lt;h1&gt;
  
  
  Vendor announcement: "v1 deprecated, migrate to v2 by March"
&lt;/h1&gt;

&lt;h1&gt;
  
  
  v2 changes:
&lt;/h1&gt;

&lt;h1&gt;
  
  
  - Different auth flow
&lt;/h1&gt;

&lt;h1&gt;
  
  
  - Different pagination
&lt;/h1&gt;

&lt;h1&gt;
  
  
  - Different response schema
&lt;/h1&gt;

&lt;h1&gt;
  
  
  - Some fields renamed
&lt;/h1&gt;

&lt;h1&gt;
  
  
  - Some fields removed
&lt;/h1&gt;

&lt;h1&gt;
  
  
  - New required parameters
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Your weekend: gone
&lt;/h1&gt;

&lt;p&gt;Multiply by 40 integrations. You’re dealing with API changes constantly. &lt;/p&gt;

&lt;p&gt;Silent Breaking Changes &lt;/p&gt;

&lt;p&gt;The worst kind: &lt;/p&gt;

&lt;h1&gt;
  
  
  What your code expects
&lt;/h1&gt;

&lt;p&gt;device = detection.get("device", {}) &lt;br&gt;
hostname = device.get("hostname")  # Returns: "workstation-1" &lt;/p&gt;

&lt;h1&gt;
  
  
  What the API started returning (no announcement)
&lt;/h1&gt;

&lt;p&gt;hostname = device.get("hostname")  # Returns: ["workstation-1"] &lt;/p&gt;

&lt;h1&gt;
  
  
  Your normalization: quietly broken
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Customer data: silently wrong
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Time to discover: days or weeks
&lt;/h1&gt;

&lt;p&gt;The Real Cost &lt;/p&gt;

&lt;p&gt;I’ve seen teams underestimate this consistently: &lt;/p&gt;

&lt;p&gt;Initial build: - 20 integrations × 2-3 weeks each = 40-60 weeks of engineering - Plus common infrastructure (auth, rate limiting, queuing) = 8-12 weeks - Plus testing, deployment, monitoring = 4-8 weeks - Total: 12-18 months for a small team &lt;/p&gt;

&lt;p&gt;Ongoing maintenance: - 2+ FTEs just to keep integrations running - Every API change = regression testing across affected tenants - Every new integration request = another month of work &lt;/p&gt;

&lt;p&gt;Opportunity cost: - Every hour on integrations = hour not spent on your actual product - I’ve seen teams lose 40-50% of engineering capacity to integration work &lt;/p&gt;

&lt;p&gt;The Alternative &lt;/p&gt;

&lt;p&gt;At some point, you have to ask: is building integration infrastructure actually your core competency? &lt;/p&gt;

&lt;p&gt;If you’re building a GRC platform, your value is in compliance logic, risk analysis, and control mapping; not in parsing CrowdStrike’s pagination quirks. &lt;/p&gt;

&lt;p&gt;The “buy” option today isn’t just Zapier-style workflow tools. There are now category-level unified APIs that handle all the complexity above. Here’s what using one looks like: &lt;/p&gt;

&lt;p&gt;// Using Unizo's SDK (from &lt;a href="https://docs.unizo.ai/docs/sdks/overview/" rel="noopener noreferrer"&gt;docs.unizo.ai/docs/sdks/overview&lt;/a&gt;) &lt;br&gt;
// npm install &lt;a class="mentioned-user" href="https://dev.to/unizo"&gt;@unizo&lt;/a&gt;/sdk &lt;/p&gt;

&lt;p&gt;import { Unizo } from '&lt;a class="mentioned-user" href="https://dev.to/unizo"&gt;@unizo&lt;/a&gt;/sdk'; &lt;/p&gt;

&lt;p&gt;const client = new Unizo({ &lt;br&gt;
  apiKey: process.env.UNIZO_API_KEY &lt;br&gt;
}); &lt;/p&gt;

&lt;p&gt;// One call - normalized vulnerabilities from ALL connected scanners &lt;br&gt;
// (Qualys, Tenable, Snyk, etc. - doesn't matter which your customer uses) &lt;br&gt;
const vulnerabilities = await client.security.vulnerabilities.list({ &lt;br&gt;
  severity: 'high', &lt;br&gt;
  status: 'open' &lt;br&gt;
}); &lt;/p&gt;

&lt;p&gt;// Iterate over normalized results &lt;br&gt;
vulnerabilities.forEach(vuln =&amp;gt; { &lt;br&gt;
  console.log(&lt;code&gt;${vuln.id}: ${vuln.title} (${vuln.severity})&lt;/code&gt;); &lt;br&gt;
}); &lt;/p&gt;

&lt;p&gt;Or if you prefer raw REST: &lt;/p&gt;

&lt;p&gt;// Direct REST call to the same endpoint &lt;br&gt;
const response = await fetch( &lt;br&gt;
  '&lt;a href="https://api.unizo.ai/v1/security/vulnerabilities?severity=high&amp;amp;status=open" rel="noopener noreferrer"&gt;https://api.unizo.ai/v1/security/vulnerabilities?severity=high&amp;amp;status=open&lt;/a&gt;', &lt;br&gt;
  { &lt;br&gt;
    headers: { &lt;br&gt;
      'Authorization': &lt;code&gt;Bearer ${process.env.UNIZO_API_KEY}&lt;/code&gt;, &lt;br&gt;
      'Content-Type': 'application/json' &lt;br&gt;
    } &lt;br&gt;
  } &lt;br&gt;
); &lt;/p&gt;

&lt;p&gt;const vulnerabilities = await response.json(); &lt;/p&gt;

&lt;p&gt;The SDK handles auth, retries, rate limits, and pagination. You get: &lt;/p&gt;

&lt;p&gt;One API call to get normalized data across all EDR/VMS/Identity vendors &lt;/p&gt;

&lt;p&gt;One webhook endpoint for real-time events from all sources &lt;/p&gt;

&lt;p&gt;One auth flow (Connect UI) for your customers to connect any tool &lt;/p&gt;

&lt;p&gt;Vendor API changes handled upstream, not in your codebase &lt;/p&gt;

&lt;p&gt;The Decision Framework &lt;/p&gt;

&lt;p&gt;Before you decide to build: &lt;/p&gt;

&lt;p&gt;Count your integrations: How many do you need now? In a year? &lt;/p&gt;

&lt;p&gt;Calculate the cost: Fully-loaded engineer cost × months of work &lt;/p&gt;

&lt;p&gt;Factor in maintenance: 2+ FTEs ongoing, forever &lt;/p&gt;

&lt;p&gt;Consider opportunity cost: What else could those engineers build? &lt;/p&gt;

&lt;p&gt;Then compare to embedding existing infrastructure. The math usually favors buying unless integrations are literally your core product. &lt;/p&gt;

&lt;p&gt;TL;DR &lt;/p&gt;

&lt;p&gt;POC integrations are easy. Production integrations are 10x harder. &lt;/p&gt;

&lt;p&gt;Multi-tenancy adds another 5x complexity. &lt;/p&gt;

&lt;p&gt;Maintenance is 70% of the work, and it never ends. &lt;/p&gt;

&lt;p&gt;The real cost isn’t just engineering time. It’s opportunity cost. &lt;/p&gt;

&lt;p&gt;Unless integrations are your core product, you probably shouldn’t build from scratch. &lt;/p&gt;

</description>
      <category>api</category>
      <category>architecture</category>
      <category>backend</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Why Unified APIs Became the Modern Software Backbone</title>
      <dc:creator> Unizo</dc:creator>
      <pubDate>Tue, 09 Dec 2025 12:16:12 +0000</pubDate>
      <link>https://forem.com/unizo_ai/why-unified-apis-became-the-modern-software-backbone-1ikh</link>
      <guid>https://forem.com/unizo_ai/why-unified-apis-became-the-modern-software-backbone-1ikh</guid>
      <description>&lt;p&gt;Discover why Unified APIs are transforming the modern software ecosystem. This blog explains how Unified APIs simplify integrations, reduce development time, accelerate product launches, and provide scalable connectivity across multiple platforms.&lt;/p&gt;

&lt;p&gt;Learn how Unified APIs help businesses streamline workflows, boost engineering efficiency, and build future-ready applications.&lt;br&gt;
👉 &lt;a href="https://unizo.ai/blog/why-unified-apis-became-modern-software-backbone/" rel="noopener noreferrer"&gt;https://unizo.ai/blog/why-unified-apis-became-modern-software-backbone/&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Rapid Integration: 40+ Security Tools in 3 Weeks</title>
      <dc:creator> Unizo</dc:creator>
      <pubDate>Wed, 03 Dec 2025 06:34:21 +0000</pubDate>
      <link>https://forem.com/unizo_ai/rapid-integration-40-security-tools-in-3-weeks-d6g</link>
      <guid>https://forem.com/unizo_ai/rapid-integration-40-security-tools-in-3-weeks-d6g</guid>
      <description>&lt;p&gt;Managing security integrations can be complex, but this case study from Unizo.ai shows how teams can deliver 40+ integrations in just three weeks without compromising on quality or stability. It outlines the workflows, automation techniques, and team coordination needed for fast, reliable deployment — ideal for product managers, security engineers, and SaaS founders.&lt;br&gt;
Link: [&lt;a href="https://unizo.ai/blog/how-security-products-ship-40-integrations-in-3-weeks/" rel="noopener noreferrer"&gt;https://unizo.ai/blog/how-security-products-ship-40-integrations-in-3-weeks/&lt;/a&gt;]&lt;/p&gt;

</description>
    </item>
    <item>
      <title>A Complete API Integration Guide for 2025</title>
      <dc:creator> Unizo</dc:creator>
      <pubDate>Fri, 21 Nov 2025 09:29:34 +0000</pubDate>
      <link>https://forem.com/unizo_ai/a-complete-api-integration-guide-for-2025-4kd2</link>
      <guid>https://forem.com/unizo_ai/a-complete-api-integration-guide-for-2025-4kd2</guid>
      <description>&lt;p&gt;If you're building products that rely on external data, automation, or third-party services, a solid API integration strategy is crucial. This guide from Unizo.ai covers authentication, error handling, rate limits, and real-world integration examples to help developers build reliable and future-ready systems.&lt;br&gt;
Link: &lt;a href="https://unizo.ai/blog/api-integration-guide-2025/" rel="noopener noreferrer"&gt;https://unizo.ai/blog/api-integration-guide-2025/&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
