<?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>DEV Community: Makini</title>
    <description>The latest articles on DEV Community by Makini (@makiniintegration).</description>
    <link>https://dev.to/makiniintegration</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%2F3643850%2F5dce7289-1662-4d39-b663-3ff2413c1c85.jpg</url>
      <title>DEV Community: Makini</title>
      <link>https://dev.to/makiniintegration</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/makiniintegration"/>
    <language>en</language>
    <item>
      <title>Connecting TechnologyOne to Your App via Makini API</title>
      <dc:creator>Makini</dc:creator>
      <pubDate>Fri, 10 Apr 2026 05:42:35 +0000</pubDate>
      <link>https://dev.to/makiniintegration/connecting-technologyone-to-your-app-via-makini-api-3584</link>
      <guid>https://dev.to/makiniintegration/connecting-technologyone-to-your-app-via-makini-api-3584</guid>
      <description>&lt;p&gt;TechnologyOne is widely used across government, education, and regulated industries for finance, asset management, HR, and operations. If you're building industrial software and your customers run TechnologyOne, sooner or later you'll need to pull data out of it — work orders, assets, purchase orders, maintenance records, and more.&lt;/p&gt;

&lt;p&gt;The traditional approach means negotiating API access with the customer's IT team, reading through dense vendor documentation, and writing a custom connector that you'll have to maintain forever. We built Makini so you don't have to do any of that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you get with the Makini × TechnologyOne integration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Through a single unified API, you get access to the full range of TechnologyOne data objects your product is likely to need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Assets&lt;/strong&gt; — equipment records and site hierarchy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Work Orders&lt;/strong&gt; — task execution, assignments, deadlines&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preventive Maintenance&lt;/strong&gt; — time-based and counter-based templates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asset Downtime&lt;/strong&gt; — planned and unplanned outage records&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Materials &amp;amp; Stock&lt;/strong&gt; — spare parts, consumables, storeroom quantities&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Purchase Orders&lt;/strong&gt; — procurement documents with line items&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service Requests&lt;/strong&gt; — internal and external maintenance requests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Labor&lt;/strong&gt; — human resources tied to work order execution&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Locations, Sites, Teams, Users, Vendors&lt;/strong&gt; — the full organizational context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these are available via the same endpoints you'd use for any other system in the Makini catalog. No per-system SDK, no bespoke field mapping — just consistent, normalized data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How the connection works&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your customer opens Makini Link (embeddable in your app or triggered via a link/email), selects TechnologyOne, and logs in with their existing credentials. That's it — the connection is live. The whole flow takes about a minute and requires no involvement from your engineering team.&lt;/p&gt;

&lt;p&gt;On the backend, Makini handles auth, syncs, retries, and keeps the integration alive as TechnologyOne's API evolves.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why this matters for your product roadmap&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you're selling to enterprise accounts in the public sector or education, TechnologyOne is a system you'll encounter regularly. Without a ready integration, you're either losing deals or committing to months of custom work upfront. With Makini, you can tell prospects "yes, we support TechnologyOne" on day one — and actually mean it.&lt;/p&gt;




&lt;p&gt;Check out the full data model and API reference: &lt;a href="https://www.makini.io/integrations/technologyone" rel="noopener noreferrer"&gt;makini.io/integrations/technologyone&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Want to test the connection? &lt;a href="https://app.makini.io/register" rel="noopener noreferrer"&gt;Start a free trial →&lt;/a&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Designing APIs for Heterogeneous Enterprise Systems: Lessons from 2000+ Integrations</title>
      <dc:creator>Makini</dc:creator>
      <pubDate>Fri, 03 Apr 2026 07:56:55 +0000</pubDate>
      <link>https://dev.to/makiniintegration/designing-apis-for-heterogeneous-enterprise-systems-lessons-from-2000-integrations-dc7</link>
      <guid>https://dev.to/makiniintegration/designing-apis-for-heterogeneous-enterprise-systems-lessons-from-2000-integrations-dc7</guid>
      <description>&lt;p&gt;At Makini, we've spent six years building integrations across enterprise systems—ERP platforms like SAP and Oracle, CMMS solutions like IBM Maximo, WMS systems like Manhattan, and over 2000 other industrial software platforms. This experience has taught us hard lessons about API design, data modeling, and the unique challenges of creating unified interfaces for fundamentally different systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Standardization Problem:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Enterprise systems weren't designed with third-party integrations in mind. They evolved to solve specific industry problems, which means their data models reflect decades of domain-specific decisions. A "purchase order" in SAP has different required fields, validation rules, and lifecycle states than a purchase order in Oracle. Yet conceptually, they represent the same business entity.&lt;/p&gt;

&lt;p&gt;The naive approach is to create a unified data model that's the union of all fields across all systems. This produces an API with hundreds of optional fields where clients never know which combinations are valid for which systems. The result is an interface that's technically complete but practically unusable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Our Approach: Core Models with Extension Points:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We've settled on a pattern where unified models define a core set of common fields that exist across most systems, use consistent naming conventions regardless of source system terminology, include standard validation rules that apply universally, and provide clear semantic meaning for each field.&lt;/p&gt;

&lt;p&gt;System-specific fields that don't fit the common model go into an &lt;code&gt;extended_properties&lt;/code&gt; object that's indexed by field name and includes metadata about the field's type and validation rules. This preserves access to all data while keeping the common case simple.&lt;/p&gt;

