Integrations — zerp's external & integration surface
The whole integrations domain reduces to one idea:
This is where zerp touches the outside world. Two modules are true outbound/inbound integrations with third-party services — MCP (lets an external AI agent drive finance) and Zoom (video meetings for HR). A third pair are client-facing edges — Version (mobile force-update check) and the Wingold member-code link (a vestigial field tying a zerp user to an external Wingold member). Everything else cross-cutting that an integration leans on — audit, logging, notifications, file upload — is documented as platform "spine" docs; this overview only points at them.
Source: BE src/modules/mcp (incl. mcp/tools/), src/modules/zoom, src/modules/version; plus the Wingold field in src/modules/user/user.schema.ts and src/constant.ts · Admin src/modules/hr/zoom (Zoom only — MCP/Version/Wingold have no admin UI)
1. The integration surface at a glance
| Integration | Direction | What it is | Doc / source |
|---|---|---|---|
| MCP | Inbound (an external AI agent → zerp) | A Model Context Protocol server over SSE exposing four finance tools (list_accounts, create/get/post_journal_entry) so an LLM can draft & post journal entries. |
mcp.md |
| Zoom | Both (zerp → Zoom REST; Zoom → zerp webhooks) | Schedule/join/end/delete Zoom meetings for HR training & interviews; embedded Web SDK join; webhook-driven status & recording sync. | zoom.md |
| Version | Outbound-facing (mobile client → zerp) | A public GET /api/version returning iOS/Android/web app versions + force-update flags + store URLs, read from the runtime config. |
See §2 Version. Source version/version.controller.ts. |
| Wingold | Linkage only | A single wingoldMemberCode field on the User and a AP_WINGOLD_TIME_FORMAT constant — a vestigial link to an external Wingold membership; no client/sync code in this repo. |
See §3 Wingold. |
MCP and Zoom are the only two with their own module folders, schemas, and full per-module docs in this domain. Version and Wingold are documented inline below — they are too small for a standalone rebuild-grade doc, and Wingold is essentially a placeholder.
Pointers to cross-cutting integration spine (platform docs)
These are integration-adjacent capabilities that many modules use; they are owned by the platform "spine", not by this domain:
- Audit / trail — every mutation's before/after snapshot via
@AuditMeta; the Zoom and MCP-posted entries both flow through it. → platform/audit-trail (BEaudit-trail/). - Activity log — the
AuditLogcollection of field-level update records. → platform/audit-trail (BElog/). - Notifications — in-app
Notificationcollection + GraphQLnotificationCreatedsubscription (real-time push to clients). → platform/notifications (BEnotification/). - Files / assets / upload —
POST /api/file-uploadscontroller +uploadcollection for binary assets referenced across modules. → platform/files-assets-upload (BEupload/).
2. Version (mobile force-update)
A tiny, unauthenticated REST edge for the mobile apps to check whether they must update.
Source: version/version.controller.ts, version/version.module.ts (imports ApConfigModule).
// GET /api/version (no auth)
@Get("/")
async version() {
const config = await this.configSvc.findLast(); // latest runtime config doc
return {
ios: { version: config.iosVersion, forceUpdate: config.iosForceUpdate,
downloadUrl: "https://apps.apple.com/us/app/msgold/id6450125286" },
android: { version: config.androidVersion, forceUpdate: config.androidForceUpdate,
downloadUrl: "https://play.google.com/store/apps/details?id=com.msgold.app" },
web: { v: "1.2", forceUpdate: true }, // hardcoded
};
}- No auth, no tenant scoping — it is a global, public endpoint the apps poll on launch.
- iOS/Android versions + force-update flags come from the runtime config (
ApConfigService.findLast()); the store URLs are hardcoded to the MSGold apps, and the web block is hardcoded (v: "1.2", forceUpdate: true). - The matching admin surface is the runtime config screen (where
iosVersion/androidVersion/*ForceUpdateare edited) — see subscription-config.
3. Wingold (member-code link)
Not a module — a dormant linkage. The only traces of "Wingold" in zerp-be:
| Trace | Location | Meaning |
|---|---|---|
wingoldMemberCode: number (default 0) |
user/user.schema.ts (UserEntity) |
A per-user code linking a zerp user to an external Wingold membership. |
AP_WINGOLD_TIME_FORMAT = "YYYY-MM-DDThh:mm:ss" |
src/constant.ts |
A date format constant intended for Wingold-compatible timestamps. |
There is no Wingold client, service, controller, sync job, or config in this repository — the field is populated/consumed elsewhere (an external Wingold system or a separate service), not by code in zerp-be. Document it as a known integration seam / TODO, not an implemented integration. If a Wingold sync is ever built, it belongs in this domain alongside mcp.md / zoom.md.
4. Cross-domain relationships
┌──────────────────────────── outside world ───────────────────────────┐
external AI agent ───▶│ MCP (SSE /api/mcp) ─delegates▶ finance: account / journal / transaction│
Zoom Cloud ◀──REST──▶│ Zoom (/webhooks/zoom + GraphQL) ─sits under▶ HR menu (training/interview)│
mobile apps ─poll────▶│ Version (GET /api/version) ─reads▶ runtime config (subscription-config) │
Wingold (external) ···│ wingoldMemberCode on User ······· (no sync code here) │
└──────────────────────────────────────────────────────────────────────┘
cross-cutting spine used by the above: audit-trail · log · notifications · upload (platform/*)
Related domains & platform docs: finance/account · finance/journal · finance/transaction (MCP writes through these) · hr/training · recruitment/recruitment (Zoom meetings are scheduled for these, by convention) · subscription-config (Version reads runtime config) · platform/auth · platform/permissions-access · platform/audit-trail · platform/notifications · platform/files-assets-upload · platform/multi-tenancy.
5. Shared mechanics & gotchas (domain-wide)
- Two different auth styles. MCP and Zoom-GraphQL use the normal zerp auth chain (
@ApiAuthorize()/@ApGqlAuthorize()), but the inbound webhooks/edges bypass it: Zoom's/webhooks/zoom/eventsauthenticates by HMAC signature + replay window, and/api/versionis fully public. Don't assume an integration endpoint is session-guarded. - Context source differs. Zoom derives tenant/host from
ApContextService(session-bound); MCP takescompanyId/branchId/createdByas tool arguments (not session-derived) — its real boundary is the transport-level auth, andcreate_journal_entryhas no company check at all. See mcp.md §9. - No outbound-call transactionality. Zoom (and any future sync) makes an external HTTP call plus a Mongo write that are not atomic — partial failures can drift zerp from the external system.
- External config lives in env, not the DB. Zoom needs
ZOOM_*env vars across two Zoom apps; Version reads app versions from the runtime config doc. Misconfiguration is logged at startup (ZoomConfigService.warnMissingConfig) rather than failing hard. - Historical naming. The MCP server announces itself as
"zyncount"and the Version store URLs / app ids point at MSGold — both are legacy names from the product's lineage, not bugs.