Integrating ERPNext Helpdesk Into a Multi-Tenant SaaS Without Leaking Tenant Data

8 Jan 2026 · ERPNext, Go, Supabase, Webhooks, Support

A support ticket is a poor place to discover that two systems disagree about who is allowed to see it. The browser needs a useful conversation; the back office needs an auditable integration. Those are related jobs, not the same permission.

Problem

The goal was to connect a SaaS support workflow to ERPNext Helpdesk without exposing ERPNext itself to tenants. The SaaS should let staff and support users work with tickets, but the tenant-facing experience must not reveal internal IDs, internal hostnames, or support-only fields.

Constraints and risks

The risks are familiar but serious:

  • Tenant data leaking across support queues.
  • Internal ERPNext identifiers appearing in tenant UI.
  • Sync conflicts when one system changes before the other.
  • Over-tight coupling to a vendor-specific workflow.

I wanted the integration to be reversible. If the ERPNext side changed, the SaaS should still keep its own support model intact.

Design / Investigation

The shape that works best is a small support gateway:

Tenant UI -> SaaS API -> Support gateway -> ERPNext Helpdesk

The SaaS stores its own support records with tenant scope. The gateway translates those records into ERPNext tickets and keeps an audit trail of what was sent, when it was sent, and whether it was acknowledged.

I start with polling because it is easier to reason about:

Sync style Why use it first What it buys later
Polling Simple to debug and retry Baseline correctness
Webhooks Add later for faster updates Lower latency and fewer stale states
Event pipeline Only if volume justifies it More resilience and fan-out

Implementation approach

I model support data in a tenant-safe way:

  • One row for the tenant-scoped support case.
  • One row for the external ERPNext mapping.
  • One row for sync status and retry state.
  • No ERPNext identifiers in the tenant UI.

I also separate the user-visible status from the integration status. A ticket can be open for the tenant while the ERP side is still reconciling. The UI should say that clearly instead of pretending the whole system is immediate.

Example reconciliation states:

State Meaning
queued Waiting to be sent
sent Accepted by the gateway
synced Confirmed in ERPNext
stale Local and remote state disagree
retrying The gateway is trying again

What I would do differently

I would keep the gateway schema even smaller at the beginning and only add fields when the support workflow proves it needs them. Support integrations tend to grow more quickly than you expect.

I would also invest earlier in a reconciliation report, because support workflows are much easier to trust when the operator can see the last successful sync and the current failure reason.

The boundary that stayed

The support gateway validates the browser’s Supabase JWT and capability before it acts, and never trusts x-pub-id by itself. It uses conditional claims, idempotency markers and polling reconciliation so a successful ERPNext call followed by a failed local update can be recovered. Redpanda, when enabled, is only a routing path; Supabase remains the tenant-visible source of truth.

I now keep internal collaboration systems behind that boundary as well. A future Raven route is explicitly disabled until a live inspection proves its DocTypes and permissions. Convenience is not worth making an internal support tool part of the tenant API by accident.