Skip to content

How to Build ERP Integrations Without Storing Customer Data

Learn how to architect pass-through, zero data retention ERP integrations (NetSuite, SAP) that pass enterprise InfoSec reviews and avoid compliance liabilities.

Uday Gajavalli Uday Gajavalli · · 13 min read
How to Build ERP Integrations Without Storing Customer Data

B2B SaaS teams face a brutal reality when moving upmarket. You build a financial workflow tool, an AI agent, or an analytics platform that needs to read and write data to enterprise ERPs like NetSuite, SAP, or Microsoft Dynamics 365. The engineering team ships the integration. The product looks great in demos. Then, the deal dies in procurement.

If you are trying to integrate your B2B SaaS product with enterprise ERPs, and your integration middleware writes any customer financial data to a database, you are building a compliance liability. Your InfoSec review will fail, your deal will stall, and your competitor who figured out pass-through architecture will close the contract instead. If you want to know how to build ERP integrations securely, the answer requires abandoning the traditional sync-and-cache model entirely in favor of a pass-through, zero data retention architecture.

This guide breaks down exactly why caching ERP data is an architectural mistake that kills enterprise deals, the technical realities of integrating with complex systems like NetSuite and SAP, and how to build a stateless integration layer that normalizes data on the fly and passes enterprise procurement without drama.

The Enterprise Procurement Wall: Why Caching ERP Data Kills Deals

Enterprise deals die in procurement, not in product demos. The number one thing that kills them is your data storage architecture.

When your sales team pushes a six-figure contract to the final stage, the buyer's InfoSec team sends over a security questionnaire. Enterprise procurement teams use data residency and security questionnaires as a primary filter for software vendors. One of the first questions you will encounter is: "Does the vendor store, cache, or persist any customer data? If so, describe the data types, retention periods, and storage locations."

If your integration layer syncs NetSuite general ledger entries, SAP purchase orders, or Dynamics 365 payroll data into a centralized database just to transform it before sending it to your application, you have to answer "Yes."

That single "Yes" triggers a massive compliance cascade. You are now legally responsible for securing a duplicate copy of your customer's most sensitive financial data. According to IBM's 2024 Cost of a Data Breach Report, the global average cost of a data breach reached a record $4.88 million - a 10% increase from the previous year. Enterprise buyers know this. They do not want their financial data sitting in your middleware provider's AWS account.

Traditional integration platforms force you into this liability. They pull data from the upstream ERP, store it in an intermediate database, apply transformation logic, and then push it to your application. This expands the attack surface. It complicates SOC 2 audits. It forces you to sign complex Data Processing Agreements (DPAs) that delay contracts by months.

To bypass this procurement wall, you must prove to the buyer that their data never rests on your integration infrastructure.

What is Zero Data Retention (ZDR) in API Architecture?

Zero Data Retention (ZDR) is an architectural design pattern where an integration layer processes third-party API payloads entirely in memory, never writing them to persistent storage. The middleware acts as a stateless proxy. It authenticates the request, transforms the payload, forwards it between your application and the upstream ERP, and discards the payload the moment the response is delivered.

In a strict ZDR architecture, there are:

  • No database writes: Payload bodies are never inserted into a relational or NoSQL database.
  • No message queue persistence: Event streams and webhooks are processed ephemerally without being dumped into long-term storage queues.
  • No payload logging: Application logs record the transaction status (e.g., HTTP 200 OK) but strip the actual request and response bodies.

This approach is becoming a strict requirement for enterprise compliance. As noted by CData Software, ZDR ensures that sensitive data is processed in-memory and never written to persistent storage, which is critical for meeting GDPR data minimization principles and HIPAA compliance.

If you want to understand the foundational concepts behind this, read our guide on what zero data retention means for SaaS integrations. The core takeaway is that by eliminating the cache, you eliminate the compliance liability. You shrink your attack surface to zero at rest.

The Hidden Risks of Traditional iPaaS and ETL Workflows

For the last decade, connecting to enterprise systems meant relying on traditional Integration Platform as a Service (iPaaS) or Extract, Transform, Load (ETL) workflows. Platforms like MuleSoft popularized an API-led connectivity model that often relies on a three-tier architecture (System, Process, Experience APIs). While powerful for internal enterprise orchestration, this model frequently depends on stateful orchestration, caching, and data synchronization.

When B2B SaaS vendors use these legacy platforms to build customer-facing integrations, they inherit hidden risks.

Legacy platforms pull data into centralized systems for processing. As IntelliStack points out, creating copies of sensitive data expands the risk surface and complicates compliance with strict regulations. If a vulnerability is discovered in the iPaaS provider's centralized database, your customer's ERP data is exposed.

