metadata.name rather than by id, so the same bundle promotes across dev / stage / prod where ids differ.
What it does
Three routes share the same bundle shape:GET /api/v1/gitops/export— dump the tenant’s current state as YAML or JSON, optionally split into per-resource files.POST /api/v1/gitops/diff— compute the diff between a posted bundle and the live DB without mutating.POST /api/v1/gitops/apply— reconcile a posted bundle into the DB, dry-run by default.
/api/v1/cron/gitops-drift-check runs the diff against a stored snapshot and surfaces drift through the cron schedule configuration. See Cron tier.
V1 reconciler scope — applied through to the database:
unchanged:
Quick start
Round-trip a tenant through git:How it works
Bundle format
A bundle is amanifest plus a flat resources[] array. Each resource follows a Kubernetes-like shape:
apiVersion value is always kaireonai.com/v1. Reconciliation matches by metadata.name, never by id, so the same bundle is promotable across environments.
The
Channel resource spec.status accepts draft, active, paused, or
archived (it defaults to active when omitted). draft is accepted because
Channel.status defaults to draft in the database — without it, a tenant
with any draft channel would export a bundle its own diff/apply endpoints then
reject. Draft channels therefore round-trip cleanly through export → apply.Authentication
Every route requires a tenant identifier on the request. The tenant binds viaX-API-Key (preferred — also used as the rate-limit identifier) or X-Tenant-Id. Missing tenant returns 401; invalid tenant returns 403. The current implementation does not enforce a role gate beyond tenant binding — a viewer-tier API key can apply changes. Production deployments wire role enforcement via an upstream proxy or by extending the route with role-based authorization.
Audit trail
Real applies (dryRun=false) write a single gitops_apply row to the audit trail summarizing { created, updated, unchanged, kinds }. The audit write is best-effort — if it fails the apply still returns success.
Diff-then-apply pattern
POST /api/v1/gitops/diff is the same code path as POST /api/v1/gitops/apply?dryRun=true — the dedicated diff endpoint runs the reconciler in preview mode and returns the same response shape. The dedicated diff endpoint is convenience — no body parsing differences, identical validation.
Reference
POST /api/v1/gitops/apply
Reconciles a posted bundle into the tenant’s DB. Dry-run by default.
Body formats accepted
The route reads the raw request body and dispatches based onContent-Type:
Content-Type: application/yaml(or any value containingyaml) — body is parsed as YAML.Content-Type: application/jsonwith a top-levelyamlTextfield — the field is unwrapped and YAML-parsed.Content-Type: application/jsonwith a bare bundle JSON object — parsed as JSON.- Any other content type — parsed first as JSON, then as YAML on JSON failure.
400 Invalid bundle: Empty request body.
Query parameters
string
default:"true"
When
"false", mutations are committed and an audit row is written. Any other value (including absent) is treated as a dry run.string
default:"false"
Reserved — when
true, the reconciler deletes resources that exist in DB but not in the bundle. Accepted as a query parameter today but not enforced for every kind in V1.string
Comma-separated kind filter — only the listed kinds are reconciled. Valid values:
Response
Body shape:boolean
Mirrors the
dryRun query parameter — true for a preview, false when changes were committed.array
Per-resource diff. Each entry is
{ kind, name, op, changedFields?, before?, after? }. op is one of "create" | "update" | "delete" | "unchanged".object
Counts across all diffs:
{ created, updated, deleted, unchanged }.Status codes
POST /api/v1/gitops/diff
Computes the diff against the live DB without mutating. Same body formats as apply.
Query parameters
string
Same comma-separated filter as
apply. Other apply-only flags (dryRun, prune) are not read by this route.Response
Same response shape asapply with dryRun: true always.
Status codes
GET /api/v1/gitops/export
Dumps the tenant’s current decisioning state as a bundle.
Query parameters
string
default:"yaml"
yaml (default) or json.string
default:"bundle"
bundle (default) returns one document. files returns a map of relative path → file content, suitable for writing to disk for git review.Response headers
bundle layout sets Content-Disposition: attachment; filename="kaireon-export-<tenantId>.<ext>". YAML responses use Content-Type: application/yaml.
Response body
Bundle layout (default) — a single YAML or JSON document with the bundle shape:files map plus the original manifest:
Status codes
Required headers
Configuration
Drift detection
/api/v1/cron/gitops-drift-check runs the diff against a stored bundle snapshot on a schedule defined in your Helm values:
File layout for git
Recommended directory structure when usinglayout=files:
Honest limits
- Reconciler scope in V1 covers five kinds: Category, Channel, Offer, DecisionFlow, and RankingProfile. The other recognized kinds (SubCategory, Creative, QualificationRule, ContactPolicy) parse and validate but always report as
unchanged— apply support for those kinds is on the roadmap. - The current routes enforce tenant binding but not a role gate. A viewer-tier API key can call apply with
dryRun=false. Production deployments either wire role enforcement upstream or extend the route to require an admin or editor role. prune=trueis accepted as a query parameter, but full prune-by-kind enforcement is not yet implemented for every kind. Treat prune as opt-in and verify behavior per kind before enabling.- The audit-log write inside the apply path is best-effort and silenced on failure. The reconciliation itself still commits — if the audit row matters for compliance, monitor audit-log ingestion separately.
- An empty request body is rejected with
400 Invalid bundle: Empty request body, but a body containing only{}will pass through to the YAML parser fallback and surface a less specific error. Senders should always include the fullmanifest + resourcesshape.
Related
- Cron tier — runs the daily
/api/v1/cron/gitops-drift-check. - Audit Logs —
gitops_applyaction rows record every real apply. - Decision Flows — one of the reconciler-supported resource kinds.
- Offers — one of the reconciler-supported resource kinds.