Skip to main content
You do not need this page to build on Voyant. It exists because knowing the shape of the thing behind the API makes the API easier to predict.
Modules are not an extension point. They are the components the platform itself is built from. A deployment cannot add, drop, or replace one, and there is no supported path for writing your own. If you need new capability, build an app; to change existing behavior, build an extension.

One deployable, many modules

Voyant is assembled from domain modules — catalog, commerce, inventory, operations, bookings, finance, and the rest. They are components of one resident Node application, not separately deployed services. A module owns four things:
  • Its data model — the tables holding the canonical state of its domain.
  • Its services — the domain logic that enforces that domain’s rules.
  • Its routes — thin HTTP handlers that validate, call a service, and shape a response.
  • Its background work — the subscribers and jobs its behavior requires.
The test for ownership is canonical state. If a table is the authoritative record of one capability, it lives in that capability’s module. Each module usually ships alongside a -contracts package (the wire types shared by server and client) and a -react package (hooks and components built on those contracts). That is why the SDK types match the API exactly: both sides import the same contract.

Why cross-domain reads look the way they do

Inside a module you get ordinary relational modeling: real foreign keys, constraints, relations. Across modules you do not. A booking does not carry a foreign key into the CRM’s tables; it carries a plain id, and the association is declared as a link. Reads stitch across modules in application code through the query graph rather than through joins. This is why the API returns ids where you might expect embedded objects, and why some reads are composed rather than single queries. The reason is packaging: a hard cross-package foreign key would force every deployment to install both sides.

Where the seams are

Four seams, in the order you should reach for them: Automations are not a seam. They run on your infrastructure, calling the API and reacting to webhooks.

Composition happens at build time

A deployment is a versioned, declarative package graph resolved at build time and lowered to one application. The project declares only its differences from the standard product; everything else comes from the versioned distribution. Provider bindings resolve at boot inside that fixed graph. The practical consequence: upgrading is a version bump plus migrations, not a code merge. See Configuration.

Architecture

The runtime, the request lifecycle, and the tenancy model.

Build on Voyant

The part you actually work with.