Beyond security, there is a performance penalty. Caching architectures introduce latency because data must be written to disk, indexed, and retrieved. Cloud-native integrations built on stateless, pass-through architectures offer lower latency and error rates. A recent study on ERP integrations published on ResearchGate showed cloud-native solutions are 33% faster in latency and have a lower error rate compared to on-premise ESB/ETL queues.

When your enterprise customer announces they are migrating legacy on-premise ERPs to cloud APIs, a sync-and-cache architecture becomes a massive bottleneck. The synchronization jobs fail during cutover, state consistency breaks, and your engineering team spends weeks manually reconciling duplicate records.

How to Architect a Pass-Through ERP Integration

Building a pass-through ERP integration requires a fundamental shift in how you handle data mapping, authentication, and request execution. You cannot rely on hardcoded integration scripts that pull data into a local database.

Instead, you need a generic execution pipeline driven by declarative configuration. Here is how you architect it.

1. The Generic Execution Pipeline

To achieve true zero data retention, your middleware must execute API calls without relying on integration-specific code. Truto achieves this by handling 100+ third-party integrations without a single line of integration-specific code in its database or runtime logic.

Instead of writing custom Python or Node.js scripts for NetSuite and SAP, you define a Unified Model. This model maps your application's standard data structure (e.g., a generic Invoice object) to the provider's specific schema using a transformation language like JSONata.

When your application requests an invoice, the middleware:

  1. Receives the request.
  2. Looks up the declarative mapping configuration for the specific ERP.
  3. Translates the request into the upstream provider's exact format (e.g., constructing a NetSuite SuiteQL query) entirely in memory.
  4. Dispatches the HTTP request to the ERP.
  5. Receives the response.
  6. Transforms the response back into your Unified Model.
  7. Returns the response to your application and clears the memory buffer.
sequenceDiagram
    participant App as Your B2B SaaS
    participant Proxy as Stateless Proxy Layer
    participant ERP as Upstream ERP (SAP/NetSuite)

    App->>Proxy: GET /unified/invoices
    Note over Proxy: Load JSONata Mapping<br>Transform to SuiteQL (In-Memory)
    Proxy->>ERP: POST /query (SuiteQL)
    ERP-->>Proxy: Raw ERP JSON Response
    Note over Proxy: Transform to Unified Model<br>Discard Raw Payload
    Proxy-->>App: Standardized Invoice JSON

This architecture is particularly vital for modern AI workflows. If you are building AI features, refer to our deep dive on zero data retention AI agent architecture to see how this pipeline secures LLM tool calling.

2. Handling Complex API Surfaces via Proxy

Enterprise ERPs rarely offer clean RESTful endpoints. NetSuite requires complex SuiteQL queries or SuiteScript deployments. SAP relies on heavy OData protocols. Some modern tools use GraphQL.

To maintain a stateless architecture, your integration layer must act as a Proxy API. For example, Truto's Proxy API allows developers to expose complex GraphQL-backed integrations as RESTful CRUD resources on the fly. It uses placeholder-driven request building to construct dynamic queries based on the incoming request context, execute them against the upstream provider, and extract the relevant nodes from the response - all without caching the underlying data.

If you need to map custom fields, you do it declaratively. You can learn exactly how to create ERP-specific mapping guides to handle these edge cases without writing stateful code.

Pass-Through Architecture Diagrams

The gap between a sync-and-cache platform and a pass-through platform is easier to see than to describe. Here is what each flow actually looks like when an enterprise buyer's InfoSec team asks where their data lives.

Traditional Sync-and-Cache

flowchart LR
    App[Your SaaS App]
    MW[Integration Middleware]
    Cache[("Staging DB<br>payloads at rest")]
    Queue[("Message Queue<br>payloads at rest")]
    ERP[Upstream ERP]

    App --> MW
    MW --> Cache
    Cache --> Queue
    Queue --> ERP
    ERP --> Queue
    Queue --> Cache
    Cache --> App

Every arrow crossing into Cache or Queue represents a copy of customer financial data written to disk. Each copy is in scope for SOC 2, GDPR, and whatever DPA the buyer negotiates. Each copy needs its own encryption keys, access reviews, and retention policy.

Pass-Through, Zero Data Retention

flowchart LR
    App[Your SaaS App]
    Proxy["Stateless Proxy<br>in-memory transform"]
    Auth[("Token Store<br>OAuth only")]
    ERP[Upstream ERP]

    App --> Proxy
    Proxy -.reads token.-> Auth
    Proxy --> ERP
    ERP --> Proxy
    Proxy --> App

Only OAuth tokens persist, and they never touch the data plane. Payload bodies exist for the duration of a single request handler and are garbage-collected the moment the response is returned to the caller.

