Skip to main content
This page is the working-example companion to the platform docs. Every cURL block below was actually executed against a live local platform during the 2026-05-02 testing pass — copy, paste, change the IDs, run.

Setup assumptions

  • Platform running at http://localhost:3000 (or substitute your host).
  • A user is signed in; cookies are persisted in kc.txt (see API Authentication for getting a session cookie programmatically). For server-to-server, swap the -b kc.txt for -H "X-API-Key: krn_..." — the endpoints in this walkthrough (connectors, schemas, pipelines) are control-plane, so the key must be minted with the control-plane scope (a default data-plane-only key gets 403; see API Keys).
  • AWS CLI configured with creds that can aws sts assume-role against the role you’re about to create.

1. Create the IAM role the connector will assume

Trust policy lets your local IAM identity assume the role with an external ID; the policy grants minimal S3 access on one bucket.

2. Upload a sample CSV

The example dataset has the columns customer_id, first_name, last_name, email, phone, address, city, state, zip_code, country, signup_date, segment, status, lifetime_value, orders_count.

3. Create the S3 connector via the API

POST /api/v1/connectors with authMethod: "iam_role" triggers the STS AssumeRole + HeadBucket + ListObjectsV2 verification path.
Verify the connection (this exercises STS AssumeRole + HeadBucket end-to-end):

4. Create the schema with customer_id as the primary key

When any field is marked isPrimaryKey: true, the auto-generated id BIGSERIAL column is skipped and your column becomes the table’s PK. See Data Model.
Inspect the resulting Postgres table to confirm customer_id is the PK and there is no auto id column:

5. Create the pipeline IR

The IR has four nodes: sourcetransformvalidatetarget (upsert keyed on customer_id, with the safety bundle: empty-source guard on, validate quarantine to a DLQ table). Note the required irVersion: "1.0" and the errorHandling block.

6. Run the pipeline

Expected response on success:

7. Switch the load mode and re-run

The IR is updated via POST /api/v1/pipelines/:id/ir (creates a new version atomically). Pattern: GET the current IR, mutate the target node, POST the new envelope. Re-upload the source file each run because the source executor archives it after success.

Switch to truncate (with the empty-source guard)

Re-upload the file and run:

Test the empty-source guard

Don’t re-upload the file. The next run produces an empty source, and the runtime aborts truncate BEFORE the destructive statement so your table is preserved:
A warning System Health alert (“Pipeline source had no files”) and an error alert (“Pipeline run failed: Customer File Test”) fire automatically. Read them via GET /api/v1/system-health (see System Health).

Switch to blue_green

Run as before. The runtime loads to ds_customer_new, then issues the atomic 3-step rename (target → target_old, target_new → target, DROP target_old). Empty-source guard applies — same protection as truncate.

Switch to incremental_watermark

The first run reads MAX(signup_date) from the target and writes the high-water to pipeline_watermarks. Subsequent runs only load rows where signup_date > <persisted watermark>. The INSERT and watermark upsert run inside a single Postgres transaction so a failed load rolls back the watermark advance.

Try cdc_mirror

cdc_mirror applies the batch as a change feed: an op column on each source row drives upsert vs delete by the CDC key. Set cdcOpColumn and upsertKey on the target (the target table needs a unique constraint on the key columns):
Each source row’s op value (case-insensitive) selects the action: insert/update/upsert/I/U/c/r upsert on customer_id; delete/D deletes that key. Any other op value fails the run before any DML — unknown ops are never silently skipped, and the op column is never written to the target.

8. Verify rows landed

For lineage:
The lifetime_value arrives as a string (“10576.95”) because the route runs through the safeJson helper that converts Postgres numeric / Decimal to its string form (preserves precision past Number.MAX_SAFE_INTEGER). See Flow Lineage.

9. Read the System Health feed

Every operational error from the run path lands here:
Mark one read:
See System Health for the full API surface (read-all, dismiss, severity / source filtering, retention purge cron).

10. Optional: drive the same pipeline via MCP

The MCP server exposes the same operations as tools. From an MCP-aware client (Claude Desktop, Cursor): Start the server: cd platform && npm run mcp (set KAIREON_API_KEY and KAIREON_TENANT_ID in env). See MCP Flow Server.

Cleanup