bundle: a single JSON object holding pure, portable configuration. When you apply a template, Sapt replays that bundle into a freshly-created sub-project.
This page documents what’s inside the bundle. For the end-to-end onboarding flow (apply → connect integrations → invite), see Building a client onboarding funnel.
Why the reference shows
bundle as an open object. The bundle is a deeply-nested structure (it embeds workflow conditions, CRM column defs, onboarding flows, and more), so the auto-generated Reference renders it as free-form JSON rather than a fixed schema. This page is the schema — treat it as the source of truth for the bundle’s shape.What the bundle is — and isn’t
The bundle is structural config only. It carries no project-local UUIDs: roles are keyed byname, and the sidebar’s per-role overlay references those same names. Apply remaps each name to the freshly-created role’s id.
Anything tenant-specific that would dangle in a fresh project is nulled during capture and reported back to you in
warnings.
Core sections
These five are always present on a bundle and are applied in this fixed order — later steps may reference earlier ones (workflows and object types reference spine and bundle slugs):Extended sections
All optional. Each reuses the canonical schema from its owning module, so the bundle shape can’t drift from the live product:Variables (custom values)
A bundle can declare variables — the “custom values” pattern. Embed{{name}} placeholders anywhere in the bundle’s strings; at apply time they’re substituted with the client’s values.
At apply you pass
variableValues (a name → value map). Anything you omit comes back in the report’s missingVariables[] and is left as a literal {{placeholder}} to fill in later.
The lifecycle
You can author a bundle by hand, or snapshot a live project to capture one automatically. Either way it ends up as a template you can apply (to a new sub-project) or load (into an existing one).Snapshotting a live project
POST /projects/{projectId}/snapshot captures the project’s configuration into a reusable template. Omit include to capture everything; set keys to true to restrict capture to those subsystems.
include allowlist accepts: roles, sidebar, workflows, objectTypes, objectRelations, crmConfig, spineFields, contentTypes, starterContent, branding, projectContext, agents. Live CMS content items are cloned only when starterContent is included. The response returns the new template plus warnings[] — the tenant-specific values that were scrubbed.
You can also tokenize on capture: pass
tokenize (a list of { find, var } replacements) alongside variables to turn literal values in the captured bundle into {{var}} placeholders — so a snapshot of one client becomes a parameterized template for the next.