Request Lifecycle in Detail

sequenceDiagram
    participant App as Your SaaS
    participant Proxy as Stateless Proxy
    participant TokenStore as Token Store
    participant ERP as Upstream ERP

    App->>Proxy: GET /unified/invoices?cursor=abc
    Proxy->>TokenStore: Fetch valid OAuth token
    TokenStore-->>Proxy: Access token (in-memory)
    Note over Proxy: Load JSONata mapping<br>Translate cursor to OData $skip<br>Build upstream request
    Proxy->>ERP: HTTPS request with bearer token
    ERP-->>Proxy: Raw ERP payload
    Note over Proxy: Transform to unified schema<br>Normalize rate limit headers<br>Extract next cursor
    Proxy-->>App: Unified invoice JSON + cursor
    Note over Proxy: Discard payload buffers<br>No writes to disk

Notice what the diagram does not contain: no staging table, no worker pool holding half-processed records, no message queue durably storing the payload. The token store is the only stateful component, and it holds credentials, not customer data.

Handling Rate Limits and Pagination Statelessly

The most common objection to pass-through architecture is handling API limits. Engineers often assume they need a database to queue requests, track rate limits, and manage pagination state. This is false. You can handle all of this statelessly.

Normalizing Rate Limits

When an upstream API like NetSuite or SAP returns an HTTP 429 Too Many Requests error, traditional middleware absorbs the error, places the request in a retry queue, and waits. This requires persistent state.

A zero data retention architecture takes a different approach: it normalizes the rate limit information and passes the responsibility to the caller.

Truto does not retry, throttle, or apply backoff on rate limit errors. When an upstream API returns an HTTP 429, Truto passes that error directly to the caller. However, because every ERP returns rate limit data differently (some in headers, some in the body, some not at all), Truto normalizes upstream rate limit info into standardized headers per the IETF specification:

  • ratelimit-limit: The total request quota.
  • ratelimit-remaining: The remaining quota.
  • ratelimit-reset: The time window reset point.

Your application receives the 429 and the standardized headers. Your application - which already maintains state for its own background jobs - applies exponential backoff and retries the request. The middleware remains perfectly stateless.

// Example: Client-side handling of normalized 429 errors
async function fetchInvoicesWithRetry(url, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url);
    
    if (response.status === 429) {
      const resetTime = response.headers.get('ratelimit-reset');
      const waitMs = (resetTime * 1000) - Date.now();
      
      console.warn(`Rate limited. Waiting ${waitMs}ms before retry.`);
      await new Promise(resolve => setTimeout(resolve, waitMs));
      continue;
    }
    
    if (!response.ok) throw new Error('API Request Failed');
    return await response.json();
  }
  throw new Error('Max retries exceeded');
}

Stateless Pagination

Pagination follows the same principle. Instead of the middleware downloading all 10,000 records, caching them, and serving them to you in chunks, the middleware normalizes the pagination cursor.

When you request page one, the middleware translates your generic cursor parameter into the ERP's specific pagination format (e.g., an OData $skip token). The ERP returns the data and a next-page token. The middleware translates that token back into a generic cursor and passes it to your application. Your application stores the cursor and passes it back on the next request. The state lives with the client, not the middleware.

Securing the Connection: OAuth, Webhooks, and Ephemeral Processing

Maintaining a zero data retention posture requires strict control over how authentication tokens and asynchronous webhooks are handled.

Ephemeral Webhook Processing

ERPs frequently send webhooks when a purchase order is approved or a journal entry is posted. Traditional platforms ingest these webhooks into a Kafka or RabbitMQ queue, writing the payload to disk before processing it. If that queue is breached, the financial data is compromised.

In a ZDR architecture, webhooks are processed ephemerally. The middleware receives the HTTP ingestion, verifies the cryptographic signature in memory, maps the vendor-specific event (e.g., netsuite.transaction.created) to a unified event format, and immediately attempts outbound delivery to your application.

If the delivery fails, the middleware does not store the payload indefinitely. It relies on the upstream provider's retry mechanics or drops the event after a short, memory-bound retry window. The payload is never written to a database schema.

Secure OAuth Token Management

Authentication state is the only data the integration layer should persist. You must store OAuth access tokens and refresh tokens to maintain the connection. However, these tokens must be strictly isolated from the data plane.

The platform schedules work ahead of token expiry. Truto refreshes OAuth tokens shortly before they expire, ensuring that when an API call is made, a valid token is already available in memory. This prevents the middleware from having to pause a request, execute a database write to update a token, and then resume the request. The token lifecycle operates completely independent of the data payload lifecycle.

Ensuring Zero Data Persistence