&lt;p&gt;Here's a simplified example of how this looks in practice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"po_123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"purchase_order"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"vendor_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vendor_456"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"total_amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;15000.00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"approved"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"line_items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"extended_properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sap_plant_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"US01"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SAP-specific plant identifier"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"custom_approval_level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"integer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Internal approval hierarchy level"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Handling Authentication Flows:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Enterprise systems use wildly different authentication mechanisms. We've encountered OAuth 2.0 with various grant types, API keys with different header formats, session-based authentication requiring login flows, SAML and other SSO protocols, and legacy systems with custom authentication schemes.&lt;/p&gt;

&lt;p&gt;Rather than exposing this complexity to API consumers, we implement a credential management layer that normalizes authentication across systems. Developers work with connection objects that abstract the underlying auth mechanism. Our authentication module handles token refresh, session management, and credential storage automatically.&lt;/p&gt;

&lt;p&gt;The key architectural decision was separating connection establishment (which happens once per customer) from API requests (which happen continuously). Connection establishment can be complex and user-interactive; API requests must be simple and programmatic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rate Limiting and Backpressure:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Different systems have vastly different rate limits, and some don't document their limits at all. We've built adaptive rate limiting that learns system behavior over time, implements per-connection backpressure to avoid overwhelming source systems, queues requests when approaching rate limits, and provides webhook-based notifications when async operations complete.&lt;/p&gt;

&lt;p&gt;For systems without documented rate limits, we start conservatively and gradually increase request rates while monitoring for rate limit errors. This auto-tuning approach has proven more reliable than trying to discover limits through documentation or support channels.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Versioning Strategy:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With 2000+ integrations, we need to handle API versioning at multiple levels. The source system's API version can change, our unified API version evolves with new features, and customer-specific configurations may be pinned to specific versions.&lt;/p&gt;

&lt;p&gt;Our versioning approach uses semantic versioning for the unified API with backward compatibility guarantees, per-connection version pinning so customers control when they upgrade, transformation layers that translate between unified API versions, and deprecation windows with clear migration paths.&lt;/p&gt;

&lt;p&gt;We maintain multiple API versions simultaneously in production, which adds operational complexity but prevents breaking changes from disrupting customer operations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Error Handling and Observability:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When an API call fails, it could be due to network issues, the source system being down, invalid data format, business rule violations in the source system, or authentication/permission problems. Each category requires different handling and different information for debugging.&lt;/p&gt;

&lt;p&gt;Our error responses include structured error codes that clients can switch on programmatically, request IDs that trace through our entire stack and into source systems where possible, contextual information about what operation was being attempted, and actionable remediation steps when we can determine them.&lt;/p&gt;

&lt;p&gt;For observability, we emit structured logs at API boundaries, track request latency percentiles per source system, monitor error rates with automatic alerting, and maintain dashboards showing health metrics for each connected system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Webhook Challenge:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Many enterprise systems support webhooks for real-time notifications, but implementations vary wildly. Some send webhooks with full payloads, others send just IDs requiring follow-up API calls, signature schemes for verification differ across systems, and retry behavior is inconsistent or undocumented.&lt;/p&gt;

&lt;p&gt;We normalize this by receiving webhooks from source systems, verifying signatures using system-specific methods, transforming payloads into our unified event format, and delivering to customer webhook endpoints with guaranteed delivery semantics.&lt;/p&gt;

&lt;p&gt;Our webhook infrastructure includes replay capabilities for debugging, automatic retry with exponential backoff, dead letter queues for persistently failing deliveries, and monitoring to detect when source systems stop sending expected webhooks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance Considerations:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Synchronizing data across thousands of connections requires careful attention to performance. We use connection pooling with per-system tuning for optimal throughput, parallel request processing with per-connection rate limiting, intelligent caching based on data volatility patterns, and incremental sync strategies that minimize data transfer.&lt;/p&gt;

&lt;p&gt;For large datasets, we implement cursor-based pagination that works across systems with different pagination schemes, streaming responses to avoid memory pressure, and background jobs for operations that exceed reasonable request timeouts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Testing Strategy:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Testing integrations with hundreds of enterprise systems presents unique challenges. Most systems are expensive to spin up test instances for, many have complex setup requirements, and production data can't be used for testing due to compliance requirements.&lt;/p&gt;

&lt;p&gt;Our testing approach includes contract tests that verify API response schemas, integration tests against sandbox environments where available, snapshot testing to detect unexpected API changes, and synthetic monitoring in production to catch issues before customers do.&lt;/p&gt;

&lt;p&gt;We also maintain a library of anonymized test fixtures captured from real customer connections, which helps us catch edge cases that wouldn't appear in clean test data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lessons Learned:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After building thousands of integrations, some patterns are clear. Documentation is often incomplete or outdated—observing actual API behavior is more reliable than trusting docs. Error messages from enterprise systems can be cryptic or entirely absent—good error handling must account for this. System behavior varies between versions even when APIs are supposedly compatible. Hidden rate limits and undocumented throttling are common.&lt;/p&gt;

&lt;p&gt;The most important lesson is that building a unified API isn't about finding the perfect abstraction—it's about making pragmatic trade-offs that optimize for developer experience while preserving system-specific capabilities when needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open Questions:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We're still exploring better approaches to several challenges. How do you design APIs that gracefully handle source system downtime without confusing clients? What's the right balance between eagerly validating input versus passing it through to source systems? How do you version data models when the underlying systems evolve in incompatible ways? What testing strategies work for systems you can't easily replicate?&lt;/p&gt;

&lt;p&gt;If you're working on similar problems—API design for heterogeneous systems, integration platforms, or enterprise software connectivity—we'd love to hear your approaches and learn from your experiences.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;More on integration architecture&lt;/strong&gt;: &lt;a href="https://www.makini.io/integrations" rel="noopener noreferrer"&gt;https://www.makini.io/integrations&lt;/a&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