Claiming "pass-through" in a security questionnaire is easy. Proving it under an audit is not. If you want to build ERP integrations without storing customer data, every layer of the middleware needs explicit controls that make persistence impossible, not merely discouraged. Here are the controls that hold up under enterprise scrutiny.

Payload-Free Logging

Application logs are the most common leak. A default console.log(request) or an APM tracer capturing HTTP bodies will silently write NetSuite journal entries to your log aggregator's disk. Enforce log scrubbing at the middleware boundary:

// Structured logging that records metadata only
logger.info({
  event: 'upstream_request',
  integration: 'netsuite',
  resource: 'invoices',
  method: 'GET',
  status: response.status,
  duration_ms: elapsed,
  correlation_id: requestId,
  // No request.body, no response.body, no query values
});

Every log line, trace span, and error report should carry transaction metadata (status, latency, integration name, correlation ID) but strip payload bodies and query parameters that may contain PII or financial figures.

In-Memory Only Transformations

Declarative mapping languages like JSONata are pure functions. They accept an input JSON blob, produce an output JSON blob, and never touch the file system. Keep the transformation step inside a single request handler so that garbage collection reclaims the payload as soon as the response returns. Avoid patterns that hand payloads off to a background worker, because "background" almost always means "persistent queue."

If a specific ERP call is genuinely slow (a NetSuite SuiteQL join across three million rows, for example), stream the response back to the caller rather than buffering it. Node.js streams, HTTP chunked transfer encoding, and server-sent events all let you flush data to the client without ever materializing the full payload in memory or on disk.

Ephemeral Webhook Handling with Hard TTLs

Inbound webhooks are the trickiest path. If you have to buffer them, buffer them in memory-bound structures with a hard TTL, not in a durable log. Sign the outbound delivery in the same handler that receives the inbound event, so the payload never leaves the process boundary. If outbound delivery fails, rely on the upstream provider's retry contract instead of persisting the event yourself. Providers like NetSuite and SAP will re-emit events on delivery failure; that is a stronger guarantee than a self-hosted retry queue anyway.

Auth State Isolated from the Data Plane

The one thing you must persist is OAuth credentials. Treat the token store as a separate security domain: different database, different encryption keys, different access policies. A breach of the data plane must not expose refresh tokens, and a breach of the token store must not expose customer ERP records (because there are none to expose).

Idempotency Without Storage

Engineers often reach for a database to implement idempotency keys. You do not need one. Pass idempotency keys through to the upstream ERP when the provider supports them, or generate deterministic keys from the request payload hash and let the ERP deduplicate. The middleware records nothing.

Verifiable Retention Policy

Enterprise InfoSec teams do not accept "trust us." Give them artifacts that map directly to your architecture:

  • A written retention policy stating that request and response bodies are never persisted.
  • A network diagram showing no arrow between the request handler and any database except the token store.
  • SOC 2 controls that test log scrubbing and enforce database schema restrictions.
  • Sample audit logs demonstrating that stored records contain only metadata, never customer payloads.
  • A DPA that lists the token store as the only data processing location, with a clear statement that no customer transactional data is retained.

When these artifacts line up with the architecture, the InfoSec review stops being a fight over cached data and becomes a routine check.

Strategic Next Steps

Building ERP integrations does not have to mean taking on massive compliance liabilities. By adopting a pass-through, zero data retention architecture, you can give your product read and write access to NetSuite, SAP, and Dynamics 365 without ever caching a single financial record.

Stop letting procurement teams kill your enterprise deals because of your integration middleware. Architect for statelessness. Process data in memory. Pass rate limits to the caller. Use declarative mapping instead of hardcoded integration scripts.

When you eliminate the cache, you eliminate the risk. Your InfoSec review will pass, your engineering team will spend less time managing stateful queues, and your sales team will close the enterprise contracts they fought for.

FAQ

What is zero data retention in ERP integrations?
Zero data retention is an architectural pattern where an integration layer processes third-party API payloads entirely in-memory and discards them immediately, never writing customer data to a database or persistent queue.
Why do caching integration architectures fail InfoSec reviews?
Caching architectures create a duplicate copy of sensitive customer financial data on third-party infrastructure. This expands the attack surface and creates severe compliance liabilities under GDPR, HIPAA, and SOC 2.
How do you handle rate limits without a database queue?
A stateless integration layer passes HTTP 429 errors directly to the caller, along with normalized rate limit headers (like ratelimit-remaining). The calling application is then responsible for applying exponential backoff and retrying the request.
Can you handle webhooks statelessly?
Yes. Webhooks can be ingested, cryptographically verified, mapped to a unified event format, and delivered to the destination application entirely in-memory without writing the payload to persistent storage.

More from our Blog