# Agents Source: https://docs.streamkap.com/agents Connect AI agents to Streamkap to monitor pipeline health, troubleshoot issues, manage sources and destinations, and automate CDC operations. Streamkap provides tools that enable AI agents to interact with your CDC infrastructure and build on top of it. Agents can monitor pipeline health, troubleshoot issues, manage resources, and automate operational tasks — through natural language, CLI, or programmatic interfaces. Not to be confused with [Streaming Agents](/streaming-agents), which run *inside* Streamkap and process records on your Kafka topics with an LLM. **This page covers connecting external AI assistants to operate your Streamkap infrastructure.** ## What Agents Can Do Connect an agent to Streamkap and ask it to: * *"Give me an overview of my infrastructure and flag anything unhealthy"* * *"Check the logs for errors in the last hour and diagnose the root cause"* * *"Create a new pipeline from my PostgreSQL source to Snowflake"* * *"Show me consumer group lag for all my pipelines"* * *"Pause all sources tagged as 'staging'"* * *"What schemas are available in my schema registry?"* * *"Export my billing usage for the last month"* Agents have access to the full Streamkap API surface — sources, destinations, pipelines, transforms, topics, schemas, logs, metrics, alerts, and more. ## Integration Paths | Integration | Best For | How It Works | | ---------------------------------------------------------- | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **[MCP Server](/mcp-server)** | AI agents in IDEs and chat interfaces | Connects to Claude, Cursor, Windsurf, VS Code Copilot, and other MCP-compatible clients. Agents call Streamkap tools through natural language. | | **[CLI](/cli)** | Scripts, CI/CD, and agent pipelines | Outputs JSON when piped and skips confirmations in non-interactive mode — designed for agents to invoke directly. | | **[REST API](/api-reference/authentication/access-token)** | Custom integrations and direct API calls | Full programmatic access to all Streamkap operations. Use when building custom tooling or integrating with platforms that don't support MCP. | | **[Terraform](/streamkap-provider-for-terraform)** | Infrastructure as code | Declare your Streamkap resources in HCL. Agents can generate, review, and apply Terraform configurations. Also works with [Terraform MCP servers](https://developer.hashicorp.com/terraform/mcp-server) for agent-driven infrastructure management. | ### Credentials All integration paths require authentication. Choose based on how you connect: * **OAuth sign-in** (hosted MCP Server) — authorize with your Streamkap account in the browser. No keys to create or store; the simplest way to connect Claude and other MCP clients. See [Sign in with OAuth](/mcp-server#sign-in-with-oauth-remote). * **[API Tokens](/api-tokens)** — standalone Client ID and Secret for the CLI, REST API, and header-authenticated MCP. * **[Project Keys](/project-keys)** (recommended for scoped agents) — unified credential files that bundle API access, Kafka access, and MCP tool scoping. Project Keys support [tool scoping](/project-keys#step-3-mcp-scoping) — you can restrict which MCP tools an agent can call without changing your server configuration. ## MCP Server The [MCP Server](/mcp-server) is the primary way to connect AI agents to Streamkap. It exposes tools covering every Streamkap API operation. ### Setup Connect to the hosted server with browser-based [OAuth sign-in](/mcp-server#sign-in-with-oauth-remote) (no API keys), pass [header credentials](/mcp-server#setup-modes), or run it [locally via npx](/mcp-server#setup-modes) — with setup guides for all major AI clients. See [MCP Server](/mcp-server) for detailed instructions. ### Documentation MCP Add the docs server — `https://docs.streamkap.com/mcp` — to any MCP client so your agent can search the Streamkap documentation as it works: connector configs, how-tos, and API reference. It's credential-free and read-only, and pairs well with the MCP Server above. See [Documentation MCP](/mcp-server#documentation-mcp) for client setup. ## CLI for Agents The [CLI](/cli) is designed to work in agentic pipelines alongside interactive use: * **Auto-JSON output** — detects when output is piped (non-TTY) and switches to JSON automatically * **No confirmation prompts** — destructive commands execute without confirmation in non-interactive mode * **Quiet mode** — `--quiet` suppresses all non-data output for clean parsing ```bash theme={null} # Auto-JSON when piped — no flag needed streamkap sources list | jq '[.result[] | select(.connector_status == "Broken") | .name]' ``` ## Getting Started 1. **Choose authentication** — hosted MCP [OAuth sign-in](/mcp-server#sign-in-with-oauth-remote) (no keys), an [API Token](/api-tokens), or a [Project Key](/project-keys) (recommended for scoped agents — supports tool scoping) 2. **Pick your integration** — [MCP Server](/mcp-server) for AI agents, [CLI](/cli) for scripts, [REST API](/api-reference/authentication/access-token) for custom tooling 3. **Connect and go** — setup is quick for any integration path # Alerts Source: https://docs.streamkap.com/alerts Monitor and receive real-time notifications for pipeline and connector events Alerts provide real-time notifications for critical events in your Streamkap environment, including pipeline state changes, connector actions, latency issues, and system events. Configure delivery channels and customize which alerts you receive to stay informed about your data infrastructure. ## Overview The Alerts page displays a chronological log of all events and notifications in your Streamkap service: ## Table Columns The Alerts table displays the following information: * **Name**: The resource name (connector, pipeline, service) that triggered the alert * Click the name to navigate to the resource detail page * Green names with dots indicate unread alerts * **Type**: The resource type (Source, Destination, Pipeline, Service, etc.) * **Issue**: A brief description of the alert event with an information icon * Click to expand and view full details * Common events: created, updated, deleted, stopped, resumed, broken, running * **Date**: When the alert was triggered * Relative time (e.g., "1 days ago") * Absolute timestamp (e.g., "October 3rd at 7:06 PM") * **Actions**: Envelope icon to toggle read/unread status * Click the envelope icon to mark an unread alert as read * Click the envelope icon to mark a read alert as unread ## Quick Actions At the top of the page: * **Search**: Filter alerts by searching issue text * **Filter by tags**: Dropdown to filter by tags (Select All, Sources, Destinations, Pipelines) * **Show only unread**: Checkbox to display only unread alerts * **Show archived**: Checkbox to display only archived alerts * **Mark all as read**: Button to mark all visible alerts as read * **Alerts Settings**: Opens the configuration modal to manage integrations and preferences ## Alerts Configuration Click the **Alerts Settings** button to open the configuration modal with two tabs: Settings and Preferences. ### Settings Tab Configure integration channels to receive alerts outside the Streamkap app: Available integrations: **EMAIL** * Status: Connect to configure email notifications * Setup: Enter email address(es) to receive alerts **SLACK** * Status: Connect via OAuth * Setup: 1. Click **Connect** Slack button 2. You’ll be redirected to Slack, where you can select the channel or user you want to receive the notification. 3. After clicking the **Allow** button, you’ll be redirected to the Alert settings page showing the connected channel or user. 4. You can connect another channel or user by clicking **Add Channel** button. **GRAFANA** * Status: Example shows "Disconnect" button when connected * Setup: 1. Go to **Grafana** 2. Navigate to **Alerts & IRM -> Alerting -> Contact points**. 3. Click **+ Add contact point**. 4. Enter a name for the contact point. 5. From the **Integration** list, select **Webhook**. 6. In the **URL** field, copy in your Webhook URL. 7. (Optional) Configure additional settings. 8. Click **Save contact point**. 9. Copy **Webhook URL** from **Grafana** and paste it into the **Webhook URL** field in the Alert Settings page, then click **Save**. * Data format: ``` { "data": { "action_type": "stopped", "datetime": "2026-01-14 21:53:06", "entity_id": "685d6baefd035ce5558ad92f", "entity_name": "example-source-mongodb", "entity_type": "sources", "message": "Source: example-source-mongodb has been stopped at 2026-01-14 21:53:06", "pending_datetime": "2026-01-14 21:53:06", "state": "TRIGGERERD", "tenant_id": "c115d79d-5aef-4f6f-8185-a15901c993f8", "threshold_value": "None", "value": "None" } } ``` **SPLUNK** * Status: The system will call the **/services/collector/event** to send data. * Setup: 1. Go to **Splunk** 2. Navigate **Settings > Data inputs** and click **HTTP Event Collector**. 3. Click **New Token** button. 4. In the **Input Settings** step, do not need to add any index. 5. Click **Submit** button. 6. Copy the **Token Value** and paste it into the **Splunk Token** field in the Alert Setting page 7. Enter the **HEC endpoint** (Please use the https with port 443) and click **Save** button. * Data format: ``` { "event": "Destination: Test anil 4 has been resumed", "fields": { "action_type": "resumed", "datetime": "2026-01-07 14:46:46", "entity_id": "67e41d2daea865c2ce982b55", "entity_name": "Test anil 4", "entity_type": "destinations", "pending_datetime": "2026-01-07 14:46:46", "state": "TRIGGERERD", "tenant_id": "82d4a52a-987a-41ab-b199-ac02718a918f", "threshold_value": "None", "value": "None" }, "source": "Streamkap" } ``` **DATADOG** * Status: The system will call the **Event v2 API** to send data. * Setup: 1. Go to **Datadog**. 2. Navigate **User > API keys** and click **New Key** and create a new **API Key**. 3. Navigate **User > Application keys** and click **New Key** and create a new **Application Key** with: * **Scope**: `events_read` * **Actions API Access**: `Enabled` 4. Enter your **Datadog site (copy it from your Datadog page URL)**, **API Key**, and **Application Key**. 5. Click **Save**. * Data format: ``` { "data": { "attributes": { "attributes": { "custom": { "action_type": "resumed", "datetime": "2026-01-07 14:46:46", "entity_id": "67e41d2daea865c2ce982b55", "entity_name": "Test anil 4", "entity_type": "destinations", "pending_datetime": "2026-01-07 14:46:46", "state": "TRIGGERERD", "tenant_id": "82d4a52a-987a-41ab-b199-ac02718a918f", "threshold_value": "None", "value": "None" }, "status": "warn" }, "category": "alert", "host": "app.streamkap.com", "message": "Destination: Test anil 4 has been resumed at 2026-01-07 14:46:46", "tags": [ "source:streamkap" ], "title": "Destination: Test anil 4 has been resumed" }, "type": "event" } } ``` **PAGERDUTY** * Status: The system will call the **Event v2 API** to send data. * Setup: 1. Go to **PagerDuty**. 2. Navigate **Integration > Service integrations** and click **+ New Serivce** to create a new service. 3. In the **Integration** step, you must to choose the **Events API V2**, then click **Create Service** button. 4. Copy the **Integration Key** and paste it into the **Integration Key** field in the Alert Setting page 5. Click **Save**. * Data format: ``` { "payload": { "summary": "Destination: Test anil 4 has been resumed at 2026-01-07 14:46:46", "source": "https://app.streamkap.com", "severity": "error", "custom_details": { "action_type": "resumed", "datetime": "2026-01-07 14:46:46", "entity_id": "67e41d2daea865c2ce982b55", "entity_name": "Test anil 4", "entity_type": "destinations", "pending_datetime": "2026-01-07 14:46:46", "state": "TRIGGERERD", "tenant_id": "82d4a52a-987a-41ab-b199-ac02718a918f", "threshold_value": "None", "value": "None" } } } ``` **CUSTOM WEBHOOK** * Type: Webhook * Status: Connect to configure * Setup: Provide incoming webhook URL and the authentication * Available authetication: * NONE * BASIC: Need to provide username and password * BEARER: Need to provide the bearer token Each integration can be connected or disconnected independently. When connected, you'll see a "Disconnect" button and the webhook URL (for webhook-based integrations). ### Preferences Tab Configure which alert types you want to receive and through which channels: Alert categories and their enabled channels: **Destination Actions** * Events: Destination connector created, updated, deleted, stopped, resumed * Default channels: (expand to configure) **Pipeline Actions** * Events: Pipeline created, updated, deleted, stopped, started * Enabled channels: Email **Recovery - Pipeline Is Running** * Events: Pipeline recovered from error state and is now running * Enabled channels: Email, In App, Chat & Webhook **Recovery - Pipeline Latency less than 10 minutes** * Events: Pipeline recovered from latency less than 10 minutes * Enabled channels: Email, In App, Chat & Webhook **Source Actions** * Events: Source connector created, updated, deleted, stopped, resumed * Enabled channels: Email, In App, Chat & Webhook **Triggered - Pipeline Dead Letter Queue** * Events: Messages sent to dead letter queue due to processing errors * Enabled channels: Email, In App, Chat & Webhook * Critical alert for data quality issues **Triggered - Pipeline JSON Column** * Events: Destination has any json columns * Enabled channels: Email, In App, Chat & Webhook * Critical alert for data quality issues **Triggered - Pipeline Latency greater than 10 minutes** * Events: Pipeline processing latency exceeds threshold * Enabled channels: Email, In App, Chat & Webhook * Important for monitoring real-time performance **Triggered - Pipeline Not Running** * Events: Pipeline stopped unexpectedly or failed to start * Enabled channels: Email, In App, Chat & Webhook * Critical alert for system availability Click any alert category to expand and customize which channels receive notifications for that specific alert type. ## Setting Up Alerts Click **Alerts** in the sidebar to view your alerts. Click the **Alerts Settings** button in the top right corner. In the **Settings** tab: * Click **Connect** next to each integration you want to enable * Follow the setup wizard for each channel: * Email: Enter email addresses * Slack: Authorize OAuth connection * Teams/Discord/Mattermost/Grafana: Provide webhook URLs * Test the connection to ensure proper configuration Switch to the **Preferences** tab: * Click on each alert category to expand it * Select which channels should receive each type of alert * Enable channels by checking the boxes (Email, In App, Chat & Webhook) * Disable alerts you don't need by unchecking all channels Your changes are saved automatically as you configure each setting. ## Alert Types ### Connector Events **Source and Destination Actions** * Created * Updated (configuration changed) * Deleted * Stopped/paused * Resumed/started **Runtime Recovery Alerts** * [MongoDB resume token recovered](#mongodb-resume-token-recovery) — connector auto-recovered from an unresumable change-stream position; events in a known window may be missing. ### Pipeline Events **State Changes** * Created * Updated (settings modified) * Deleted * Started * Stopped * Broken (encountered error) * Recovered (returned to running state) **Performance Alerts** * Pipeline latency exceeds 10 minutes * Pipeline dead letter queue triggered * Pipeline not running (unexpected stoppage) ### Service Events * Service configuration changes * Service status updates * Resource quota alerts ## MongoDB Resume Token Recovery This alert fires when a MongoDB source connector auto-recovers from an unresumable change-stream position. It is informational, not a failure — the connector has continued streaming — but it indicates that **events in a known time window may not have reached the destination**. ### When it fires The alert fires after the connector encounters one of: * `ChangeStreamFatalError` (error code 280) * `InvalidatedResumeTokenError` (error code 260) * `ChangeStreamHistoryLost` (error code 286) …and the recovery option is enabled on the connector (`streamkap.recovery.invalid.resume.token = skip_to_latest`). Instead of failing the task, the connector discards the unresumable position, reopens the change stream from the current end of the oplog, and emits this alert so you know events in the gap window may be missing. These conditions typically arise from: * **`updateLookup` against a deleted document.** An update event references a document that is deleted before the change stream's resume token can be persisted. MongoDB then rejects any attempt to resume from that token. * **Oplog history loss.** The connector was offline or behind long enough that the resume position fell out of the oplog retention window. ### Payload Every channel (in-app, email, Slack, webhook) receives the same data envelope. Key fields: | Field | Description | | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `entity_name` | Connector name | | `entity_id` | Connector ID (links to the connector page in the Streamkap app) | | `recovery_attempt` | Consecutive recovery attempt number (resets to 1 on healthy streaming) | | `error_code` | MongoDB error code (`280` / `260` / `286`, or `-1` if matched by message text) | | `error_message` | Truncated MongoDB server response | | `gap_start` | `YYYY-MM-DD HH:MM:SS` — last successful resume position before the failure. May be `(unknown)` if the failure happened before any event was processed in the current task lifetime. | | `gap_end` | `YYYY-MM-DD HH:MM:SS` — wall-clock time when recovery skipped to the latest oplog position | | `datetime` | `YYYY-MM-DD HH:MM:SS` — alert timestamp | Events whose oplog timestamp falls between `gap_start` and `gap_end` may be missing from the destination. Webhook consumers (PagerDuty, Splunk, Datadog, generic) receive the rendered `message` plus all the fields above in the structured payload, ready for filtering or alert routing on the consumer side. ### What to do when this alert fires 1. **Identify affected data**. The gap window (`gap_start` → `gap_end`) tells you which time range in the source may have under-delivered to the destination. The connector itself continues streaming normally after the alert fires. 2. **Decide whether to replay**. For collections with strict completeness requirements, trigger a [snapshot](/snapshots) over the affected collections to backfill. For collections where occasional missed updates are acceptable, no action may be required. 3. **Investigate the root cause if alerts are frequent**. Repeated firings on the same connector usually point to either oplog sizing (consider increasing oplog retention in MongoDB) or a workload pattern that combines `updateLookup` with rapid document deletes — discussed further in the MongoDB source connector documentation. ### Configuring the recovery behaviour Two connector properties control the feature: | Property | Default | Effect | | --------------------------------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `streamkap.recovery.invalid.resume.token` | `fail` | Set to `skip_to_latest` to enable auto-recovery. `fail` preserves the standard fail-fast behaviour. | | `streamkap.recovery.max.consecutive.attempts` | `-1` (unlimited) | Circuit breaker. After this many consecutive recoveries with no successful events between them, the connector falls through to FAILED state. `0` trips immediately (equivalent to `fail`); `> 0` trips after N. | These properties are configured at the connector level. Contact Streamkap support if you'd like recovery enabled on your MongoDB connectors. ## Alert Channels ### In-App Notifications All alerts appear in the Alerts page within the Streamkap app. Unread alerts are indicated with green text and a dot indicator. ### Email Notifications Receive alerts via email to configured addresses. Emails include: * Alert title and severity * Resource name and type * Event description * Timestamp * Direct link to resource in Streamkap ### Slack Integration Post alerts to Slack channels with rich formatting: * Colored indicators based on severity * Resource details and links * Inline actions (when applicable) * Thread support for related alerts ### Webhook Integrations Teams, Discord, Mattermost, and Grafana receive alerts via webhooks with structured payloads including: * Event metadata * Resource information * Timestamp * Severity level * Direct link to Streamkap ## Managing Alerts ### Marking Alerts as Read * Click the envelope icon on an individual alert to toggle its read/unread status * Click **Mark all as read** at the top to mark all visible alerts as read * Unread alerts appear with green text and remain visible when "Show only unread" is enabled ### Filtering Alerts * **Search**: Type in the search box to filter by issue text * **Type**: Use the dropdown to show only specific resource types * **Unread Toggle**: Enable "Show only unread" to hide alerts you've already seen ### Pagination * View 20, 50, or 100 alerts per page using the dropdown * Navigate between pages using Previous/Next buttons or page numbers ## Best Practices 1. **Configure Critical Alerts First**: Set up notifications for pipeline failures, dead letter queues, and high latency alerts 2. **Use Multiple Channels**: Configure both email and chat integrations for redundancy 3. **Customize by Environment**: Use different Slack channels or email lists for production vs. development services 4. **Review Regularly**: Check the Alerts page periodically to identify patterns or recurring issues 5. **Test Integrations**: After setting up webhooks, trigger a test alert to verify delivery 6. **Tune Thresholds**: Adjust latency thresholds and alert frequency to reduce noise while maintaining awareness ## Troubleshooting ### Not Receiving Alerts If alerts aren't being delivered: 1. **Check Integration Status**: Ensure the integration shows "Connected" in Settings tab 2. **Verify Webhook URLs**: Copy and test webhook URLs in a tool like curl or Postman 3. **Check Email Spam**: Email alerts may be filtered as spam initially 4. **Review Preferences**: Confirm the alert type has the correct channels enabled 5. **Test Connection**: Disconnect and reconnect the integration ### Too Many Alerts If you're overwhelmed with notifications: 1. **Disable Low-Priority Alerts**: Uncheck channels for informational alerts in Preferences 2. **Use Filters**: Set up separate channels for different severity levels 3. **Adjust Thresholds**: Consider increasing latency thresholds if appropriate 4. **Mute Specific Types**: Disable alerts for routine operations like connector updates ### Slack Not Posting If Slack integration isn't working: 1. **Re-authorize**: Disconnect and reconnect the Slack integration 2. **Check Permissions**: Ensure the Streamkap app has permission to post in your workspace 3. **Verify Channel**: Confirm the target Slack channel still exists 4. **Test OAuth Token**: OAuth tokens may expire; reconnecting refreshes the token ## Related Documentation * [Logs](/logs) - View detailed system logs for debugging * [Pipelines](/pipelines) - Manage data pipelines * [Sources](/sources) - Configure source connectors * [Destinations](/destinations) - Configure destination connectors * [Projects](/projects) - Manage Streamkap projects # AlloyDB Source: https://docs.streamkap.com/alloydb Stream change data from Google Cloud AlloyDB for PostgreSQL into Streamkap, covering logical replication, the replication user role, and heartbeats. ## Prerequisites * PostgreSQL version ≥ 10 * A database user with sufficient privileges to configure the database, including enabling logical replication and creating users ## PostgreSQL Setup ### 1. Grant Database Access * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your database. **Connecting via AlloyDB Auth Proxy** If you need to connect to your AlloyDB instance using the AlloyDB Auth Proxy, please contact Streamkap support for assistance. The setup and configuration for Auth Proxy access may require additional steps. ### 2. Enable Logical Replication Logical replication is a method of replicating data objects and their changes, based upon their replication identity (usually a primary key). The Connector relies on PostgreSQLs implementation of this. * In the Google Cloud console, go to the Cloud SQL Instances page. * Go to Cloud SQL Instances * Open the more actions menu for the instance you want to enable point-in-time recovery on and click Edit. * Under Customize your instance, expand the Data Protection section. * Select the Enable point-in-time recovery checkbox. * Expand Advanced options. * Enter the number of days to retain logs, from 3-7. We recommend 7 days, * Click Save. **WAL and database storage** The write-ahead logs take up disk space on your database. If you run out of disk space, you may face an interruption to your database. To mitigate this: * Ensure that your database server has ample free space. * Enable sufficient space with auto growth on the database. * Monitor the WAL growth and configure alerts. * Configure the publication (See [Create Publication & Slot](#create-publication--slot)) to contain only tables you need. ### 3. Create Database User It's recommended to create a separate user and role for the Connector to access your PostgreSQL database. Below is an example script that does that. ```sql SQL theme={null} -- Replace { ... } placeholders as required CREATE USER streamkap_user PASSWORD '{password}'; ALTER USER streamkap_user WITH REPLICATION; -- Create a role for Streamkap CREATE ROLE streamkap_role; GRANT streamkap_role TO streamkap_user; -- Grant Streamkap permissions on the database, schema and all tables to capture GRANT CONNECT ON DATABASE "{database}" TO streamkap_role; GRANT CREATE, USAGE ON SCHEMA "{schema}" TO streamkap_role; GRANT SELECT ON ALL TABLES IN SCHEMA "{schema}" TO streamkap_role; ALTER DEFAULT PRIVILEGES IN SCHEMA "{schema}" GRANT SELECT ON TABLES TO streamkap_role; ``` ### 4. Enable Snapshots To backfill your data, the Connector needs to be able to perform snapshots. See [Snapshots & Backfilling](/snapshots) for more information. To enable this feature, there are 2 methods available: #### Method 1: Enable read only connection Requires PostgreSQL version 13 or higher. This method is recommended if you cannot create a table in the source database and grant the Connector read/write privileges to that. * Set **Read only** to **Yes** during Streamkap Setup. No other configuration should be necessary. #### Method 2: Create a table in the source database You will need to create the table and give necessary permissions to the `streamkap_user`. The Connector will use this collection for managing snapshots. Below is an example script that does that. The examples below use `streamkap_signal` as the signal table name, but you can choose any name. During [Streamkap Setup](#3-snapshot-settings), provide the full path to your signal table in `schema.table` format (e.g., `streamkap.streamkap_signal`). ```SQL SQL theme={null} -- Create the schema CREATE SCHEMA streamkap; -- Switch to the newly created schema SET search_path TO streamkap; -- Create the table CREATE TABLE streamkap_signal ( id VARCHAR(255) PRIMARY KEY, type VARCHAR(32) NOT NULL, data VARCHAR(2000) NULL ); -- Grant necessary privileges on the table to the role GRANT CREATE, USAGE ON SCHEMA streamkap TO streamkap_role; GRANT SELECT ON ALL TABLES IN SCHEMA streamkap TO streamkap_role; GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE streamkap_signal TO streamkap_role; ``` **Publications and signal tables** When you create the PostgreSQL publication in the next step, if you choose to specify tables for capture instead of all tables, you **must** include your signal table. ### 5. Heartbeats Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. For read-write connections (when **Read only** is **No** during Streamkap Setup), the Connector writes to the heartbeat table directly. ```SQL SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Switch to the streamkap schema SET search_path TO streamkap; -- Create the heartbeat table with id, text, and last_update fields CREATE TABLE streamkap_heartbeat ( id SERIAL PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Grant permission to the Streamkap user GRANT USAGE ON SCHEMA streamkap TO streamkap_user; GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE streamkap_heartbeat TO streamkap_user; -- Insert the first row into the heartbeat table INSERT INTO streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` **Heartbeat tables and PostgreSQL publications** If the `streamkap_pub` publication created during PostgreSQL Setup was for specific tables e.g. `CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, table3, ...;` instead of `FOR ALL TABLES;`, you **must** add the heartbeat table to the publication: `ALTER PUBLICATION streamkap_pub ADD TABLE streamkap.streamkap_heartbeat;`. For read-only connections (when **Read only** is **Yes** during Streamkap Setup), the Connector cannot write to the heartbeat table directly. Instead, you must configure a scheduled job on the **primary** database to generate artificial traffic. These changes will replicate to the read replica, which the Connector then consumes. **Run these commands on the primary database, not the read replica.** The heartbeat table and scheduled job must be created on the primary database. The changes will automatically replicate to the read replica. The `pg_cron` extension must be allowed in your database's configuration. See your provider's documentation for enabling extensions: Scheduling maintenance with pg\_cron Scheduling maintenance with pg\_cron PostgreSQL extensions PostgreSQL extensions pg\_cron extension pg\_cron extension Supported extensions Once allowed, create the extension: ```sql SQL theme={null} CREATE EXTENSION IF NOT EXISTS pg_cron; ``` ```sql SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Create the heartbeat table CREATE TABLE streamkap.streamkap_heartbeat ( id SERIAL PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Insert the initial row INSERT INTO streamkap.streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` ```sql SQL theme={null} -- Schedule a job to update the heartbeat table every minute SELECT cron.schedule( 'streamkap_heartbeat_job', '*/1 * * * *', $$UPDATE streamkap.streamkap_heartbeat SET text = 'updated_heartbeat', last_update = CURRENT_TIMESTAMP WHERE id = 1;$$ ); ``` Whichever database user is used to create and run the cron jobs (often the `postgres` user or a dedicated cron user) needs appropriate permissions on the heartbeat table. Additionally, the Streamkap user also needs permissions to monitor the heartbeat table. ```sql SQL theme={null} GRANT USAGE ON SCHEMA streamkap TO {cron user}; GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO {cron user}; -- Grant permissions to the Streamkap user for monitoring and diagnostics GRANT USAGE ON SCHEMA streamkap TO streamkap_user; GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO streamkap_user; ``` The `pg_cron` extension stores job execution history in the table `cron.job_run_details`. To prevent this table from growing indefinitely: ```sql SQL theme={null} SELECT cron.schedule( 'streamkap_cron_cleanup', '0 0 * * *', $$DELETE FROM cron.job_run_details WHERE end_time < now() - interval '7 days';$$ ); ``` ```sql SQL theme={null} -- View all scheduled jobs SELECT * FROM cron.job; -- View recent job execution history SELECT * FROM cron.job_run_details ORDER BY start_time DESC LIMIT 10; -- Unschedule a job (replace {jobid} with the actual job ID) SELECT cron.unschedule({jobid}); ``` **Heartbeat tables and PostgreSQL publications** If the `streamkap_pub` publication was created for specific tables (e.g., `CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, ...;`) instead of `FOR ALL TABLES`, you **must** add the heartbeat table to the publication: ```sql SQL theme={null} ALTER PUBLICATION streamkap_pub ADD TABLE streamkap.streamkap_heartbeat; ``` ### 6. Create Publication & Slot Publications contain a set of change events for the tables you want the Connector to capture. * Create a publication for your tables. You can create a publication for all tables or selected tables. ```sql SQL - All Tables theme={null} -- Create a publication for all tables to capture CREATE PUBLICATION streamkap_pub FOR ALL TABLES WITH (publish_via_partition_root = true); ``` ```sql SQL - Selected Tables theme={null} -- Create a publication for specific tables to capture CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, table3, ... WITH (publish_via_partition_root = true); -- Verify the tables to capture were added to the publication SELECT * FROM pg_publication_tables where pubname = 'streamkap_pub'; ``` **Altering publications** You cannot alter `FOR ALL TABLES` publications to include/exclude tables. If you set up a `FOR ALL TABLES` publication and later decide to change that, you have to drop the publication and create another to include specific tables e.g. `CREATE PUBLICATION ... TABLE table1, table2, table3, ...`. However, any change events that occur before the new publication's created will not be included in it, so a snapshot's required to ensure they are not missed by your Streamkap pipelines. You should also stop the [Source](https://app.streamkap.com/connectors/sources) *before* changing the publication. A replication slot represents a stream of change events the Connector reads from. * Create a replication slot. ```SQL SQL theme={null} -- Create a logical replication slot SELECT pg_create_logical_replication_slot('streamkap_pgoutput_slot', 'pgoutput'); -- Verify the replication slot is working (this may take a few moments to return the count) SELECT count(*) FROM pg_logical_slot_peek_binary_changes('streamkap_pgoutput_slot', null, null, 'proto_version', '1', 'publication_names', 'streamkap_pub'); ``` **Replication slot auto-creation** If the Connector cannot find the replication slot named in [Replication Settings](#replication-settings) during Streamkap Setup, it will attempt to create one automatically as it cannot read the database transaction logs without one. This is assuming the database user has been created `WITH REPLICATION` privileges as per [Create Database User](#create-database-user). #### Excluding columns from replication By default a publication streams every column of each captured table. On **PostgreSQL 15+** you can attach a *column list* to a table in the publication so that only the listed columns are replicated — any column you leave out is excluded at the source and never enters the replication stream. ```SQL SQL - Selected Columns theme={null} -- Only the listed columns are published; every other column is excluded. -- Each table's list MUST include its replica identity (primary key), -- otherwise UPDATE/DELETE for that table cannot be replicated. CREATE PUBLICATION streamkap_pub FOR TABLE public.users (id, email, created_at, updated_at), public.orders (id, user_id, status, total), public.products (id, sku, name, price); -- Add or change a table's column list on an existing publication ALTER PUBLICATION streamkap_pub SET TABLE public.users (id, email, updated_at); -- Verify the published columns per table SELECT pubname, schemaname, tablename, attnames FROM pg_publication_tables WHERE pubname = 'streamkap_pub'; ``` * Requires **PostgreSQL 15 or later**. On earlier versions a publication always streams all columns. * Each table's column list **must include the table's replica identity** — its primary key, or the columns from `REPLICA IDENTITY USING INDEX` / `FULL`. Otherwise `UPDATE` and `DELETE` events for that table are not replicated. * Column lists are only valid with `FOR TABLE`, not `FOR ALL TABLES`. A table listed without parentheses publishes all of its columns, so you can mix restricted and unrestricted tables in one publication. A publication column list only governs **streamed (CDC) changes**. The initial snapshot reads columns directly from the table, so an excluded column can still appear in snapshot records. If a column must never be captured, exclude it from the snapshot as well, not only from the publication. *** ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **AlloyDB**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Hostname**: Specify the hostname. * **Port**: Default is `5432`. * **Connect via SSH Tunnel**: The Connector will connect to an SSH server in your network which has access to your database. This is necessary if the Connector cannot connect directly to your database. * See [SSH Tunnel](/ssh-tunnel) for setup instructions. * **Username**: Username to access the database. By default, Streamkap scripts use `streamkap_user`. * **Password**: Password to access the database. * **Database**: Specify the database to stream data from. * **Read only**: Whether or not to use a read-only connection. Requires PostgreSQL version 13 or higher. * When connecting to a read replica, set this to **Yes** to support Streamkap snapshots. * **Heartbeats**: Enabled by default. * For **read-write** connections, configure a heartbeat table in the source database and set **Heartbeat Table Schema**. See [Heartbeats](#5-heartbeats) for setup instructions. * For **read-only** connections, configure a scheduled heartbeat job on the primary database using `pg_cron`, and include the heartbeat table in [Schema and Table Capture](#6-schema-and-table-capture). See [Heartbeats](#5-heartbeats) for setup instructions. ### 3. Snapshot Settings If you set Read only to **No**, you will need to create a snapshot signal table and give permissions to the `streamkap_user`. See [Enable Snapshots](#enable-snapshots) for setup instructions. * **Signal Table**: Full path to the signal table including schema and table name (e.g., `streamkap.streamkap_signal`). This table is used for incremental snapshotting. See [Enable Snapshots](#4-enable-snapshots) for setup instructions. ### 4. Replication Settings * **Replication Slot Name**: The name of the replication slot for the connector to use. Default is `streamkap_pgoutput_slot`. * **Publication Name**: The name of the publication for the connector to use. Default is `streamkap_pub`. ### 5. Advanced Parameters * **SSL mode**: Whether to use an encrypted connection to the PostgreSQL server. By default, it's required. * **Prefix with Database Name?**: Changes the format of topics to `DatabaseName_TopicName` * **Represent binary data as**: Specifies how the data for binary columns e.g. `blob`, `binary`, `varbinary` should be interpreted. Your destination for this data can impact which option you choose. Default is `bytes`. Click **Next**. ### 6. Schema and Table Capture * **Add Schemas/Tables**: Specify the schema(s) and table(s) for capture * You can bulk upload here. The format is a simple list of schemas and tables, with each entry on a new row. Save as a `.csv` file without a header. **CDC only captures base tables, not Views** Change Data Capture reads the PostgreSQL write-ahead log (WAL), which only records changes to physical tables. Database Views are query-time computations with no physical storage—they don't generate WAL entries. **What you cannot capture:** Views, temporary tables, unlogged tables, foreign tables (FDW), or system tables (information\_schema, pg\_catalog). **Solution:** Specify only the underlying base tables that feed your views. You can recreate the view logic in your destination or transformation layer. **Generated columns are not kept up to date** PostgreSQL [generated columns](https://www.postgresql.org/docs/current/ddl-generated-columns.html) (defined with `GENERATED ALWAYS AS (...) STORED`) are calculated by the database from other columns. PostgreSQL does **not** include them in the replication stream Streamkap reads, so live streaming cannot keep their values current. **We recommend excluding generated columns from capture.** In the Source's **Settings → Advanced** section, set **Column Selection Mode** to **Exclusion** and add the column to the **Column Exclusion List** (fully-qualified, e.g. `schema.table.column`). If you need the value at your destination, calculate it there — or with a [transform](/transforms) — from the columns it is derived from. **Snapshots:** An incremental snapshot (the watermarked **Filtered** and **Full** options) of a table that contains a generated column will **fail** unless the column is excluded. **Blocking** and **Parallel** snapshots are not affected, though any value they capture will become stale because streaming cannot update it. See [Snapshots & Backfilling](/snapshots). PostgreSQL 18 and later can optionally stream stored generated columns when the publication is created with `publish_generated_columns = stored`; earlier versions cannot. Click **Save**. ## Troubleshooting When upgrading the PostgreSQL database used by Streamkap, there are specific steps to prevent data loss and ensure continued operation. Streamkap handles network failures and outages well. If a monitored database stops, the connector resumes from the last recorded log sequence number (LSN) once communication is restored. It retrieves this offset and queries PostgreSQL for a matching LSN in the replication slot. A replication slot is required for change capture, but PostgreSQL removes slots during upgrades and doesn't restore them. When the connector restarts, it requests the last known offset, but PostgreSQL cannot return it. Creating a new replication slot isn't enough to prevent data loss. New slots only track changes from their creation point and lack earlier offsets. The connector fetches its last known offset from Kafka but can't retrieve corresponding data from the new slot. It skips older change events and resumes from the latest log position, causing silent data loss with no warnings. ### Procedure Follow these steps to minimize data loss. Note that a few steps may require support from Streamkap. We recommend notifying us about your database upgrade ahead of time to ensure you have the necessary support. * Using your database's upgrade procedure, ensure writes to it have stopped. * Allow the connector to capture all change events before starting the upgrade procedure. Ask Streamkap to confirm this for you. **Interrupting change data capture** If all the events were not captured before stopping the Source and upgrading the database, you can perform a snapshot (after the procedure is completed) in Streamkap for that Source to ensure no change events were missed. * Assuming all events are captured, stop the Source in the Streamkap app. This flushes the last records and saves the last offset. * Stop the database and upgrade it using your upgrade procedure. Once the database is upgraded, and **before** allowing writes again: * Recreate the logical replication slot, otherwise, Streamkap will miss changes. See [Create Publication & Slot](#create-publication--slot). * Verify the publication for Streamkap exists; recreate it if necessary: [Create Publication & Slot](#create-publication--slot). **Update the Source in the Streamkap app** If you have chosen different names for the logical replication slot or publication, make sure to update them in the Streamkap setup page for the relevant PostgreSQL Source. In this case, contact Streamkap, as they may need to reset your Connector's offsets in this scenario. * Restore write access to the database. * Resume or restart the Source in the Streamkap app. Introduced in PostgreSQL 13, the `REPLICA IDENTITY` table setting controls what data is logged for row updates and deletes. By default, only the primary key and Streamkap metadata column values are retained for deleted records. All other columns will be empty. This leaves you with an incomplete record. If you require - for auditing and historical tracking purposes - **all** column values for deleted records, or if your deletion strategy for your destination is 'soft deletes' (retain the deleted record with a deletion flag), you should set the `REPLICA IDENTITY` to `FULL` for all capture tables. ```sql SQL theme={null} ALTER TABLE {table} REPLICA IDENTITY FULL; ``` This ensures complete data retention. For capturing partitioned tables, it's essential to enable `publish_via_partition_root` on the publication. By default, changes to partitions are published from the partition itself. The connector expects changes to come from the root table. To ensure compatibility and consistent replication, enable this setting: `ALTER PUBLICATION streamkap_pub SET (publish_via_partition_root = true);` # Amazon RDS Aurora MySQL Source: https://docs.streamkap.com/amazon-rds-aurora-mysql Configure MySQL change data capture from Amazon RDS Aurora to Streamkap, including binlog parameters, replication user permissions, and heartbeat setup. ## Prerequisites * MySQL version ≥ 5.7 * MySQL [binlog](https://dev.mysql.com/doc/refman/8.0/en/binary-log.html) enabled * A database user with sufficient privileges to configure the database, including enabling binary logging and creating users The following table summarizes all permissions the Streamkap user needs. The setup steps on each source connector page walk through granting each one. **Core permissions (always required):** | Permission | Scope | Purpose | | -------------------- | ------------ | -------------------------------------------------------- | | `REPLICATION CLIENT` | `*.*` | Read binlog metadata and positions | | `REPLICATION SLAVE` | `*.*` | Read binlog events for CDC streaming | | `RELOAD` | `*.*` | Flush operations required for consistent snapshots | | `SHOW DATABASES` | `*.*` | Discover available databases and schemas | | `SELECT` | `{schema}.*` | Read table data during initial and incremental snapshots | **Snapshot signal table (required if GTID is not enabled):** | Permission | Scope | Purpose | | ---------- | ---------------------------- | -------------------------- | | `SELECT` | `streamkap.streamkap_signal` | Read signal table state | | `INSERT` | `streamkap.streamkap_signal` | Trigger snapshot signals | | `UPDATE` | `streamkap.streamkap_signal` | Update signal table state | | `DELETE` | `streamkap.streamkap_signal` | Clean up processed signals | **Heartbeat table (required if heartbeats are enabled):** | Permission | Scope | Purpose | | ---------- | ------------------------------- | ------------------------------ | | `SELECT` | `streamkap.streamkap_heartbeat` | Read heartbeat state | | `INSERT` | `streamkap.streamkap_heartbeat` | Write heartbeat records | | `UPDATE` | `streamkap.streamkap_heartbeat` | Update heartbeat timestamps | | `DELETE` | `streamkap.streamkap_heartbeat` | Clean up old heartbeat records | If GTID is enabled (default on Google Cloud SQL), the signal table is not required. If using **read-only** mode with GTID enabled, the signal table permissions are not needed. The heartbeat table still requires permissions for monitoring and diagnostics. **Combined GRANT statements:** ```sql SQL theme={null} -- Core permissions (always required) GRANT REPLICATION CLIENT, RELOAD, SHOW DATABASES, REPLICATION SLAVE ON *.* TO 'streamkap_user'@'%'; GRANT SELECT ON {schema}.* TO 'streamkap_user'@'%'; -- Signal table (if GTID is not enabled) GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_signal TO 'streamkap_user'@'%'; -- Heartbeat table (if heartbeats are enabled) GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO 'streamkap_user'@'%'; ``` Amazon Aurora MySQL uses standard MySQL `GRANT` statements for user permissions. Binary logging is configured separately via Aurora DB cluster parameter groups (see step 2 below). Use the **cluster endpoint** to connect, not reader or instance endpoints. ## MySQL Setup ### 1. Grant Database Access * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your database. ### 2. Configure Binary Logging Binary logging records all changes to your database tables. The Connector relies on MySQL's implementation of this. * Open the Amazon RDS console at [https://console.aws.amazon.com/rds/](https://console.aws.amazon.com/rds/). * In the navigation pane, choose **Parameter groups**. **Default parameter groups can't be modified** If the DB instance is using a default parameter group, create a new one: * Choose **Create parameter group**. * Enter a **Parameter group name** and **Description**. * For **Engine type**, choose your database engine. * For **Parameter group family**, choose a DB parameter group family. * Choose **Create**. * Select the parameter group to edit. * Choose **Edit** from **Parameter group actions**. * Set `binlog_format` to `ROW`. * Set `binlog_row_image` to `Full`. * Choose **Save changes**. If you created a new parameter group, associate it with your DB instance: * In the navigation pane, choose **Databases** and select the target DB instance. * Choose **Modify**. * Change the **DB parameter group** setting. * Choose **Continue** and review modifications. * On the confirmation page, choose **Modify DB Instance**. A reboot is required to apply the changes. ### 3. Set Binary Log Retention Period * Connect to your master database with your SQL tool. * View current settings with `CALL mysql.rds_show_configuration;` * If less than 24 hours or null, run `CALL mysql.rds_set_configuration('binlog retention hours', 72);` ### 4. Verify Binary Logs Are Enabled You can verify using any of these methods: * Check the parameter group for the DB instance and that `log_bin` parameter is `ON` * Run the following SQL query on the DB instance `SHOW VARIABLES LIKE '%log_bin%';`. Result should be `ON` * Run `SHOW BINARY LOGS` ### 5. Create Database User It's recommended to create a separate user and role for the Connector to access your MySQL database. Below is an example script that does that. ```SQL SQL theme={null} -- Replace { ... } placeholders as required -- Identify version SHOW VARIABLES LIKE 'VERSION'; -- On MySQL version 5.x to 8.0 CREATE USER 'streamkap_user'@'%' IDENTIFIED BY '{password}'; -- On MySQL version 8.0+ CREATE USER 'streamkap_user'@'%' IDENTIFIED WITH mysql_native_password BY '{password}'; -- Grant Permissions GRANT REPLICATION CLIENT, RELOAD, SHOW DATABASES, REPLICATION SLAVE ON *.* TO 'streamkap_user'@'%'; -- Grant Select on all schemas needed GRANT SELECT ON {schema}.* TO 'streamkap_user'@'%'; ``` ### 6. Enable Snapshots To backfill your data, the Connector needs to be able to perform snapshots. See [Snapshots & Backfilling](/snapshots) for more information. To enable this feature, there are 2 methods available: #### Method 1: Enable GTID (Recommended) This method is recommended if you cannot create and grant Streamkap read/write privileges on a 'signal' table (method 2) for any reason. It's the equivalent of a 'read only' connection. GTIDs only available in MySQL version ≥ 5.6.5 Global transaction identifiers (GTIDs) uniquely identify transactions that occur on a server within a cluster. Though not required, using GTIDs simplifies replication and enables you to more easily confirm if primary and replica servers are consistent as well as carry out incremental snapshots. Set up following these instructions. Ensure you follow the guide for your version: [MySQL Replication GTID - Configuring Aurora](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/mysql-replication-gtid.html#mysql-replication-gtid.configuring-aurora) and that `GTID` mode is `ON`. #### Method 2: Create a table in the source database If you cannot enable GTID mode, you will need to create the table and give permissions to the `streamkap_user`. The Connector will use this collection for managing snapshots. The examples below use `streamkap_signal` as the signal table name, but you can choose any name. During [Streamkap Setup](#3-snapshot-settings), provide the full path to your signal table in `database.table` format (e.g., `streamkap.streamkap_signal`). ```SQL SQL theme={null} -- Create the schema CREATE SCHEMA streamkap; CREATE TABLE streamkap_signal ( id VARCHAR(255) PRIMARY KEY, type VARCHAR(32) NOT NULL, data VARCHAR(2000) NULL ); GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_signal TO 'streamkap_user'@'%'; ``` ### 7. Heartbeats Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. For read-write connections (when **Read only** is **No** during Streamkap Setup), the Connector writes to the heartbeat table directly. ```SQL SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Switch to the streamkap schema USE streamkap; -- Create the heartbeat table with id, text, and last_update fields CREATE TABLE streamkap_heartbeat ( id INT AUTO_INCREMENT PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Grant permission to the Streamkap user GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO 'streamkap_user'@'%'; -- Insert the first row into the heartbeat table INSERT INTO streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` For read-only connections (when **Read only** is **Yes** during Streamkap Setup), the Connector cannot write to the heartbeat table directly. Instead, you must configure a scheduled job on the **primary** database to generate artificial traffic. These changes will replicate to the read replica, which the Connector then consumes. **Run these commands on the primary database, not the read replica.** The heartbeat table and scheduled event must be created on the primary database. The changes will automatically replicate to the read replica. The MySQL Event Scheduler flag must be enabled on your database (`event_scheduler=ON`). See your provider's documentation: Modifying parameters in a DB parameter group in Amazon RDS Aurora MySQL configuration parameters reference Configuring database flags Server parameters Check if the event scheduler is enabled: ```sql SQL theme={null} SHOW VARIABLES WHERE VARIABLE_NAME = 'event_scheduler'; ``` **Making this setting persistent** For self-hosted MySQL, add `event_scheduler = ON` to your MySQL configuration file (usually `my.cnf` or `my.ini`) in the `[mysqld]` section. For managed databases (RDS, Cloud SQL, Azure), configure the `event_scheduler` parameter through your provider's console or CLI. ```sql SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Create the heartbeat table CREATE TABLE streamkap.streamkap_heartbeat ( id INT AUTO_INCREMENT PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Insert the initial row INSERT INTO streamkap.streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` ```sql SQL theme={null} CREATE EVENT streamkap.streamkap_heartbeat_event ON SCHEDULE EVERY 1 MINUTE DO UPDATE streamkap.streamkap_heartbeat SET text = 'updated_heartbeat', last_update = CURRENT_TIMESTAMP WHERE id = 1; ``` Whichever database user is used to create and run the event scheduler (often the MySQL root user or a dedicated event scheduler user) needs appropriate permissions on the heartbeat table. Additionally, the Streamkap user also needs permissions to monitor the heartbeat table. ```sql SQL theme={null} GRANT EVENT ON streamkap.* TO {event scheduler user}; GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO {event scheduler user}; -- Grant permissions to the Streamkap user for monitoring and diagnostics GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO 'streamkap_user'@'%'; ``` ```sql SQL theme={null} -- View all scheduled events in the streamkap schema SHOW EVENTS IN streamkap; -- View event details SELECT * FROM information_schema.EVENTS WHERE EVENT_SCHEMA = 'streamkap'; -- Disable an event temporarily ALTER EVENT streamkap.streamkap_heartbeat_event DISABLE; -- Enable an event ALTER EVENT streamkap.streamkap_heartbeat_event ENABLE; -- Drop an event DROP EVENT IF EXISTS streamkap.streamkap_heartbeat_event; ``` *** ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **MySQL**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Hostname**: Specify the hostname. **Aurora endpoints and binary logs** Binary logs are accessible only from the primary DB instance, not from the replicas. Please use the **cluster endpoint** rather than reader or instance endpoints. In the event of failure, the Connector can then fail over to a new primary DB instance. See [Amazon connection management](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Aurora.Overview.Endpoints.html#Aurora.Overview.Endpoints.Types) for more about the types of endpoints. * **Port**: Default is `3306`. * **Connect via SSH Tunnel**: The Connector will connect to an SSH server in your network which has access to your database. This is necessary if the Connector cannot connect directly to your database. * See [SSH Tunnel](/ssh-tunnel) for setup instructions. * **Username**: Username to access the database. By default, Streamkap scripts use `streamkap_user`. * **Password**: Password to access the database. * **Read only**: Whether or not to use a read-only connection. Requires GTID to be enabled on the source database. See [Enable GTID](#method-1%3A-enable-gtid-recommended) for more information. * **Heartbeats**: Enabled by default. * For **read-write** connections, configure a heartbeat table in the source database and set **Heartbeat Table Database**. See [Heartbeats](#7-heartbeats) for setup instructions. * For **read-only** connections, configure a scheduled heartbeat event on the primary database using the MySQL Event Scheduler, and include the heartbeat table in [Schema and Table Capture](#5-schema-and-table-capture). See [Heartbeats](#7-heartbeats) for setup instructions. * **Connection Timezone**: The timezone of your database. **Timezone conversion** MySQL converts `TIMESTAMP` values from the current time zone to UTC for storage, and back from UTC to the current time zone for retrieval. By default, the current time zone for each connection is the database server's time zone but this option allows you to override that. As long as the time zones remain the same, you get back the same value you store. We recommend using the default `SERVER` option which attempts to detect the session time zone from the values configured on the MySQL server session variables 'time\_zone' or 'system\_time\_zone'. It also reduces the chance of problems with daylight savings adjustment 'fall back' and 'spring forward'. If either time zones change, an [ad-hoc snapshot](/snapshots#triggering-a-snapshot) is recommended so your source and destination timestamps are consistent. ### 3. Snapshot Settings If you set **Read only** to **No**, you will need to create a snapshot signal table and give permissions to the `streamkap_user`. See [Enable Snapshots](#6-enable-snapshots) for setup instructions. * **Signal Table**: Full path to the signal table including database and table name (e.g., `streamkap.streamkap_signal`). This table is used for incremental snapshotting. See [Enable Snapshots](#6-enable-snapshots) for setup instructions. ### 4. Advanced Parameters * **Represent binary data as**: Specifies how the data for binary columns e.g. `blob`, `binary`, `varbinary` should be interpreted. Your destination for this data can impact which option you choose. Default is `bytes`. * **Capture Only Captured Databases DDL**: Specifies whether the connector records schema structures from all databases defined in the server (the default) or only those databases for which you've explicitly configured the connector. Specify `true` to capture schema history only for the specific databases you've configured. This is particularly valuable when databases are large, to reduce the volume of DDL stored in the schema history topic. It also improves startup times when the connector restarts or recovers from failures. Default is `false`. See [Schema History Optimization](/schema-history-optimization) for details. * **Capture Only Captured Tables DDL**: Specifies whether the connector records the schema structure for all tables in the configured databases (the default) or only the tables whose changes the connector captures. Specify `true` to capture schema history only for the specific tables you've configured. This is particularly valuable when tables are large, to reduce the volume of DDL statements stored in the schema history topic. It also improves startup times when the connector restarts or recovers from failures. Default is `false`. See [Schema History Optimization](/schema-history-optimization) for details. Click **Next**. ### 5. Schema and Table Capture * **Add Schemas/Tables**: Specify the schema(s) and table(s) for capture. * You can bulk upload here. The format is a simple list of schemas and tables, with each entry on a new row. Save as a `.csv` file without a header. **CDC only captures base tables, not Views** Change Data Capture reads the MySQL binary log (binlog), which only records changes to physical tables. Database Views are query-time computations with no physical storage—they don't generate binlog entries. **What you cannot capture:** Views, temporary tables, MEMORY tables, BLACKHOLE tables, or system tables (information\_schema, performance\_schema). **Solution:** Specify only the underlying base tables that feed your views. You can recreate the view logic in your destination or transformation layer. **VIRTUAL generated columns are not streamed** MySQL [VIRTUAL generated columns](https://dev.mysql.com/doc/refman/8.0/en/create-table-generated-columns.html) are computed when rows are read and are **not** written to the binary log, so live streaming cannot capture or update their values. (STORED generated columns are written to the binary log and stream normally.) Snapshots read the table directly, so they **do** capture a value for VIRTUAL columns — but because streaming cannot keep it current, that value will become stale. **We recommend excluding VIRTUAL generated columns from capture:** in the Source's **Settings → Advanced** section, set **Column Selection Mode** to **Exclusion** and add the column to the **Column Exclusion List** (fully-qualified, e.g. `database.table.column`). If you need the value at your destination, calculate it there — or with a [transform](/transforms) — from the columns it is derived from. Click **Save**. **Adding tables programmatically** -- You can add tables to an existing source via the API or Terraform using the `table.include.list.user.defined` parameter (Terraform: `table_include_list_user_defined`). See the [REST API](/api) for getting started with the API, or [Terraform Resources](/terraform-resources#sources) for Terraform source configuration. **Have questions?** See the [MySQL Source FAQ](/mysql-source-faq) for answers to common questions about MySQL sources, troubleshooting, and best practices. # Amazon RDS Aurora PostgreSQL Source: https://docs.streamkap.com/amazon-rds-aurora-postgresql Set up real-time PostgreSQL change data capture from Amazon RDS Aurora to Streamkap, including logical replication, user roles, and heartbeats. ## Prerequisites * PostgreSQL version ≥ 10 * A database user with sufficient privileges to configure the database, including enabling logical replication and creating users ## PostgreSQL Setup ### 1. Grant Database Access * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your database. ### 2. Enable Logical Replication **Aurora Read Replicas** Aurora Read Replicas only support **physical** not logical replication. Please configure and use the primary server with Streamkap. Logical replication is a method of replicating data objects and their changes, based upon their replication identity (usually a primary key). The Connector relies on PostgreSQLs implementation of this. * Open the Amazon RDS console at [https://console.aws.amazon.com/rds/](https://console.aws.amazon.com/rds/). * In the navigation pane, choose **Parameter groups**. **Default parameter groups can't be modified** If the DB instance is using a default parameter group, create a new one: * Choose **Create parameter group**. * Enter a **Parameter group name** and **Description**. * For **Engine type**, choose your database engine. * For **Parameter group family**, choose a DB parameter group family. * For **Type**, choose **DB Cluster Parameter Group**. * Choose **Create**. * Select the parameter group to edit. * Choose **Edit** from **Actions**. * Set `rds.logical_replication` to `1`. * Set `wal_sender_timeout` to `0`. A nonzero value may cause disconnects in low/intermittent traffic databases. Enable [Heartbeats](#5-heartbeats) or set an appropriate value if needed. * Choose **Save changes**. If you created a new parameter group, associate it with your Aurora DB cluster: * In the navigation pane, choose **Databases** and select the target DB cluster. * Choose **Modify**. * Change the **DB cluster parameter group** setting. * Choose **Continue** and review modifications. * The change is applied immediately, regardless of the **Scheduling of modifications** setting. * On the confirmation page, choose **Modify cluster**. A reboot is required to apply the changes. **WAL and database storage** The write-ahead logs take up disk space on your database. If you run out of disk space, you may face an interruption to your database. To mitigate this: * Ensure that your database server has ample free space. * Enable sufficient space with auto growth on the database. * Monitor the WAL growth and configure alerts. * Configure the publication (See [Create Publication & Slot](/amazon-rds-aurora-postgresql#5-create-publication--slot)) to contain only tables you need. ### 3. Create Database User It's recommended to create a separate user and role for the Connector to access your PostgreSQL database. Below is an example script that does that. ```SQL SQL theme={null} -- Replace { ... } placeholders as required CREATE USER streamkap_user PASSWORD '{password}'; -- Create a role for Streamkap CREATE ROLE streamkap_role; GRANT streamkap_role TO streamkap_user; GRANT rds_replication TO streamkap_role; -- Grant Streamkap permissions on the database, schema and all tables to capture GRANT CONNECT ON DATABASE "{database}" TO streamkap_role; GRANT CREATE, USAGE ON SCHEMA "{schema}" TO streamkap_role; GRANT SELECT ON ALL TABLES IN SCHEMA "{schema}" TO streamkap_role; ALTER DEFAULT PRIVILEGES IN SCHEMA "{schema}" GRANT SELECT ON TABLES TO streamkap_role; ``` ### 4. Enable Snapshots To backfill your data, the Connector needs to be able to perform snapshots. See [Snapshots & Backfilling](/snapshots) for more information. To enable this feature, there are 2 methods available: #### Method 1: Enable read only connection **Requires PostgreSQL version 13 or higher.** This method is recommended if you cannot create a table in the source database and grant the Connector read/write privileges to that. * Set **Read only** to **Yes** during Streamkap Setup. No other configuration should be necessary. #### Method 2: Create a table in the source database You will need to create the table and give necessary permissions to the `streamkap_user`. The Connector will use this collection for managing snapshots. Below is an example script that does that. The examples below use `streamkap_signal` as the signal table name, but you can choose any name. During [Streamkap Setup](#3-snapshot-settings), provide the full path to your signal table in `schema.table` format (e.g., `streamkap.streamkap_signal`). ```SQL SQL theme={null} -- Create the schema CREATE SCHEMA streamkap; -- Switch to the newly created schema SET search_path TO streamkap; -- Create the table CREATE TABLE streamkap_signal ( id VARCHAR(255) PRIMARY KEY, type VARCHAR(32) NOT NULL, data VARCHAR(2000) NULL ); -- Grant necessary privileges on the table to the role GRANT CREATE, USAGE ON SCHEMA streamkap TO streamkap_role; GRANT SELECT ON ALL TABLES IN SCHEMA streamkap TO streamkap_role; GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE streamkap_signal TO streamkap_role; ``` **Publications and signal tables** When you create the PostgreSQL publication in the next step, if you choose to specify tables for capture instead of all tables, you **must** include your signal table. ### 5. Heartbeats Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. For read-write connections (when **Read only** is **No** during Streamkap Setup), the Connector writes to the heartbeat table directly. ```SQL SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Switch to the streamkap schema SET search_path TO streamkap; -- Create the heartbeat table with id, text, and last_update fields CREATE TABLE streamkap_heartbeat ( id SERIAL PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Grant permission to the Streamkap user GRANT USAGE ON SCHEMA streamkap TO streamkap_user; GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE streamkap_heartbeat TO streamkap_user; -- Insert the first row into the heartbeat table INSERT INTO streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` **Heartbeat tables and PostgreSQL publications** If the `streamkap_pub` publication created during PostgreSQL Setup was for specific tables e.g. `CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, table3, ...;` instead of `FOR ALL TABLES;`, you **must** add the heartbeat table to the publication: `ALTER PUBLICATION streamkap_pub ADD TABLE streamkap.streamkap_heartbeat;`. For read-only connections (when **Read only** is **Yes** during Streamkap Setup), the Connector cannot write to the heartbeat table directly. Instead, you must configure a scheduled job on the **primary** database to generate artificial traffic. These changes will replicate to the read replica, which the Connector then consumes. **Run these commands on the primary database, not the read replica.** The heartbeat table and scheduled job must be created on the primary database. The changes will automatically replicate to the read replica. The `pg_cron` extension must be allowed in your database's configuration. See your provider's documentation for enabling extensions: Scheduling maintenance with pg\_cron Scheduling maintenance with pg\_cron PostgreSQL extensions PostgreSQL extensions pg\_cron extension pg\_cron extension Supported extensions Once allowed, create the extension: ```sql SQL theme={null} CREATE EXTENSION IF NOT EXISTS pg_cron; ``` ```sql SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Create the heartbeat table CREATE TABLE streamkap.streamkap_heartbeat ( id SERIAL PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Insert the initial row INSERT INTO streamkap.streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` ```sql SQL theme={null} -- Schedule a job to update the heartbeat table every minute SELECT cron.schedule( 'streamkap_heartbeat_job', '*/1 * * * *', $$UPDATE streamkap.streamkap_heartbeat SET text = 'updated_heartbeat', last_update = CURRENT_TIMESTAMP WHERE id = 1;$$ ); ``` Whichever database user is used to create and run the cron jobs (often the `postgres` user or a dedicated cron user) needs appropriate permissions on the heartbeat table. Additionally, the Streamkap user also needs permissions to monitor the heartbeat table. ```sql SQL theme={null} GRANT USAGE ON SCHEMA streamkap TO {cron user}; GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO {cron user}; -- Grant permissions to the Streamkap user for monitoring and diagnostics GRANT USAGE ON SCHEMA streamkap TO streamkap_user; GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO streamkap_user; ``` The `pg_cron` extension stores job execution history in the table `cron.job_run_details`. To prevent this table from growing indefinitely: ```sql SQL theme={null} SELECT cron.schedule( 'streamkap_cron_cleanup', '0 0 * * *', $$DELETE FROM cron.job_run_details WHERE end_time < now() - interval '7 days';$$ ); ``` ```sql SQL theme={null} -- View all scheduled jobs SELECT * FROM cron.job; -- View recent job execution history SELECT * FROM cron.job_run_details ORDER BY start_time DESC LIMIT 10; -- Unschedule a job (replace {jobid} with the actual job ID) SELECT cron.unschedule({jobid}); ``` **Heartbeat tables and PostgreSQL publications** If the `streamkap_pub` publication was created for specific tables (e.g., `CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, ...;`) instead of `FOR ALL TABLES`, you **must** add the heartbeat table to the publication: ```sql SQL theme={null} ALTER PUBLICATION streamkap_pub ADD TABLE streamkap.streamkap_heartbeat; ``` **Logical-message heartbeats** are an alternative to a heartbeat table. The Connector itself calls `pg_logical_emit_message()` on each beat, which appends a transactional record to the write-ahead log (WAL). PostgreSQL treats that record like any other committed change, so the replication slot advances and WAL files are released—without any new table, scheduled job, or write privileges on the source database. **When to choose this over a heartbeat table** Use **logical-message heartbeats** when you want zero footprint in the source database itself—no schemas, tables, scheduled jobs, or write grants to maintain. Use a **heartbeat table** when you want the heartbeat to be visible as an ordinary row that operators can `SELECT` to confirm liveness, or when the requirements below rule logical-message out. **Requirements** * **Streamkap must be connected directly to a primary**, not to a read replica or other hot standby. The Connector executes `pg_logical_emit_message()` against the database it's connected to, and that function writes to the WAL—hot standbys are read-only and will reject the call with an error like `cannot execute ... during recovery`. If your Streamkap source is configured against a replica, use the **Read-only connections** tab instead. * PostgreSQL **14 or later** on the primary. Earlier versions are not supported because the built-in `pgoutput` replication plugin only began streaming logical-decoding messages to the Connector in PostgreSQL 14. On PostgreSQL 13 and earlier, `pg_logical_emit_message()` still appends records to the WAL, but the Connector cannot read them and the replication slot will not advance. * The Streamkap user needs `SELECT` privileges only—no `INSERT`, `UPDATE`, or `DELETE` on any heartbeat table. * Logical replication must already be enabled for the Connector itself (this is a prerequisite for any PostgreSQL source, regardless of heartbeat mode). During [Connection Settings](#2-connection-settings): * Set **Heartbeats** to **Yes**. * Leave **Heartbeat Table Schema** blank. * Set **Use Logical-Message Heartbeat** to **Yes**. No further setup is required in the source database. The Connector will issue the heartbeat query once per minute using the standard Streamkap heartbeat interval. After enabling, you can confirm that the replication slot is advancing against an otherwise idle database. Run on the primary: ```sql SQL theme={null} SELECT slot_name, confirmed_flush_lsn, restart_lsn FROM pg_replication_slots WHERE slot_name = 'streamkap_pgoutput_slot'; ``` Re-run the query after one or two minutes. Both `confirmed_flush_lsn` and `restart_lsn` should move forward. **What the Connector sends** When this mode is enabled, the Connector issues the following query on each beat: ```sql SQL theme={null} SELECT pg_logical_emit_message(true, '__debezium-heartbeat', now()::varchar); ``` The first argument (`true`) makes the emit **transactional**. Non-transactional emits advance the replication slot's `confirmed_flush_lsn` but do not recompute global WAL retention across all slots, so WAL files can still accumulate on an idle source database—transactional emits avoid that. The transactional cost is negligible (one extra transaction ID per beat, well below PostgreSQL's wraparound horizon at the default one-minute interval). The heartbeat record is filtered out before reaching Kafka, so it does not appear as a record in any topic in your pipeline. ### 6. Create Publication & Slot Publications contain a set of change events for the tables you want the Connector to capture. * Create a publication for your tables. You can create a publication for all tables or selected tables. ```SQL SQL - All Table theme={null} -- Create a publication for all tables to capture CREATE PUBLICATION streamkap_pub FOR ALL TABLES; ``` ```SQL SQL - Selected Tables theme={null} -- Create a publication for specific tables to capture CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, table3, ...; -- Verify the tables to capture were added to the publication SELECT * FROM pg_publication_tables where pubname = 'streamkap_pub'; ``` **Altering publications** You cannot alter `FOR ALL TABLES` publications to include/exclude tables. If you set up a `FOR ALL TABLES` publication and later decide to change that, you have to drop the publication and create another to include specific tables e.g. `CREATE PUBLICATION ... TABLE table1, table2, table3, ...`. However, any change events that occur before the new publication's created will not be included in it, so a snapshot's required to ensure they are not missed by your Streamkap pipelines. You should also stop the [Source](https://app.streamkap.com/connectors/sources) *before* changing the publication. A replication slot represents a stream of change events the Connector reads from. * Create a replication slot. ```SQL SQL theme={null} -- Create a logical replication slot SELECT pg_create_logical_replication_slot('streamkap_pgoutput_slot', 'pgoutput'); -- Verify the replication slot is working (this may take a few moments to return the count) SELECT count(*) FROM pg_logical_slot_peek_binary_changes('streamkap_pgoutput_slot', null, null, 'proto_version', '1', 'publication_names', 'streamkap_pub'); ``` **Replication slot auto-creation** If the Connector cannot find the replication slot named in [Replication Settings](/amazon-rds-aurora-postgresql#4-replication-settings) during Streamkap Setup, it will attempt to create one automatically as it cannot read the database transaction logs without one. This is assuming the database user has been granted the `rds_replication` role as per [Create Database User](/amazon-rds-aurora-postgresql#3-create-database-user). #### Excluding columns from replication By default a publication streams every column of each captured table. On **PostgreSQL 15+** you can attach a *column list* to a table in the publication so that only the listed columns are replicated — any column you leave out is excluded at the source and never enters the replication stream. ```SQL SQL - Selected Columns theme={null} -- Only the listed columns are published; every other column is excluded. -- Each table's list MUST include its replica identity (primary key), -- otherwise UPDATE/DELETE for that table cannot be replicated. CREATE PUBLICATION streamkap_pub FOR TABLE public.users (id, email, created_at, updated_at), public.orders (id, user_id, status, total), public.products (id, sku, name, price); -- Add or change a table's column list on an existing publication ALTER PUBLICATION streamkap_pub SET TABLE public.users (id, email, updated_at); -- Verify the published columns per table SELECT pubname, schemaname, tablename, attnames FROM pg_publication_tables WHERE pubname = 'streamkap_pub'; ``` * Requires **PostgreSQL 15 or later**. On earlier versions a publication always streams all columns. * Each table's column list **must include the table's replica identity** — its primary key, or the columns from `REPLICA IDENTITY USING INDEX` / `FULL`. Otherwise `UPDATE` and `DELETE` events for that table are not replicated. * Column lists are only valid with `FOR TABLE`, not `FOR ALL TABLES`. A table listed without parentheses publishes all of its columns, so you can mix restricted and unrestricted tables in one publication. A publication column list only governs **streamed (CDC) changes**. The initial snapshot reads columns directly from the table, so an excluded column can still appear in snapshot records. If a column must never be captured, exclude it from the snapshot as well, not only from the publication. *** ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **PostgreSQL**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Hostname**: Specify the hostname. * **Port**: Default is `5432`. * **Connect via SSH Tunnel**: The Connector will connect to an SSH server in your network which has access to your database. This is necessary if the Connector cannot connect directly to your database. * See [SSH Tunnel](/ssh-tunnel) for setup instructions. * **Username**: Username to access the database. By default, Streamkap scripts use `streamkap_user`. * **Password**: Password to access the database. * **Database**: Specify the database to stream data from. * **Read only**: Whether or not to use a read-only connection. Requires PostgreSQL version 13 or higher. * **Heartbeats**: Enabled by default. Choose one of three modes—see [Heartbeats](#5-heartbeats) for setup instructions: * **Read-write** connections: configure a heartbeat table in the source database and set **Heartbeat Table Schema**. * **Read-only** connections: configure a scheduled heartbeat job on the primary database using `pg_cron`, and include the heartbeat table in [Schema and Table Capture](#6-schema-and-table-capture). * **Logical-message** (PostgreSQL 14+, works under read-only): set **Use Logical-Message Heartbeat** to **Yes** and leave **Heartbeat Table Schema** blank. No table or write privileges required on the source. ### 3. Snapshot Settings If you set Read only to **No**, you will need to create a snapshot signal table and give permissions to the `streamkap_user`. See [Enable Snapshots](/amazon-rds-aurora-postgresql#4-enable-snapshots) for setup instructions. * **Signal Table**: Full path to the signal table including schema and table name (e.g., `streamkap.streamkap_signal`). This table is used for incremental snapshotting. See [Enable Snapshots](#4-enable-snapshots) for setup instructions. ### 4. Replication Settings * **Replication Slot Name**: The name of the replication slot for the connector to use. Default is `streamkap_pgoutput_slot`. * **Publication Name**: The name of the publication for the connector to use. Default is `streamkap_pub`. ### 5. Advanced Parameters * **SSL mode**: Whether to use an encrypted connection to the PostgreSQL server. By default, it's required. * **Prefix with Database Name?**: Changes the format of topics to `DatabaseName_TopicName` * **Represent binary data as**: Specifies how the data for binary columns e.g. `blob`, `binary`, `varbinary` should be interpreted. Your destination for this data can impact which option you choose. Default is `bytes`. Click **Next**. ### 6. Schema and Table Capture * **Add Schemas/Tables**: Specify the schema(s) and table(s) for capture * You can bulk upload here. The format is a simple list of schemas and tables, with each entry on a new row. Save as a `.csv` file without a header. **CDC only captures base tables, not Views** Change Data Capture reads the PostgreSQL write-ahead log (WAL), which only records changes to physical tables. Database Views are query-time computations with no physical storage—they don't generate WAL entries. **What you cannot capture:** Views, temporary tables, unlogged tables, foreign tables (FDW), or system tables (information\_schema, pg\_catalog). **Solution:** Specify only the underlying base tables that feed your views. You can recreate the view logic in your destination or transformation layer. **Generated columns are not kept up to date** PostgreSQL [generated columns](https://www.postgresql.org/docs/current/ddl-generated-columns.html) (defined with `GENERATED ALWAYS AS (...) STORED`) are calculated by the database from other columns. PostgreSQL does **not** include them in the replication stream Streamkap reads, so live streaming cannot keep their values current. **We recommend excluding generated columns from capture.** In the Source's **Settings → Advanced** section, set **Column Selection Mode** to **Exclusion** and add the column to the **Column Exclusion List** (fully-qualified, e.g. `schema.table.column`). If you need the value at your destination, calculate it there — or with a [transform](/transforms) — from the columns it is derived from. **Snapshots:** An incremental snapshot (the watermarked **Filtered** and **Full** options) of a table that contains a generated column will **fail** unless the column is excluded. **Blocking** and **Parallel** snapshots are not affected, though any value they capture will become stale because streaming cannot update it. See [Snapshots & Backfilling](/snapshots). PostgreSQL 18 and later can optionally stream stored generated columns when the publication is created with `publish_generated_columns = stored`; earlier versions cannot. Click **Save**. ## Troubleshooting When upgrading the PostgreSQL database used by Streamkap, there are specific steps to prevent data loss and ensure continued operation.

Streamkap handles network failures and outages well. If a monitored database stops, the connector resumes from the last recorded log sequence number (LSN) once communication is restored. It retrieves this offset and queries PostgreSQL for a matching LSN in the replication slot. A replication slot is required for change capture, but PostgreSQL removes slots during upgrades and doesn’t restore them. When the connector restarts, it requests the last known offset, but PostgreSQL cannot return it. Creating a new replication slot isn't enough to prevent data loss. New slots only track changes from their creation point and lack earlier offsets. The connector fetches its last known offset from Kafka but can't retrieve corresponding data from the new slot. It skips older change events and resumes from the latest log position, causing silent data loss with no warnings.

### Procedure Follow these steps to minimize data loss. Note that a few steps may require support from Streamkap. We recommend notifying us about your database upgrade ahead of time to ensure you have the necessary support. * Using your database's upgrade procedure, ensure writes to it have stopped. * Allow the connector to capture all change events before starting the upgrade procedure. Ask Streamkap to confirm this for you. **Interrupting change data capture** If all the events were not captured before stopping the Source and upgrading the database, you can perform a snapshot (after the procedure is completed) in Streamkap for that Source to ensure no change events were missed. * Assuming all events are captured, stop the Source in the Streamkap app. This flushes the last records and saves the last offset. * Stop the database and upgrade it using your upgrade procedure. Once the database is upgraded, and **before** allowing writes again: * Recreate the logical replication slot, otherwise, Streamkap will miss changes. See [Create Publication & Slot](/amazon-rds-aurora-postgresql#5-create-publication--slot). * Verify the publication for Streamkap exists; recreate it if necessary: [Create Publication & Slot](/amazon-rds-aurora-postgresql#5-create-publication--slot). **Update the Source in the Streamkap app** If you have chosen different names for the logical replication slot or publication, make sure to update them in the Streamkap setup page for the relevant PostgreSQL Source. In this case, contact Streamkap, as they may need to reset your Connector's offsets in this scenario. * Restore write access to the database. * Resume or restart the Source in the Streamkap app.
Introduced in PostgreSQL 13, the `REPLICA IDENTITY` table setting controls what data is logged for row updates and deletes. By default, only the primary key and Streamkap metadata column values are retained for deleted records. All other columns will be empty. This leaves you with an incomplete record.

If you require - for auditing and historical tracking purposes - **all** column values for deleted records, or if your deletion strategy for your destination is 'soft deletes' (retain the deleted record with a deletion flag), you should set the `REPLICA IDENTITY` to `FULL` for all capture tables. ```SQL SQL theme={null} ALTER TABLE {table} REPLICA IDENTITY FULL; ``` This ensures complete data retention.
For capturing partitioned tables, it's essential to enable `publish_via_partition_root` on the publication. By default, changes to partitions are published from the partition itself. The connector expects changes to come from the root table. To ensure compatibility and consistent replication, enable this setting: `ALTER PUBLICATION streamkap_pub SET (publish_via_partition_root = true);`
# Amazon RDS MariaDB Source: https://docs.streamkap.com/amazon-rds-mariadb Set up MariaDB change data capture on Amazon RDS with Streamkap, covering binary log parameter groups, replication user grants, and heartbeat configuration. # Prerequisites * MariaDB version ≥ 11.4.3 * [binlog](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_LogAccess.MariaDB.BinaryFormat.html) configured * Streamkap user and role The following table summarizes all permissions the Streamkap user needs. The setup steps on each source connector page walk through granting each one. **Core permissions (always required):** | Permission | Scope | Purpose | | -------------------- | ------------ | -------------------------------------------------------- | | `REPLICATION CLIENT` | `*.*` | Read binlog metadata and positions | | `REPLICATION SLAVE` | `*.*` | Read binlog events for CDC streaming | | `RELOAD` | `*.*` | Flush operations required for consistent snapshots | | `SHOW DATABASES` | `*.*` | Discover available databases and schemas | | `SELECT` | `{schema}.*` | Read table data during initial and incremental snapshots | **Snapshot signal table (required if GTID is disabled):** | Permission | Scope | Purpose | | ---------- | ---------------------------- | -------------------------- | | `SELECT` | `streamkap.streamkap_signal` | Read signal table state | | `INSERT` | `streamkap.streamkap_signal` | Trigger snapshot signals | | `UPDATE` | `streamkap.streamkap_signal` | Update signal table state | | `DELETE` | `streamkap.streamkap_signal` | Clean up processed signals | **Heartbeat table (required if heartbeats are enabled):** | Permission | Scope | Purpose | | ---------- | ------------------------------- | ------------------------------ | | `SELECT` | `streamkap.streamkap_heartbeat` | Read heartbeat state | | `INSERT` | `streamkap.streamkap_heartbeat` | Write heartbeat records | | `UPDATE` | `streamkap.streamkap_heartbeat` | Update heartbeat timestamps | | `DELETE` | `streamkap.streamkap_heartbeat` | Clean up old heartbeat records | MariaDB has GTID enabled by default. If you are using the default GTID configuration with a read-only connection, the signal table permissions are not needed. The heartbeat table still requires permissions for monitoring and diagnostics. **Combined GRANT statements:** ```sql SQL theme={null} -- Core permissions (always required) GRANT REPLICATION CLIENT, RELOAD, SHOW DATABASES, REPLICATION SLAVE ON *.* TO 'streamkap_user'@'%'; GRANT SELECT ON {schema}.* TO 'streamkap_user'@'%'; -- Signal table (if GTID is disabled) GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_signal TO 'streamkap_user'@'%'; -- Heartbeat table (if heartbeats are enabled) GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO 'streamkap_user'@'%'; ``` Amazon RDS MariaDB uses standard MariaDB `GRANT` statements for user permissions. Binary logging is configured separately via RDS automated backups and parameter groups (see steps below). # Granting Privileges It's recommended to create a separate user and role for Streamkap to access your MariaDB database. Below is an example script that does that. ```SQL SQL theme={null} -- Replace { ... } placeholders as required; CREATE USER streamkap_user@'%' IDENTIFIED BY '{password}'; -- Grant Permissions GRANT REPLICATION CLIENT, RELOAD, SHOW DATABASES, REPLICATION SLAVE ON *.* TO streamkap_user@'%'; -- Grant Select on all schemas needed GRANT SELECT ON {schema}.* TO 'streamkap_user'@'%'; ``` ## Enable Snapshots You can perform ad-hoc snapshots of all or some of your tables in the Streamkap app. See [Snapshots & Backfilling](/snapshots) for more information. To enable this feature, there are 2 methods available for MariaDB databases. ### Method 1: Enable GTID (default) Global transaction identifiers (GTIDs) uniquely identify transactions that occur on a server within a cluster. Though not required, using GTIDs simplifies replication and enables you to more easily confirm if primary and replica servers are consistent as well as carry out incremental snapshots. For MariaDB, this is **enabled by default**, no additional setup is necessary. ### Method 2: Create a table in the source database If for some reason you have disabled GTIDs and cannot enable them, you will need to create the table and give permissions to the `streamkap_user`. Streamkap will use this collection for managing snapshots. The examples below use `streamkap_signal` as the signal table name, but you can choose any name. During [Streamkap Setup](#3-snapshot-settings), provide the full path to your signal table in `database.table` format (e.g., `streamkap.streamkap_signal`). ```SQL SQL theme={null} -- Create the schema CREATE SCHEMA streamkap; CREATE TABLE streamkap.streamkap_signal ( id VARCHAR(255) PRIMARY KEY, type VARCHAR(32) NOT NULL, data VARCHAR(2000) NULL ); GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_signal TO 'streamkap_user'@'%'; ``` ## Heartbeats Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. For read-write connections (when **Read only** is **No** during Streamkap Setup), the Connector writes to the heartbeat table directly. ```SQL SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Switch to the streamkap schema USE streamkap; -- Create the heartbeat table with id, text, and last_update fields CREATE TABLE streamkap_heartbeat ( id INT AUTO_INCREMENT PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ); -- Grant permission to the Streamkap user GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO 'streamkap_user'@'%'; -- Insert the first row into the heartbeat table INSERT INTO streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` For read-only connections (when **Read only** is **Yes** during Streamkap Setup), the Connector cannot write to the heartbeat table directly. Instead, you must configure a scheduled job on the **primary** database to generate artificial traffic. These changes will replicate to the read replica, which the Connector then consumes. **Run these commands on the primary database, not the read replica.** The heartbeat table and scheduled event must be created on the primary database. The changes will automatically replicate to the read replica. The MariaDB Event Scheduler flag must be enabled on your database (`event_scheduler=ON`). See your provider's documentation: Modifying parameters in a DB parameter group in Amazon RDS Event Scheduler documentation Check if the event scheduler is enabled: ```sql SQL theme={null} SHOW VARIABLES WHERE VARIABLE_NAME = 'event_scheduler'; ``` **Making this setting persistent** For self-hosted MariaDB, add `event_scheduler = ON` to your MariaDB configuration file (usually `my.cnf` or `my.ini`) in the `[mysqld]` section. For managed databases (RDS), configure the `event_scheduler` parameter through your provider's console or CLI. ```sql SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Switch to the streamkap schema USE streamkap; -- Create the heartbeat table CREATE TABLE streamkap.streamkap_heartbeat ( id INT AUTO_INCREMENT PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Insert the initial row INSERT INTO streamkap.streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` ```sql SQL theme={null} CREATE EVENT streamkap.streamkap_heartbeat_event ON SCHEDULE EVERY 1 MINUTE DO UPDATE streamkap.streamkap_heartbeat SET text = 'updated_heartbeat', last_update = CURRENT_TIMESTAMP WHERE id = 1; ``` Whichever database user is used to create and run the event scheduler (often the MariaDB root user or a dedicated event scheduler user) needs appropriate permissions on the heartbeat table. Additionally, the Streamkap user also needs permissions to monitor the heartbeat table. ```sql SQL theme={null} GRANT EVENT ON streamkap.* TO {event scheduler user}; GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO {event scheduler user}; -- Grant permissions to the Streamkap user for monitoring and diagnostics GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO 'streamkap_user'@'%'; ``` ```sql SQL theme={null} -- View all scheduled events in the streamkap schema SHOW EVENTS IN streamkap; -- View event details SELECT * FROM information_schema.EVENTS WHERE EVENT_SCHEMA = 'streamkap'; -- Disable an event temporarily ALTER EVENT streamkap.streamkap_heartbeat_event DISABLE; -- Enable an event ALTER EVENT streamkap.streamkap_heartbeat_event ENABLE; -- Drop an event DROP EVENT IF EXISTS streamkap.streamkap_heartbeat_event; ``` ## Enable binary logging The automated backups feature determines whether binary logging is turned on or off for MariaDB. 1. Sign in to the AWS Management Console and open the Amazon RDS console at [https://console.aws.amazon.com/rds/](https://console.aws.amazon.com/rds/) 2. In the navigation pane, choose **Databases**, and then choose the DB instance or Multi-AZ DB cluster that you want to modify 3. Choose **Modify** 1. For Backup retention period, choose a positive nonzero value, for example 3 days 4. Choose **Continue** 5. Choose **Apply immediately** 6. Choose **Modify DB instance** or **Modify cluster** to save your changes and enable automated backups ## Configure binary logging 1. Open the Amazon RDS console at [https://console.aws.amazon.com/rds/](https://console.aws.amazon.com/rds/) 2. In the navigation pane, choose **Parameter groups** 3. Choose the parameter group used by the DB instance you want to modify 4. You can't modify a default parameter group. If the DB instance is using a default parameter group, create a new parameter group and associate it with the DB instance 5. From **Parameter group actions**, choose **Edit** 6. Set the `binlog_format` parameter to the binary logging format of `ROW` 7. Set the `binlog_row_image`parameter to `FULL` 8. Choose **Save changes** to save the updates to the DB parameter group ## Verify binary logs are enabled You can either: * Check the parameter group for the DB instance and that `log_bin` parameter is `ON` * Run the following SQL query on the DB instance `SHOW VARIABLES LIKE '%log_bin%';`. Result should be `ON` * Run `SHOW BINARY LOGS` # Consider Access Restrictions * Visit [Connection Options](/connection-options) to ensure Streamkap can reach your database # Setup MariaDB Connector in Streamkap * Go to Sources and click Create New * Input * Name for your Connector * Hostname * Port (Default `3306`) * Username (Username you chose earlier, our scripts use `streamkap_user`) * Password * Read only * Whether or not to use a read-only connection. MariaDB has GTID enabled by default, so no additional configuration is necessary. See [Enable GTID](/amazon-rds-mariadb#method-1-enable-gtid) for more information. * If you have disabled GTID mode and cannot enable it, set **Read only** to **No** and create the signal table as described [here](/amazon-rds-mariadb#method-2-create-a-table-in-the-source-database). * Signal Table: Full path to the signal table including database and table name (e.g., `streamkap.streamkap_signal`). This table is used for incremental snapshotting. See [Enable Snapshots](/amazon-rds-mariadb#enable-snapshots) for setup instructions. * Heartbeats: Enabled by default. * For **read-write** connections, configure a heartbeat table in the source database and set **Heartbeat Table Database**. See [Heartbeats](#heartbeats) for setup instructions. * For **read-only** connections, configure a scheduled heartbeat event on the primary database using the MariaDB Event Scheduler, and include the heartbeat table in [Add Schemas/Tables](#setup-mariadb-connector-in-streamkap). See [Heartbeats](#heartbeats) for setup instructions. * Connection Timezone - The timezone of your database * **Timezone conversion** MariaDB converts `TIMESTAMP` values from the current time zone to UTC for storage, and back from UTC to the current time zone for retrieval. By default, the current time zone for each connection is the database server's time zone but this option allows you to override that. As long as the time zones remain the *same*, you get back the same value you store. We recommend using the default `SERVER` option which attempts to detect the session time zone from the values configured on the MariaDB server session variables 'time\_zone' or 'system\_time\_zone'. It also reduces the chance of problems with daylight savings adjustment 'fall back' and 'spring forward'. If either time zones change, an [ad-hoc snapshot](/snapshots#triggering-a-snapshot) is recommended so your source and destination timestamps are consistent. * Connect via SSH Tunnel. See [SSH Tunnel](/ssh-tunnel) * Advanced Parameters * Represent Binary Data As (Default `bytes`) * Capture Only Captured Databases DDL (Default `false`) - Specifies whether the connector records schema structures from all databases defined in the server (the default) or only those databases for which you've explicitly configured the connector. Specify `true` to capture schema history only for the specific databases you've configured. This is particularly valuable when databases are large, to reduce the volume of DDL stored in the schema history topic. It also improves startup times when the connector restarts or recovers from failures. See [Schema History Optimization](/schema-history-optimization) for details. * Capture Only Captured Tables DDL (Default `false`) - Specifies whether the connector records the schema structure for all tables in the configured databases (the default) or only the tables whose changes the connector captures. Specify `true` to capture schema history only for the specific tables you've configured. This is particularly valuable when tables are large, to reduce the volume of DDL statements stored in the schema history topic. It also improves startup times when the connector restarts or recovers from failures. See [Schema History Optimization](/schema-history-optimization) for details. * Add Schemas/Tables. Can also bulk upload here. The format is a simple list of each schema or table per row saved in csv format without a header. **CDC only captures base tables, not Views** Change Data Capture reads the MariaDB binary log (binlog), which only records changes to physical tables. Database Views are query-time computations with no physical storage—they don't generate binlog entries. **What you cannot capture:** Views, temporary tables, MEMORY tables, BLACKHOLE tables, or system tables (information\_schema, performance\_schema). **Solution:** Specify only the underlying base tables that feed your views. You can recreate the view logic in your destination or transformation layer. **VIRTUAL generated columns are not streamed** MariaDB [VIRTUAL generated columns](https://mariadb.com/kb/en/generated-columns/) are computed when rows are read and are **not** written to the binary log, so live streaming cannot capture or update their values. (PERSISTENT/stored generated columns are written to the binary log and stream normally.) Snapshots read the table directly, so they **do** capture a value for VIRTUAL columns — but because streaming cannot keep it current, that value will become stale. **We recommend excluding VIRTUAL generated columns from capture:** in the Source's **Settings → Advanced** section, set **Column Selection Mode** to **Exclusion** and add the column to the **Column Exclusion List** (fully-qualified, e.g. `database.table.column`). If you need the value at your destination, calculate it there — or with a [transform](/transforms) — from the columns it is derived from. * Click Save The connector will take approximately 1 minute to start processing data. **Adding tables programmatically** -- You can add tables to an existing source via the API or Terraform using the `table.include.list.user.defined` parameter (Terraform: `table_include_list_user_defined`). See the [REST API](/api) for getting started with the API, or [Terraform Resources](/terraform-resources#sources) for Terraform source configuration. # Amazon RDS MySQL Source: https://docs.streamkap.com/amazon-rds-mysql Stream MySQL change data from Amazon RDS to Streamkap with binlog-based CDC, including parameter groups, replication user grants, and heartbeat tables. ## Prerequisites * MySQL version ≥ 5.7 * MySQL [binlog](https://dev.mysql.com/doc/refman/8.0/en/binary-log.html) enabled * A database user with sufficient privileges to configure the database, including enabling binary logging and creating users The following table summarizes all permissions the Streamkap user needs. The setup steps on each source connector page walk through granting each one. **Core permissions (always required):** | Permission | Scope | Purpose | | -------------------- | ------------ | -------------------------------------------------------- | | `REPLICATION CLIENT` | `*.*` | Read binlog metadata and positions | | `REPLICATION SLAVE` | `*.*` | Read binlog events for CDC streaming | | `RELOAD` | `*.*` | Flush operations required for consistent snapshots | | `SHOW DATABASES` | `*.*` | Discover available databases and schemas | | `SELECT` | `{schema}.*` | Read table data during initial and incremental snapshots | **Snapshot signal table (required if GTID is not enabled):** | Permission | Scope | Purpose | | ---------- | ---------------------------- | -------------------------- | | `SELECT` | `streamkap.streamkap_signal` | Read signal table state | | `INSERT` | `streamkap.streamkap_signal` | Trigger snapshot signals | | `UPDATE` | `streamkap.streamkap_signal` | Update signal table state | | `DELETE` | `streamkap.streamkap_signal` | Clean up processed signals | **Heartbeat table (required if heartbeats are enabled):** | Permission | Scope | Purpose | | ---------- | ------------------------------- | ------------------------------ | | `SELECT` | `streamkap.streamkap_heartbeat` | Read heartbeat state | | `INSERT` | `streamkap.streamkap_heartbeat` | Write heartbeat records | | `UPDATE` | `streamkap.streamkap_heartbeat` | Update heartbeat timestamps | | `DELETE` | `streamkap.streamkap_heartbeat` | Clean up old heartbeat records | If GTID is enabled (default on Google Cloud SQL), the signal table is not required. If using **read-only** mode with GTID enabled, the signal table permissions are not needed. The heartbeat table still requires permissions for monitoring and diagnostics. **Combined GRANT statements:** ```sql SQL theme={null} -- Core permissions (always required) GRANT REPLICATION CLIENT, RELOAD, SHOW DATABASES, REPLICATION SLAVE ON *.* TO 'streamkap_user'@'%'; GRANT SELECT ON {schema}.* TO 'streamkap_user'@'%'; -- Signal table (if GTID is not enabled) GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_signal TO 'streamkap_user'@'%'; -- Heartbeat table (if heartbeats are enabled) GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO 'streamkap_user'@'%'; ``` Amazon RDS MySQL uses standard MySQL `GRANT` statements for user permissions. Binary logging is configured separately via RDS parameter groups (see step 2 below). ## MySQL Setup ### 1. Grant Database Access * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your database. ### 2. Configure Binary Logging Binary logging records all changes to your database tables. The Connector relies on MySQL's implementation of this. * Open the Amazon RDS console at [https://console.aws.amazon.com/rds/](https://console.aws.amazon.com/rds/). * In the navigation pane, choose **Parameter groups**. **Default parameter groups can't be modified** If the DB instance is using a default parameter group, create a new one: * Choose **Create parameter group**. * Enter a **Parameter group name** and **Description**. * For **Engine type**, choose your database engine. * For **Parameter group family**, choose a DB parameter group family. * Choose **Create**. * Select the parameter group to edit. * Choose **Edit** from **Parameter group actions**. * Set `binlog_format` to `ROW`. * Set `binlog_row_image` to `Full`. * Choose **Save changes**. If you created a new parameter group, associate it with your DB instance: * In the navigation pane, choose **Databases** and select the target DB instance. * Choose **Modify**. * Change the **DB parameter group** setting. * Choose **Continue** and review modifications. * On the confirmation page, choose **Modify DB Instance**. A reboot is required to apply the changes. ### 3. Set Binary Log Retention Period * Connect to your master database with your SQL tool. * View current settings with `CALL mysql.rds_show_configuration;` * If less than 24 hours or null, run `CALL mysql.rds_set_configuration('binlog retention hours', 72);` ### 4. Verify Binary Logs Are Enabled You can verify using any of these methods: * Check the parameter group for the DB instance and that `log_bin` parameter is `ON` * Run the following SQL query on the DB instance `SHOW VARIABLES LIKE '%log_bin%';`. Result should be `ON` * Run `SHOW BINARY LOGS` ### 5. Create Database User It's recommended to create a separate user and role for the Connector to access your MySQL database. Below is an example script that does that. ```SQL SQL theme={null} -- Replace { ... } placeholders as required -- Identify version SHOW VARIABLES LIKE 'VERSION'; -- On MySQL version 5.6 to 8.0 CREATE USER 'streamkap_user'@'%' IDENTIFIED BY '{password}'; -- On MySQL version 8.0+ CREATE USER 'streamkap_user'@'%' IDENTIFIED WITH mysql_native_password BY '{password}'; -- Grant Permissions GRANT REPLICATION CLIENT, RELOAD, SHOW DATABASES, REPLICATION SLAVE ON *.* TO 'streamkap_user'@'%'; -- Grant Select on all schemas needed GRANT SELECT ON {schema}.* TO 'streamkap_user'@'%'; ``` ### 6. Enable Snapshots To backfill your data, the Connector needs to be able to perform snapshots. See [Snapshots & Backfilling](/snapshots) for more information. To enable this feature, there are 2 methods available: #### Method 1: Enable GTID (Recommended) This method is recommended if you cannot create and grant Streamkap read/write privileges on a 'signal' table (method 2) for any reason. It's the equivalent of a 'read only' connection. GTID mode is only available in MySQL version ≥ 5.6.5 Global transaction identifiers (GTIDs) uniquely identify transactions that occur on a server within a cluster. Though not required, using GTIDs simplifies replication and enables you to more easily confirm if primary and replica servers are consistent as well as carry out incremental snapshots. Set up following these instructions. Ensure you follow the guide for your version [https://dev.mysql.com/doc/refman/8.0/en/replication-gtids-howto.html](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/mysql-replication-gtid.html) and that `GTID` mode is `ON`. #### Method 2: Create a table in the source database If you cannot enable GTID mode, you will need to create the table and give permissions to the `streamkap_user`. The Connector will use this collection for managing snapshots. The examples below use `streamkap_signal` as the signal table name, but you can choose any name. During [Streamkap Setup](#3-snapshot-settings), provide the full path to your signal table in `database.table` format (e.g., `streamkap.streamkap_signal`). ```SQL SQL theme={null} -- Create the schema CREATE SCHEMA streamkap; CREATE TABLE streamkap_signal ( id VARCHAR(255) PRIMARY KEY, type VARCHAR(32) NOT NULL, data VARCHAR(2000) NULL ); GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_signal TO 'streamkap_user'@'%'; ``` ### 7. Heartbeats Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. For read-write connections (when **Read only** is **No** during Streamkap Setup), the Connector writes to the heartbeat table directly. ```SQL SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Switch to the streamkap schema USE streamkap; -- Create the heartbeat table with id, text, and last_update fields CREATE TABLE streamkap_heartbeat ( id INT AUTO_INCREMENT PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Grant permission to the Streamkap user GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO 'streamkap_user'@'%'; -- Insert the first row into the heartbeat table INSERT INTO streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` For read-only connections (when **Read only** is **Yes** during Streamkap Setup), the Connector cannot write to the heartbeat table directly. Instead, you must configure a scheduled job on the **primary** database to generate artificial traffic. These changes will replicate to the read replica, which the Connector then consumes. **Run these commands on the primary database, not the read replica.** The heartbeat table and scheduled event must be created on the primary database. The changes will automatically replicate to the read replica. The MySQL Event Scheduler flag must be enabled on your database (`event_scheduler=ON`). See your provider's documentation: Modifying parameters in a DB parameter group in Amazon RDS Aurora MySQL configuration parameters reference Configuring database flags Server parameters Check if the event scheduler is enabled: ```sql SQL theme={null} SHOW VARIABLES WHERE VARIABLE_NAME = 'event_scheduler'; ``` **Making this setting persistent** For self-hosted MySQL, add `event_scheduler = ON` to your MySQL configuration file (usually `my.cnf` or `my.ini`) in the `[mysqld]` section. For managed databases (RDS, Cloud SQL, Azure), configure the `event_scheduler` parameter through your provider's console or CLI. ```sql SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Create the heartbeat table CREATE TABLE streamkap.streamkap_heartbeat ( id INT AUTO_INCREMENT PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Insert the initial row INSERT INTO streamkap.streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` ```sql SQL theme={null} CREATE EVENT streamkap.streamkap_heartbeat_event ON SCHEDULE EVERY 1 MINUTE DO UPDATE streamkap.streamkap_heartbeat SET text = 'updated_heartbeat', last_update = CURRENT_TIMESTAMP WHERE id = 1; ``` Whichever database user is used to create and run the event scheduler (often the MySQL root user or a dedicated event scheduler user) needs appropriate permissions on the heartbeat table. Additionally, the Streamkap user also needs permissions to monitor the heartbeat table. ```sql SQL theme={null} GRANT EVENT ON streamkap.* TO {event scheduler user}; GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO {event scheduler user}; -- Grant permissions to the Streamkap user for monitoring and diagnostics GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO 'streamkap_user'@'%'; ``` ```sql SQL theme={null} -- View all scheduled events in the streamkap schema SHOW EVENTS IN streamkap; -- View event details SELECT * FROM information_schema.EVENTS WHERE EVENT_SCHEMA = 'streamkap'; -- Disable an event temporarily ALTER EVENT streamkap.streamkap_heartbeat_event DISABLE; -- Enable an event ALTER EVENT streamkap.streamkap_heartbeat_event ENABLE; -- Drop an event DROP EVENT IF EXISTS streamkap.streamkap_heartbeat_event; ``` *** ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **MySQL**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Hostname**: Specify the hostname. * **Port**: Default is `3306`. * **Connect via SSH Tunnel**: The Connector will connect to an SSH server in your network which has access to your database. This is necessary if the Connector cannot connect directly to your database. * See [SSH Tunnel](/ssh-tunnel) for setup instructions. * **Username**: Username to access the database. By default, Streamkap scripts use `streamkap_user`. * **Password**: Password to access the database. * **Read only**: Whether or not to use a read-only connection. Requires GTID to be enabled on the source database. See [Enable GTID](#method-1%3A-enable-gtid-recommended) for more information. * **Heartbeats**: Enabled by default. * For **read-write** connections, configure a heartbeat table in the source database and set **Heartbeat Table Database**. See [Heartbeats](#7-heartbeats) for setup instructions. * For **read-only** connections, configure a scheduled heartbeat event on the primary database using the MySQL Event Scheduler, and include the heartbeat table in [Schema and Table Capture](#5-schema-and-table-capture). See [Heartbeats](#7-heartbeats) for setup instructions. * **Connection Timezone**: The timezone of your database. **Timezone conversion** MySQL converts `TIMESTAMP` values from the current time zone to UTC for storage, and back from UTC to the current time zone for retrieval. By default, the current time zone for each connection is the database server's time zone but this option allows you to override that. As long as the time zones remain the *same*, you get back the same value you store. We recommend using the default `SERVER` option which attempts to detect the session time zone from the values configured on the MySQL server session variables 'time\_zone' or 'system\_time\_zone'. It also reduces the chance of problems with daylight savings adjustment 'fall back' and 'spring forward'. If either time zones change, an [ad-hoc snapshot](/snapshots#triggering-a-snapshot) is recommended so your source and destination timestamps are consistent. ### 3. Snapshot Settings If you set **Read only** to **No**, you will need to create a snapshot signal table and give permissions to the `streamkap_user`. See [Enable Snapshots](#6-enable-snapshots) for setup instructions. * **Signal Table**: Full path to the signal table including database and table name (e.g., `streamkap.streamkap_signal`). This table is used for incremental snapshotting. See [Enable Snapshots](#6-enable-snapshots) for setup instructions. ### 4. Advanced Parameters * **Represent binary data as**: Specifies how the data for binary columns e.g. `blob`, `binary`, `varbinary` should be interpreted. Your destination for this data can impact which option you choose. Default is `bytes`. * **Capture Only Captured Databases DDL**: Used to control whether the connector records schema structures from all databases defined in the server (the default) or only those databases for which you've explicitly configured the connector. Specify `true` to capture schema history only for the specific databases you've configured. This is particularly valuable when databases are large, to reduce the volume of DDL stored in the schema history topic. It also improves startup times when the connector restarts or recovers from failures. Default is `false`. See [Schema History Optimization](/schema-history-optimization) for details. * **Capture Only Captured Tables DDL**: Used to control whether the connector records the schema structure for all tables in the configured databases (the default) or only the tables whose changes the connector captures. Specify `true` to capture schema history only for the specific tables you've configured. This is particularly valuable when tables are large, to reduce the volume of DDL statements stored in the schema history topic. It also improves startup times when the connector restarts or recovers from failures. Default is `false`. See [Schema History Optimization](/schema-history-optimization) for details. Click **Next**. ### 5. Schema and Table Capture * **Add Schemas/Tables**: Specify the schema(s) and table(s) for capture. * You can bulk upload here. The format is a simple list of schemas and tables, with each entry on a new row. Save as a `.csv` file without a header. **CDC only captures base tables, not Views** Change Data Capture reads the MySQL binary log (binlog), which only records changes to physical tables. Database Views are query-time computations with no physical storage—they don't generate binlog entries. **What you cannot capture:** Views, temporary tables, MEMORY tables, BLACKHOLE tables, or system tables (information\_schema, performance\_schema). **Solution:** Specify only the underlying base tables that feed your views. You can recreate the view logic in your destination or transformation layer. **VIRTUAL generated columns are not streamed** MySQL [VIRTUAL generated columns](https://dev.mysql.com/doc/refman/8.0/en/create-table-generated-columns.html) are computed when rows are read and are **not** written to the binary log, so live streaming cannot capture or update their values. (STORED generated columns are written to the binary log and stream normally.) Snapshots read the table directly, so they **do** capture a value for VIRTUAL columns — but because streaming cannot keep it current, that value will become stale. **We recommend excluding VIRTUAL generated columns from capture:** in the Source's **Settings → Advanced** section, set **Column Selection Mode** to **Exclusion** and add the column to the **Column Exclusion List** (fully-qualified, e.g. `database.table.column`). If you need the value at your destination, calculate it there — or with a [transform](/transforms) — from the columns it is derived from. Click **Save**. **Adding tables programmatically** -- You can add tables to an existing source via the API or Terraform using the `table.include.list.user.defined` parameter (Terraform: `table_include_list_user_defined`). See the [REST API](/api) for getting started with the API, or [Terraform Resources](/terraform-resources#sources) for Terraform source configuration. **Have questions?** See the [MySQL Source FAQ](/mysql-source-faq) for answers to common questions about MySQL sources, troubleshooting, and best practices. # Amazon RDS Oracle Source: https://docs.streamkap.com/amazon-rds-oracle Oracle Change Data Capture Setup on Amazon RDS with Streamkap ## Prerequisites **Multi-tenant databases** Due to the AWS RDS [limitation](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Oracle.Concepts.CDBs.html#Oracle.Concepts.single-tenant-limitations) where you cannot connect to the CDB and only being able to create [local users](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Oracle.Concepts.CDBs.html#Oracle.Concepts.single-tenant.users), Streamkap does not support Oracle multi-tenant databases on AWS RDS. **Standby databases** An Oracle database can be configured with either a physical or a logical standby database for recovery after a production failure. At this time, Streamkap does not support them. * Oracle 12c or above, Standard or Enterprise Edition * AWS RDS endpoint and port of the database * (single-tenant architecture): Database name * The Oracle database master user credentials or equivalent * An AWS console account with administrator access to the database ## Oracle Setup Streamkap's Oracle Source supports **LogMiner**, **XStream**, and **OpenLogReplicator** as log reading methods. For the Connector to ingest changes from your database it is dependent on Oracle's **redo** logs and **archive** logs. It is important that these redo logs are large enough and the archive logs are retained for long enough to ensure all changes are captured. ### 1. Grant Database Access * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your database. ### 2. Enable Archive Logs When redo logs fill up, Oracle archives *groups* of them into archive logs. For Oracle on RDS, archiving is enabled when **AWS automated backups** is enabled. #### Enable AWS Automated Backups **Reboot required** When automated backups are enabled, your RDS instance and database are taken **offline** and a backup is immediately created which can take some time. * Sign in to and open the Amazon RDS console at [https://console.aws.amazon.com/rds/](https://console.aws.amazon.com/rds/). * In the left-side navigation menu, choose **Databases**. The **Databases** page should appear. * Select the DB instance that you want to modify. * Click **Modify**. The **Modify DB instance** page should appear. * For **Backup retention period**, choose at least **1** or higher. * Click **Continue**. * Select **Apply immediately**. * On the confirmation page, click **Modify DB instance** to enable automated backups. To check, you can connect to the database and run this query: ```SQL SQL theme={null} SELECT NAME, LOG_MODE FROM V$DATABASE; ``` If the `LOG_MODE` is `ARCHIVELOG` then it is enabled. Or: * Sign into your AWS account. * Once signed in, navigate to the **RDS** dashboard by clicking on **Services** in the top left corner, **Databases** and then **RDS** or by typing **RDS** into the top left search box. * From the AWS RDS Dashboard, click on **DB Instances** or **Databases** in the left side menu. * Click on the **DB identifier** for the Oracle database you want Streamkap to use. * Click on the **Maintenance & backups** tab. * Under the **Backups** section, check **Automated backups** says "Enabled (*N* Days)". #### Configure Log Retention Archive logs should be retained for at least 24 hours. However, we recommend retaining them for longer, if possible. Too short a retention period and changes may not be captured and processed. **Retention periods and database storage** Archive logs are retained on your **database instance** using up its storage capacity. It is important to make sure it has enough space, otherwise, performance issues and outages can occur. In general, the more tables (and columns) there are, the more capacity is required. For the Connector, additional capacity is essential because it depends on Oracle's supplemental logging. Assuming your Oracle database has supplemental logging enabled already, to estimate storage capacity you can look at the last 1 hour of log storage usage and multiply that by `archivelog retention hours`. Here's an example script for that: ```sql SQL theme={null} SELECT SUM(BLOCKS * BLOCK_SIZE) bytes, -- usage SUM(BLOCKS * BLOCK_SIZE) * 24 estimated_bytes -- assuming 24 hours archivelog retention FROM V$ARCHIVED_LOG WHERE FIRST_TIME >= SYSDATE-(1/24) -- last hour AND DEST_ID=1; ``` ```SQL SQL theme={null} -- Set log retention EXECUTE rdsadmin.rdsadmin_util.set_configuration('archivelog retention hours', 72); COMMIT; -- The commit is required for the change to take effect. ``` ### 3. Enable LogMiner For the Connector to query the redo and archive logs, it is dependent on the [Oracle LogMiner](https://docs.oracle.com/en/database/oracle/oracle-database/19/sutil/oracle-logminer-utility.html) utility. To enable that, supplemental logging must be enabled. ```SQL SQL theme={null} EXECUTE rdsadmin.rdsadmin_util.alter_supplemental_logging('ADD','ALL'); ``` To confirm if supplemental logging has been enabled, run this query: ```SQL SQL theme={null} SELECT NAME, SUPPLEMENTAL_LOG_DATA_MIN FROM V$DATABASE; ``` If the `SUPPLEMENTAL_LOG_DATA_MIN` is `YES` then it is enabled. #### Resize Redo Logs An Amazon RDS Oracle instance starts with four, online redo log files, 128MB each. That is too small; more log files are necessary, and the logs should be resized to at least 1024MB or more, especially for production databases. Before making any changes, run this query to check the current log sizes: ```SQL SQL theme={null} SELECT GROUP#, BYTES/1024/1024 SIZE_MB, STATUS FROM V$LOG ORDER BY 1; ``` | GROUP# | SIZE\_MB | STATUS | | ------ | -------- | -------- | | 1 | 128 | INACTIVE | | 2 | 128 | CURRENT | | 3 | 128 | INACTIVE | | 4 | 128 | INACTIVE | Now let's add 8 new, larger logs - that will eventually replace the smaller logs - and run the query from earlier to confirm they have been created. ```SQL SQL theme={null} EXEC rdsadmin.rdsadmin_util.add_logfile(bytes => 1073741824); EXEC rdsadmin.rdsadmin_util.add_logfile(bytes => 1073741824); EXEC rdsadmin.rdsadmin_util.add_logfile(bytes => 1073741824); EXEC rdsadmin.rdsadmin_util.add_logfile(bytes => 1073741824); EXEC rdsadmin.rdsadmin_util.add_logfile(bytes => 1073741824); EXEC rdsadmin.rdsadmin_util.add_logfile(bytes => 1073741824); EXEC rdsadmin.rdsadmin_util.add_logfile(bytes => 1073741824); EXEC rdsadmin.rdsadmin_util.add_logfile(bytes => 1073741824); SELECT GROUP#, BYTES/1024/1024 SIZE_MB, STATUS FROM V$LOG ORDER BY 1; ``` | GROUP# | SIZE\_MB | STATUS | | ------ | -------- | -------- | | 1 | 128 | INACTIVE | | 2 | 128 | CURRENT | | 3 | 128 | INACTIVE | | 4 | 128 | INACTIVE | | 5 | 1024 | UNUSED | | 6 | 1024 | UNUSED | | ... | ... | ... | For all *groups* showing as `INACTIVE`, drop them and run the query from earlier to confirm they have been dropped: ```SQL SQL theme={null} -- Replace {group_number} placeholder and execute the procedure as required EXEC rdsadmin.rdsadmin_util.drop_logfile(grp => {group_number}); SELECT GROUP#, BYTES/1024/1024 SIZE_MB, STATUS FROM V$LOG ORDER BY 1; ``` | GROUP# | SIZE\_MB | STATUS | | ------ | -------- | ------- | | 2 | 128 | CURRENT | | 5 | 1024 | UNUSED | | 6 | 1024 | UNUSED | | ... | ... | ... | Now let's switch the `CURRENT` log so we can drop it and run the query from earlier to confirm it has switched. ```SQL SQL theme={null} EXEC rdsadmin.rdsadmin_util.switch_logfile; SELECT GROUP#, BYTES/1024/1024 SIZE_MB, STATUS FROM V$LOG ORDER BY 1; ``` | GROUP# | SIZE\_MB | STATUS | | ------ | -------- | ------- | | 2 | 128 | ACTIVE | | 5 | 1024 | CURRENT | | 6 | 1024 | UNUSED | | ... | ... | ... | If the `STATUS` of the log we want to drop is still `ACTIVE`, we need to issue a checkpoint to make it `INACTIVE`. ```SQL SQL theme={null} EXEC rdsadmin.rdsadmin_util.checkpoint; ``` Finally, let's drop the remaining log file and run the query from earlier to confirm we now have 8 larger logs. ```SQL SQL theme={null} -- Replace {group_number} placeholder and execute the procedure as required EXEC rdsadmin.rdsadmin_util.drop_logfile(grp => {group_number}); ``` | GROUP# | SIZE\_MB | STATUS | | ------ | -------- | ------- | | 5 | 1024 | CURRENT | | 6 | 1024 | UNUSED | | ... | ... | ... | ### 4. Create Database User The script below creates a database user with privileges for the Connector. ```SQL Non-CDB (Single-tenant) theme={null} -- Replace {...} placeholders as needed CREATE USER STREAMKAP_USER IDENTIFIED BY {password}; CREATE TABLESPACE STREAMKAP_LOGMINER_TBS DATAFILE SIZE 25M AUTOEXTEND ON MAXSIZE UNLIMITED; ALTER USER STREAMKAP_USER DEFAULT TABLESPACE STREAMKAP_LOGMINER_TBS; ALTER USER STREAMKAP_USER QUOTA UNLIMITED ON STREAMKAP_LOGMINER_TBS; -- Grant permissions GRANT CREATE SESSION TO STREAMKAP_USER; -- Allows the Connector to use LogMiner GRANT LOGMINING TO STREAMKAP_USER; -- Flashback queries used for performing initial snapshots of the data GRANT FLASHBACK ANY TABLE TO STREAMKAP_USER; GRANT SELECT ANY TRANSACTION TO STREAMKAP_USER; -- Required for schema history when performing initial snapshots GRANT SELECT_CATALOG_ROLE TO STREAMKAP_USER; GRANT EXECUTE_CATALOG_ROLE TO STREAMKAP_USER; -- Connector creates a table for explicitly managing the flushing of internal log buffers (LGWR) GRANT CREATE TABLE TO STREAMKAP_USER; GRANT CREATE SEQUENCE TO STREAMKAP_USER; -- Read-only privileges on system tables containing redo, archive log and current transaction state EXECUTE rdsadmin.rdsadmin_util.grant_sys_object('V_$DATABASE','STREAMKAP_USER','SELECT'); EXECUTE rdsadmin.rdsadmin_util.grant_sys_object('V_$LOG','STREAMKAP_USER','SELECT'); EXECUTE rdsadmin.rdsadmin_util.grant_sys_object('V_$LOG_HISTORY','STREAMKAP_USER','SELECT'); EXECUTE rdsadmin.rdsadmin_util.grant_sys_object('V_$LOGMNR_LOGS','STREAMKAP_USER','SELECT'); EXECUTE rdsadmin.rdsadmin_util.grant_sys_object('V_$LOGMNR_CONTENTS','STREAMKAP_USER','SELECT'); EXECUTE rdsadmin.rdsadmin_util.grant_sys_object('V_$LOGMNR_PARAMETERS','STREAMKAP_USER','SELECT'); EXECUTE rdsadmin.rdsadmin_util.grant_sys_object('V_$LOGFILE','STREAMKAP_USER','SELECT'); EXECUTE rdsadmin.rdsadmin_util.grant_sys_object('V_$ARCHIVED_LOG','STREAMKAP_USER','SELECT'); EXECUTE rdsadmin.rdsadmin_util.grant_sys_object('V_$ARCHIVE_DEST_STATUS','STREAMKAP_USER','SELECT'); EXECUTE rdsadmin.rdsadmin_util.grant_sys_object('V_$TRANSACTION','STREAMKAP_USER','SELECT'); -- Grant the Streamkap user permission to read each schema and table you wish to sync GRANT SELECT ON {schema}.{table} TO STREAMKAP_USER; -- Alternatively, you can grant access to all -- GRANT SELECT ANY TABLE TO STREAMKAP_USER; ``` ### 5. Enable Snapshots To backfill your data, the Connector needs to be able to perform Snapshots (See [Snapshots & Backfilling](/snapshots) for more information). To enable this process, a table must be created for the Connector to use. The examples below use `STREAMKAP_SIGNAL` as the signal table name, but you can choose any name. During [Streamkap Setup](#3-snapshot-settings), provide the full path to your signal table in `schema.table` format (e.g., `STREAMKAP_USER.STREAMKAP_SIGNAL`). ```SQL Non-CDB (Single-tenant) theme={null} -- Create the table CREATE TABLE STREAMKAP_USER.STREAMKAP_SIGNAL ( id VARCHAR2(255) PRIMARY KEY, type VARCHAR2(32) NOT NULL, data VARCHAR2(2000) NULL ); -- Grant necessary privileges on the table to the user GRANT SELECT, UPDATE, INSERT, DELETE ON STREAMKAP_USER.STREAMKAP_SIGNAL TO STREAMKAP_USER; ``` ### 6. Heartbeats Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. For read-write connections (when **Read only** is **No** during Streamkap Setup), the Connector writes to the heartbeat table directly. ```SQL SQL theme={null} -- Replace {...} placeholders as needed ALTER SESSION SET CONTAINER={PDB}; -- Create the heartbeat table with id, text, and last_update fields CREATE TABLE STREAMKAP_USER.STREAMKAP_HEARTBEAT ( id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY, text VARCHAR2(4000), last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Grant permission to the Streamkap user GRANT SELECT, UPDATE, INSERT, DELETE ON STREAMKAP_USER.STREAMKAP_HEARTBEAT TO STREAMKAP_USER; -- Grant necessary privileges on the table to the common user GRANT SELECT, UPDATE, INSERT, DELETE ON STREAMKAP_USER.STREAMKAP_HEARTBEAT TO C##STREAMKAP_USER; -- Insert the first row into the heartbeat table INSERT INTO STREAMKAP_USER.STREAMKAP_HEARTBEAT (text) VALUES ('test_heartbeat'); ``` *** ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **Oracle**. * Select **Amazon RDS**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Hostname**: Specify the database endpoint. * **Port**: Default is `1521`. * **Connect via SSH Tunnel**: The Connector will connect to an SSH server in your network which has access to your database. This is necessary if the Connector cannot connect directly to your database. * See [SSH Tunnel](/ssh-tunnel) for setup instructions. * **Username** (case sensitive): Username to access the database. By default, Streamkap scripts use `STREAMKAP_USER`. * **Password**: Password to access the database. * **Database**: The database name (single-tenant architecture). * **Heartbeats**: * **Heartbeat Table Schema**: Streamkap will use a table in this schema to manage heartbeats. Usually this is the same as the Signal Table. See [Heartbeats](#6-heartbeats) for setup instructions. ### 3. Snapshot Settings * **Signal Table**: Full path to the signal table including schema and table name (e.g., `STREAMKAP_USER.STREAMKAP_SIGNAL`). This table is used for incremental snapshotting. See [Enable Snapshots](#5-enable-snapshots) for setup instructions. ### 4. Advanced Parameters * **Represent binary data as**: Specifies how the data for binary columns should be interpreted. Your destination for this data can impact which option you choose. Default is `bytes`. * **Capture Only Captured Databases DDL**: Used to control whether the connector records schema structures from all databases defined in the server (the default) or only those databases for which you've explicitly configured the connector. Specify `true` to capture schema history only for the specific databases you've configured. This is particularly valuable when databases are large, to reduce the volume of DDL stored in the schema history topic. It also improves startup times when the connector restarts or recovers from failures. Default is `false`. See [Schema History Optimization](/schema-history-optimization) for details. * **Capture Only Captured Tables DDL**: Used to control whether the connector records the schema structure for all tables in the configured databases (the default) or only the tables whose changes the connector captures. Specify `true` to capture schema history only for the specific tables you've configured. This is particularly valuable when tables are large, to reduce the volume of DDL statements stored in the schema history topic. It also improves startup times when the connector restarts or recovers from failures. Default is `false`. See [Schema History Optimization](/schema-history-optimization) for details. Click **Next**. ### 5. Schema and Table Capture * **Add Schemas/Tables**: Specify the schema(s) and table(s) for capture. * You can bulk upload here. The format is a simple list of schemas and tables, with each entry on a new row. Save as a `.csv` file without a header. **CDC only captures base tables, not Views** Change Data Capture reads Oracle redo logs via LogMiner, which only record changes to physical tables. Database Views are query-time computations with no physical storage—they don't generate redo log entries. **What you cannot capture:** Views, materialized views (capture base tables instead), global temporary tables, external tables, or system tables (DBA\_\*, ALL\_\*, USER\_\*). **Solution:** Specify only the underlying base tables that feed your views. You can recreate the view logic in your destination or transformation layer. Click **Save**. **Have questions?** See the [Oracle Source FAQ](/oracle-source-faq) for answers to common questions about Oracle sources, AWS RDS specifics, troubleshooting, and best practices. # Amazon RDS PostgreSQL Serverless Source: https://docs.streamkap.com/amazon-rds-postgresql-serverless Configure PostgreSQL CDC on Amazon RDS Serverless with Streamkap, covering parameter groups, replication user setup, and heartbeat options. ## Prerequisites * PostgreSQL version ≥ 10 * A database user with sufficient privileges to configure the database, including enabling logical replication and creating users ## PostgreSQL Setup ### 1. Grant Database Access * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your database. ### 2. Enable Logical Replication **Aurora Serverless Read Replicas** Aurora Serverless Read Replicas only support **physical** not logical replication. Please configure and use the primary server with Streamkap. Logical replication is a method of replicating data objects and their changes, based upon their replication identity (usually a primary key). The Connector relies on PostgreSQLs implementation of this. * Open the Amazon RDS console at [https://console.aws.amazon.com/rds/](https://console.aws.amazon.com/rds/). * In the navigation pane, choose **Parameter groups**. **Default parameter groups can't be modified** If the DB instance is using a default parameter group, create a new one: * Choose **Create parameter group**. * Enter a **Parameter group name** and **Description**. * For **Engine type**, choose your database engine. * For **Parameter group family**, choose a DB parameter group family. * For **Type**, choose **DB Cluster Parameter Group**. * Choose **Create**. * Select the parameter group to edit. * Choose **Edit** from **Actions**. * Set `rds.logical_replication` to `1`. * Set `wal_sender_timeout` to `0`. A nonzero value may cause disconnects in low/intermittent traffic databases. Enable [Heartbeats](#6-heartbeats) or set an appropriate value if needed. * Choose **Save changes**. If you created a new parameter group, associate it with your Aurora DB cluster: * In the navigation pane, choose **Databases** and select the target DB cluster. * Choose **Modify**. * Change the **DB cluster parameter group** setting. * Choose **Continue** and review modifications. * The change is applied immediately, regardless of the **Scheduling of modifications** setting. * On the confirmation page, choose **Modify cluster**. A reboot is required to apply the changes. **WAL and database storage** The write-ahead logs take up disk space on your database. If you run out of disk space, you may face an interruption to your database. To mitigate this: * Ensure that your database server has ample free space. * Enable sufficient space with auto growth on the database. * Monitor the WAL growth and configure alerts. * Configure the publication (See [Create Publication & Slot](#5-create-publication--slot)) to contain only tables you need. ### 3. Create Database User It's recommended to create a separate user and role for the Connector to access your PostgreSQL database. Below is an example script that does that. ```SQL SQL theme={null} -- Replace { ... } placeholders as required CREATE USER streamkap_user PASSWORD '{password}'; -- Create a role for Streamkap CREATE ROLE streamkap_role; GRANT streamkap_role TO streamkap_user; GRANT rds_replication TO streamkap_role; -- Grant Streamkap permissions on the database, schema and all tables to capture GRANT CONNECT ON DATABASE "{database}" TO streamkap_role; GRANT CREATE, USAGE ON SCHEMA "{schema}" TO streamkap_role; GRANT SELECT ON ALL TABLES IN SCHEMA "{schema}" TO streamkap_role; ALTER DEFAULT PRIVILEGES IN SCHEMA "{schema}" GRANT SELECT ON TABLES TO streamkap_role; ``` ### 4. Enable Snapshots To backfill your data, the Connector needs to be able to perform snapshots. See [Snapshots & Backfilling](/snapshots) for more information. To enable this feature, there are 2 methods available: #### Method 1: Enable read only connection Requires PostgreSQL version 13 or higher. This method is recommended if you cannot create a table in the source database and grant the Connector read/write privileges to that. * Set **Read only** to **Yes** during Streamkap Setup. No other configuration should be necessary. #### Method 2: Create a table in the source database You will need to create the table and give necessary permissions to the `streamkap_user`. The Connector will use this collection for managing snapshots. Below is an example script that does that. The examples below use `streamkap_signal` as the signal table name, but you can choose any name. During [Streamkap Setup](#3-snapshot-settings), provide the full path to your signal table in `schema.table` format (e.g., `streamkap.streamkap_signal`). ```SQL SQL theme={null} -- Create the schema CREATE SCHEMA streamkap; -- Switch to the newly created schema SET search_path TO streamkap; -- Create the table CREATE TABLE streamkap_signal ( id VARCHAR(255) PRIMARY KEY, type VARCHAR(32) NOT NULL, data VARCHAR(2000) NULL ); -- Grant necessary privileges on the table to the role GRANT CREATE, USAGE ON SCHEMA streamkap TO streamkap_role; GRANT SELECT ON ALL TABLES IN SCHEMA streamkap TO streamkap_role; GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE streamkap_signal TO streamkap_role; ``` **Publications and signal tables** When you create the PostgreSQL publication in the next step, if you choose to specify tables for capture instead of all tables, you **must** include your signal table. ### 5. Heartbeats Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. For read-write connections (when **Read only** is **No** during Streamkap Setup), the Connector writes to the heartbeat table directly. ```SQL SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Switch to the streamkap schema SET search_path TO streamkap; -- Create the heartbeat table with id, text, and last_update fields CREATE TABLE streamkap_heartbeat ( id SERIAL PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Grant permission to the Streamkap user GRANT USAGE ON SCHEMA streamkap TO streamkap_user; GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE streamkap_heartbeat TO streamkap_user; -- Insert the first row into the heartbeat table INSERT INTO streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` **Heartbeat tables and PostgreSQL publications** If the `streamkap_pub` publication created during PostgreSQL Setup was for specific tables e.g. `CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, table3, ...;` instead of `FOR ALL TABLES;`, you **must** add the heartbeat table to the publication: `ALTER PUBLICATION streamkap_pub ADD TABLE streamkap.streamkap_heartbeat;`. For read-only connections (when **Read only** is **Yes** during Streamkap Setup), the Connector cannot write to the heartbeat table directly. Instead, you must configure a scheduled job on the **primary** database to generate artificial traffic. These changes will replicate to the read replica, which the Connector then consumes. **Run these commands on the primary database, not the read replica.** The heartbeat table and scheduled job must be created on the primary database. The changes will automatically replicate to the read replica. The `pg_cron` extension must be allowed in your database's configuration. See your provider's documentation for enabling extensions: Scheduling maintenance with pg\_cron Scheduling maintenance with pg\_cron PostgreSQL extensions PostgreSQL extensions pg\_cron extension pg\_cron extension Supported extensions Once allowed, create the extension: ```sql SQL theme={null} CREATE EXTENSION IF NOT EXISTS pg_cron; ``` ```sql SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Create the heartbeat table CREATE TABLE streamkap.streamkap_heartbeat ( id SERIAL PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Insert the initial row INSERT INTO streamkap.streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` ```sql SQL theme={null} -- Schedule a job to update the heartbeat table every minute SELECT cron.schedule( 'streamkap_heartbeat_job', '*/1 * * * *', $$UPDATE streamkap.streamkap_heartbeat SET text = 'updated_heartbeat', last_update = CURRENT_TIMESTAMP WHERE id = 1;$$ ); ``` Whichever database user is used to create and run the cron jobs (often the `postgres` user or a dedicated cron user) needs appropriate permissions on the heartbeat table. Additionally, the Streamkap user also needs permissions to monitor the heartbeat table. ```sql SQL theme={null} GRANT USAGE ON SCHEMA streamkap TO {cron user}; GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO {cron user}; -- Grant permissions to the Streamkap user for monitoring and diagnostics GRANT USAGE ON SCHEMA streamkap TO streamkap_user; GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO streamkap_user; ``` The `pg_cron` extension stores job execution history in the table `cron.job_run_details`. To prevent this table from growing indefinitely: ```sql SQL theme={null} SELECT cron.schedule( 'streamkap_cron_cleanup', '0 0 * * *', $$DELETE FROM cron.job_run_details WHERE end_time < now() - interval '7 days';$$ ); ``` ```sql SQL theme={null} -- View all scheduled jobs SELECT * FROM cron.job; -- View recent job execution history SELECT * FROM cron.job_run_details ORDER BY start_time DESC LIMIT 10; -- Unschedule a job (replace {jobid} with the actual job ID) SELECT cron.unschedule({jobid}); ``` **Heartbeat tables and PostgreSQL publications** If the `streamkap_pub` publication was created for specific tables (e.g., `CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, ...;`) instead of `FOR ALL TABLES`, you **must** add the heartbeat table to the publication: ```sql SQL theme={null} ALTER PUBLICATION streamkap_pub ADD TABLE streamkap.streamkap_heartbeat; ``` **Logical-message heartbeats** are an alternative to a heartbeat table. The Connector itself calls `pg_logical_emit_message()` on each beat, which appends a transactional record to the write-ahead log (WAL). PostgreSQL treats that record like any other committed change, so the replication slot advances and WAL files are released—without any new table, scheduled job, or write privileges on the source database. **When to choose this over a heartbeat table** Use **logical-message heartbeats** when you want zero footprint in the source database itself—no schemas, tables, scheduled jobs, or write grants to maintain. Use a **heartbeat table** when you want the heartbeat to be visible as an ordinary row that operators can `SELECT` to confirm liveness, or when the requirements below rule logical-message out. **Requirements** * **Streamkap must be connected directly to a primary**, not to a read replica or other hot standby. The Connector executes `pg_logical_emit_message()` against the database it's connected to, and that function writes to the WAL—hot standbys are read-only and will reject the call with an error like `cannot execute ... during recovery`. If your Streamkap source is configured against a replica, use the **Read-only connections** tab instead. * PostgreSQL **14 or later** on the primary. Earlier versions are not supported because the built-in `pgoutput` replication plugin only began streaming logical-decoding messages to the Connector in PostgreSQL 14. On PostgreSQL 13 and earlier, `pg_logical_emit_message()` still appends records to the WAL, but the Connector cannot read them and the replication slot will not advance. * The Streamkap user needs `SELECT` privileges only—no `INSERT`, `UPDATE`, or `DELETE` on any heartbeat table. * Logical replication must already be enabled for the Connector itself (this is a prerequisite for any PostgreSQL source, regardless of heartbeat mode). During [Connection Settings](#2-connection-settings): * Set **Heartbeats** to **Yes**. * Leave **Heartbeat Table Schema** blank. * Set **Use Logical-Message Heartbeat** to **Yes**. No further setup is required in the source database. The Connector will issue the heartbeat query once per minute using the standard Streamkap heartbeat interval. After enabling, you can confirm that the replication slot is advancing against an otherwise idle database. Run on the primary: ```sql SQL theme={null} SELECT slot_name, confirmed_flush_lsn, restart_lsn FROM pg_replication_slots WHERE slot_name = 'streamkap_pgoutput_slot'; ``` Re-run the query after one or two minutes. Both `confirmed_flush_lsn` and `restart_lsn` should move forward. **What the Connector sends** When this mode is enabled, the Connector issues the following query on each beat: ```sql SQL theme={null} SELECT pg_logical_emit_message(true, '__debezium-heartbeat', now()::varchar); ``` The first argument (`true`) makes the emit **transactional**. Non-transactional emits advance the replication slot's `confirmed_flush_lsn` but do not recompute global WAL retention across all slots, so WAL files can still accumulate on an idle source database—transactional emits avoid that. The transactional cost is negligible (one extra transaction ID per beat, well below PostgreSQL's wraparound horizon at the default one-minute interval). The heartbeat record is filtered out before reaching Kafka, so it does not appear as a record in any topic in your pipeline. ### 6. Create Publication & Slot Publications contain a set of change events for the tables you want the Connector to capture. * Create a publication for your tables. You can create a publication for all tables or selected tables. ```SQL SQL - All Tables theme={null} -- Create a publication for all tables to capture CREATE PUBLICATION streamkap_pub FOR ALL TABLES; ``` ```SQL SQL - Selected Tables theme={null} -- Create a publication for specific tables to capture CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, table3, ...; -- Verify the tables to capture were added to the publication SELECT * FROM pg_publication_tables where pubname = 'streamkap_pub'; ``` **Altering publications** You cannot alter `FOR ALL TABLES` publications to include/exclude tables. If you set up a `FOR ALL TABLES` publication and later decide to change that, you have to drop the publication and create another to include specific tables e.g. `CREATE PUBLICATION ... TABLE table1, table2, table3, ...`. However, any change events that occur before the new publication's created will not be included in it, so a snapshot's required to ensure they are not missed by your Streamkap pipelines. You should also stop the [Source](https://app.streamkap.com/connectors/sources) *before* changing the publication. A replication slot represents a stream of change events the Connector reads from. * Create a replication slot. ```SQL SQL theme={null} -- Create a logical replication slot SELECT pg_create_logical_replication_slot('streamkap_pgoutput_slot', 'pgoutput'); -- Verify the replication slot is working (this may take a few moments to return the count) SELECT count(*) FROM pg_logical_slot_peek_binary_changes('streamkap_pgoutput_slot', null, null, 'proto_version', '1', 'publication_names', 'streamkap_pub'); ``` **Replication slot auto-creation** If the Connector cannot find the replication slot named in [Replication Settings](/amazon-rds-postgresql-serverless#4-replication-settings) during Streamkap Setup, it will attempt to create one automatically as it cannot read the database transaction logs without one. This is assuming the database user has been granted the `rds_replication` role as per [Create Database User](/amazon-rds-postgresql-serverless#3-create-database-user). #### Excluding columns from replication By default a publication streams every column of each captured table. On **PostgreSQL 15+** you can attach a *column list* to a table in the publication so that only the listed columns are replicated — any column you leave out is excluded at the source and never enters the replication stream. ```SQL SQL - Selected Columns theme={null} -- Only the listed columns are published; every other column is excluded. -- Each table's list MUST include its replica identity (primary key), -- otherwise UPDATE/DELETE for that table cannot be replicated. CREATE PUBLICATION streamkap_pub FOR TABLE public.users (id, email, created_at, updated_at), public.orders (id, user_id, status, total), public.products (id, sku, name, price); -- Add or change a table's column list on an existing publication ALTER PUBLICATION streamkap_pub SET TABLE public.users (id, email, updated_at); -- Verify the published columns per table SELECT pubname, schemaname, tablename, attnames FROM pg_publication_tables WHERE pubname = 'streamkap_pub'; ``` * Requires **PostgreSQL 15 or later**. On earlier versions a publication always streams all columns. * Each table's column list **must include the table's replica identity** — its primary key, or the columns from `REPLICA IDENTITY USING INDEX` / `FULL`. Otherwise `UPDATE` and `DELETE` events for that table are not replicated. * Column lists are only valid with `FOR TABLE`, not `FOR ALL TABLES`. A table listed without parentheses publishes all of its columns, so you can mix restricted and unrestricted tables in one publication. A publication column list only governs **streamed (CDC) changes**. The initial snapshot reads columns directly from the table, so an excluded column can still appear in snapshot records. If a column must never be captured, exclude it from the snapshot as well, not only from the publication. ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **PostgreSQL**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Hostname**: Specify the hostname. * **Port**: Default is `5432`. * **Connect via SSH Tunnel**: The Connector will connect to an SSH server in your network which has access to your database. This is necessary if the Connector cannot connect directly to your database. * See [SSH Tunnel](/ssh-tunnel) for setup instructions. * **Username**: Username to access the database. By default, Streamkap scripts use `streamkap_user`. * **Password**: Password to access the database. * **Database**: Specify the database to stream data from. * **Read only**: Whether or not to use a read-only connection. Requires PostgreSQL version 13 or higher. * **Heartbeats**: Enabled by default. Choose one of three modes—see [Heartbeats](#5-heartbeats) for setup instructions: * **Read-write** connections: configure a heartbeat table in the source database and set **Heartbeat Table Schema**. * **Read-only** connections: configure a scheduled heartbeat job on the primary database using `pg_cron`, and include the heartbeat table in [Schema and Table Capture](#6-schema-and-table-capture). * **Logical-message** (PostgreSQL 14+, works under read-only): set **Use Logical-Message Heartbeat** to **Yes** and leave **Heartbeat Table Schema** blank. No table or write privileges required on the source. ### 3. Snapshot Settings If you set Read only to **No**, you will need to create a snapshot signal table and give permissions to the `streamkap_user`. See [Enable Snapshots](/amazon-rds-postgresql-serverless#4-enable-snapshots) for setup instructions. * **Signal Table**: Full path to the signal table including schema and table name (e.g., `streamkap.streamkap_signal`). This table is used for incremental snapshotting. See [Enable Snapshots](#4-enable-snapshots) for setup instructions. ### 4. Replication Settings * **Replication Slot Name**: The name of the replication slot for the connector to use. Default is `streamkap_pgoutput_slot`. * **Publication Name**: The name of the publication for the connector to use. Default is `streamkap_pub`. ### 5. Advanced Parameters * **SSL mode**: Whether to use an encrypted connection to the PostgreSQL server. By default, it's required. * **Prefix with Database Name?**: Changes the format of topics to `DatabaseName_TopicName` * **Represent binary data as**: Specifies how the data for binary columns e.g. `blob`, `binary`, `varbinary` should be interpreted. Your destination for this data can impact which option you choose. Default is `bytes`. Click **Next**. ### 6. Schema and Table Capture * **Add Schemas/Tables**: Specify the schema(s) and table(s) for capture * You can bulk upload here. The format is a simple list of schemas and tables, with each entry on a new row. Save as a `.csv` file without a header. **CDC only captures base tables, not Views** Change Data Capture reads the PostgreSQL write-ahead log (WAL), which only records changes to physical tables. Database Views are query-time computations with no physical storage—they don't generate WAL entries. **What you cannot capture:** Views, temporary tables, unlogged tables, foreign tables (FDW), or system tables (information\_schema, pg\_catalog). **Solution:** Specify only the underlying base tables that feed your views. You can recreate the view logic in your destination or transformation layer. **Generated columns are not kept up to date** PostgreSQL [generated columns](https://www.postgresql.org/docs/current/ddl-generated-columns.html) (defined with `GENERATED ALWAYS AS (...) STORED`) are calculated by the database from other columns. PostgreSQL does **not** include them in the replication stream Streamkap reads, so live streaming cannot keep their values current. **We recommend excluding generated columns from capture.** In the Source's **Settings → Advanced** section, set **Column Selection Mode** to **Exclusion** and add the column to the **Column Exclusion List** (fully-qualified, e.g. `schema.table.column`). If you need the value at your destination, calculate it there — or with a [transform](/transforms) — from the columns it is derived from. **Snapshots:** An incremental snapshot (the watermarked **Filtered** and **Full** options) of a table that contains a generated column will **fail** unless the column is excluded. **Blocking** and **Parallel** snapshots are not affected, though any value they capture will become stale because streaming cannot update it. See [Snapshots & Backfilling](/snapshots). PostgreSQL 18 and later can optionally stream stored generated columns when the publication is created with `publish_generated_columns = stored`; earlier versions cannot. Click **Save**. ## Troubleshooting When upgrading the PostgreSQL database used by Streamkap, there are specific steps to prevent data loss and ensure continued operation. Streamkap handles network failures and outages well. If a monitored database stops, the connector resumes from the last recorded log sequence number (LSN) once communication is restored. It retrieves this offset and queries PostgreSQL for a matching LSN in the replication slot. A replication slot is required for change capture, but PostgreSQL removes slots during upgrades and doesn’t restore them. When the connector restarts, it requests the last known offset, but PostgreSQL cannot return it. Creating a new replication slot isn't enough to prevent data loss. New slots only track changes from their creation point and lack earlier offsets. The connector fetches its last known offset from Kafka but can't retrieve corresponding data from the new slot. It skips older change events and resumes from the latest log position, causing silent data loss with no warnings.

### Procedure Follow these steps to minimize data loss. Note that a few steps may require support from Streamkap. We recommend notifying us about your database upgrade ahead of time to ensure you have the necessary support. * Using your database's upgrade procedure, ensure writes to it have stopped. * Allow the connector to capture all change events before starting the upgrade procedure. Ask Streamkap to confirm this for you. **Interrupting change data capture** If all the events were not captured before stopping the Source and upgrading the database, you can perform a snapshot (after the procedure is completed) in Streamkap for that Source to ensure no change events were missed. * Assuming all events are captured, stop the Source in the Streamkap app. This flushes the last records and saves the last offset. * Stop the database and upgrade it using your upgrade procedure. Once the database is upgraded, and **before** allowing writes again: * Recreate the logical replication slot, otherwise, Streamkap will miss changes. See [Create Publication & Slot](/amazon-rds-postgresql-serverless#5-create-publication--slot). * Verify the publication for Streamkap exists; recreate it if necessary: [Create Publication & Slot](/amazon-rds-postgresql-serverless#5-create-publication--slot). **Update the Source in the Streamkap app** If you have chosen different names for the logical replication slot or publication, make sure to update them in the Streamkap setup page for the relevant PostgreSQL Source. In this case, contact Streamkap, as they may need to reset your Connector's offsets in this scenario. * Restore write access to the database. * Resume or restart the Source in the Streamkap app.
Introduced in PostgreSQL 13, the `REPLICA IDENTITY` table setting controls what data is logged for row updates and deletes. By default, only the primary key and Streamkap metadata column values are retained for deleted records. All other columns will be empty. This leaves you with an incomplete record. If you require - for auditing and historical tracking purposes - **all** column values for deleted records, or if your deletion strategy for your destination is 'soft deletes' (retain the deleted record with a deletion flag), you should set the `REPLICA IDENTITY` to `FULL` for all capture tables. ```SQL SQL theme={null} ALTER TABLE {table} REPLICA IDENTITY FULL; ``` This ensures complete data retention. For capturing partitioned tables, it's essential to enable `publish_via_partition_root` on the publication. By default, changes to partitions are published from the partition itself. The connector expects changes to come from the root table. To ensure compatibility and consistent replication, enable this setting: `ALTER PUBLICATION streamkap_pub SET (publish_via_partition_root = true);`
# SQL Server on Amazon RDS Source: https://docs.streamkap.com/amazon-rds-sql-server SQL Server Change Data Capture on Amazon RDS with Streamkap ## Prerequisites * SQL Server version ≥ 2016 Service Pack 1 Standard or Enterprise Edition * A database user with sufficient privileges to configure the database, including enabling CDC, CT and creating users ## SQL Server Setup ### 1. Grant Database Access * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your database. ### 2. Enable Change Data Capture **Restores** During restores, CDC will be disabled. All of the related metadata is automatically removed from the database. This applies to snapshot restores, point-in-time restores, and SQL Server Native restores from S3. After performing one of these types of restores, you can re-enable CDC and re-specify tables to track. Before you can use CDC with your Amazon RDS DB instance, you must enable it on the database. You must have master user privileges to enable CDC in the Amazon RDS DB instance. After CDC is enabled, any user who is db\_owner of that database can enable or disable CDC on tables in that database. ```SQL SQL theme={null} EXEC msdb.dbo.rds_cdc_enable_db '{database}'; GO ``` Microsoft recommends keeping CDC data files separate from your primary database files. ```SQL SQL theme={null} -- Replace { ... } placeholders as required ALTER DATABASE [{database}] ADD FILEGROUP Streamkap_ChangeTracking; ALTER DATABASE [{database}] ADD FILE ( NAME = Streamkap_ChangeTracking_Data, FILENAME = N'D:\rdsdbdata\DATA\Streamkap_ChangeTracking_Data.ndf' ) TO FILEGROUP Streamkap_ChangeTracking; GO ``` After CDC is enabled on the database, for every table you want Streamkap to capture, enable change tracking on that using the script below: ```SQL SQL theme={null} -- Replace { ... } placeholders as required EXEC sys.sp_cdc_enable_table @source_schema = N'{schema}', @source_name = N'{table}', @role_name = N'streamkap_role', @filegroup_name = N'Streamkap_ChangeTracking', @supports_net_changes = 0 GO ``` ### 3. Create Database User It's recommended to create a separate user and role for Streamkap to access your SQL Server database. Below is an example script that does that. ```SQL SQL theme={null} -- Replace {database}, {schema} and {password} placeholders as required USE {database}; GO CREATE LOGIN streamkap_user WITH PASSWORD = '{password}'; CREATE USER streamkap_user FOR LOGIN streamkap_user; CREATE ROLE streamkap_role; ALTER ROLE streamkap_role ADD MEMBER streamkap_user; GRANT SELECT ON SCHEMA::{schema} TO streamkap_role; GRANT SELECT ON SCHEMA::cdc TO streamkap_role; GO USE master; GO GRANT VIEW SERVER STATE TO streamkap_user; GO ``` ### 4. Enable Snapshots To backfill your data, the Connector needs to be able to perform **snapshots** (See [Snapshots & Backfilling](/snapshots) for more information). To enable this process, a table must be created for the Connector to use. The examples below use `streamkap_signal` as the signal table name, but you can choose any name. During [Streamkap Setup](#3-snapshot-settings), provide the full path to your signal table in `schema.table` format (e.g., `streamkap.streamkap_signal`). ```SQL SQL theme={null} CREATE SCHEMA streamkap; -- Create the table within the schema CREATE TABLE streamkap.streamkap_signal ( id VARCHAR(255) PRIMARY KEY, type VARCHAR(32) NOT NULL, data VARCHAR(2000) NULL ); -- Enable change tracking on the signal table EXEC sys.sp_cdc_enable_table @source_schema = N'streamkap', @source_name = N'streamkap_signal', @role_name = N'streamkap_role', @filegroup_name = N'Streamkap_ChangeTracking', @supports_net_changes = 0 GO -- Grant permission on the signal table to the Streamkap role GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_signal TO streamkap_role; ``` ### 5. Heartbeats Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. For read-write connections (when **Read only** is **No** during Streamkap Setup), the Connector writes to the heartbeat table directly. ```SQL SQL theme={null} -- Create the heartbeat table with id, text, and last_update fields CREATE TABLE streamkap.streamkap_heartbeat ( id INT IDENTITY(1,1) PRIMARY KEY, text NVARCHAR(MAX), last_update DATETIME2 DEFAULT SYSUTCDATETIME() ); -- Enable change tracking on the heartbeat table EXEC sys.sp_cdc_enable_table @source_schema = N'streamkap', @source_name = N'streamkap_heartbeat', @role_name = N'streamkap_role', @filegroup_name = N'Streamkap_ChangeTracking', -- Not applicable for Azure SQL Databases @supports_net_changes = 0 GO -- Grant permission on the heartbeat table to the Streamkap role GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO streamkap_role; -- Insert the first row into the heartbeat table INSERT INTO streamkap.streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **SQL Server**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Endpoint**: Specify the endpoint of the SQL Server database. * **Port**: Default is `1433`. * **Connect via SSH Tunnel**: The Connector will connect to an SSH server in your network which has access to your database. This is necessary if the Connector cannot connect directly to your database. * See [SSH Tunnel](/ssh-tunnel) for setup instructions. * **Username**: Username to access the database. By default, Streamkap scripts use `streamkap_user`. * **Password**: Password to access the database. * **Source Database**: Specify the database to stream data from. * **Heartbeats**: * **Heartbeat Table Schema**: Streamkap will use a table in this schema to manage heartbeats. Usually this is the same as the Signal Table. See [Heartbeats](#5-heartbeats) for setup instructions. ### 3. Snapshot Settings * **Signal Table**: Full path to the signal table including schema and table name (e.g., `streamkap.streamkap_signal`). This table is used for incremental snapshotting. See [Enable Snapshots](#4-enable-snapshots) for setup instructions. ### 4. Advanced Parameters * **Represent binary data as**: Specifies how the data for binary columns e.g. `blob`, `binary`, `varbinary` should be interpreted. Your destination for this data can impact which option you choose. Default is `bytes`. * **Capture Only Captured Databases DDL** (Default `false`) - Used to control whether the connector records schema structures from all databases defined in the server (the default) or only those databases for which you've explicitly configured the connector. Specify `true` to capture schema history only for the specific databases you've configured. This is particularly valuable when databases are large, to reduce the volume of DDL stored in the schema history topic. It also improves startup times when the connector restarts or recovers from failures. See [Schema History Optimization](/schema-history-optimization) for details. * **Capture Only Captured Tables DDL** (Default `false`) - Used to control whether the connector records the schema structure for all tables in the configured databases (the default) or only the tables whose changes the connector captures. Specify `true` to capture schema history only for the specific tables you've configured. This is particularly valuable when tables are large, to reduce the volume of DDL statements stored in the schema history topic. It also improves startup times when the connector restarts or recovers from failures. See [Schema History Optimization](/schema-history-optimization) for details. Click **Next**. ### 5. Schema and Table Capture * **Add Schemas/Tables**: Specify the schema(s) and table(s) for capture * You can bulk upload here. The format is a simple list of schemas and tables, with each entry on a new row. Save as a `.csv` file without a header. **CDC only captures base tables, not Views** Change Data Capture reads SQL Server CDC change tables, which only track changes to physical base tables. Database Views are query-time computations with no physical storage—they don't have CDC change tables. **What you cannot capture:** Views, indexed views (materialized views), table variables, temporary tables, or system tables. **Memory-optimized tables**: While technically possible with special configuration (SQL Server 2016 SP2+, compatibility level 130+), CDC on memory-optimized tables introduces severe performance penalties that negate In-Memory OLTP benefits. Not recommended—use disk-based tables for CDC instead. **Solution:** Specify only the underlying base tables that feed your views. You can recreate the view logic in your destination or transformation layer. Click **Save**. **Have questions or issues?** See the [SQL Server Source FAQ](/sql-server-source-faq) for troubleshooting steps, schema evolution procedures, and best practices. # Apache Iceberg (Generic) Source: https://docs.streamkap.com/apache-iceberg ## Prerequisites * An AWS user with sufficient privileges to create and configure IAM users, roles and S3 buckets. ## Iceberg Data Lake Setup ### 1. Create S3 Bucket For better **isolation**, **security** and **resource management**, it is recommended to create a new S3 bucket, specifically for your Streamkap-to-Iceberg pipelines. * Go to the [Amazon S3 Console](https://s3.console.aws.amazon.com/s3). * Click **Create bucket**. * Set: * **Bucket name:** e.g., `iceberg-bucket`. * **Region:** Match your compute region (e.g., `us-west-2`). * Keep **Object Ownership** set to **ACLs disabled**. * **Uncheck** “Block all public access” only if you’re managing access via bucket policies (optional). * Click **Create Bucket.** Optionally, create a base folder to further organize Iceberg tables created by the connector. * Open the new bucket. * Click **Create folder**. * Enter a name: e.g., `warehouse/streamkap/`. ### 2. Create S3 User It is recommended to create a separate IAM user and role with minimum necessary access. * Go to the [Amazon IAM Console](https://console.aws.amazon.com/iam/). * Click **Users** in the sidebar. * Click **Add users**. * Enter a **User name**: e.g., `streamkap_user` * Set the **Access type**: * ✅ Check “Access key – Programmatic access” * ❌ Uncheck “Console access” (not needed) Click **Next**. * Choose **Attach policies directly** and create a custom policy: * Click **Create policy** (opens in new tab). * Go to the JSON tab and paste this policy, ensuring you replace any `<...>` placeholders as required: ```JSON JSON theme={null} { "Version": "2012-10-17", "Statement": [ { "Sid": "AllowStreamkapAccess", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam:::user/streamkap_user" }, "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::", "arn:aws:s3:::/*" ] } ] } ``` ## Streamkap Setup ### 1. Create the Destination * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Destinations). * Choose **Iceberg**. * **Name:** Enter a name for your connector. ### 2. Connection Settings * **Catalog Type:** The type of Iceberg catalog. * **`REST`or`HIVE`**: * **Catalog Name:** Iceberg catalog name. * **Catalog URI:** The Iceberg catalog URI. * **AWS Access Key:** The AWS Access Key ID used to connect to S3. * **AWS Secret Access Key:** The AWS Secret Access Key used to connect to S3. * **Region:** The AWS region to be used. * **S3 Bucket Path:** Path to the storage location for the Iceberg tables. * **Schema:** An Iceberg table name prefix—equivalent to a database schema (e.g., public, sales, analytics). ### 3. Ingestion Settings * **Ingestion Mode**: Specifies the strategy used to insert events into the Iceberg tables. **Changing ingestion mode** `append` and `upsert` modes use different, incompatible methods for loading data into the Iceberg tables. If - for whatever reason - you want to change modes for an existing Iceberg Connector, please create a **new** Iceberg Destination instead i.e. a separate destination for `insert`, and for `upsert`. * **`upsert`mode**: * **Primary key fields:** Optional. A comma-separated list of field names to use as record identifiers when a primary key's not present. Click **Save**. # Overview Source: https://docs.streamkap.com/api Programmatic access to Streamkap via REST API, CLI, MCP, and Terraform. Streamkap provides multiple ways to manage your infrastructure programmatically: * **[API Reference](/api-reference/authentication/access-token)** — Interactive REST API documentation * **[CLI](/cli)** — Command-line tool for managing pipelines, sources, destinations, and more * **[MCP Server](/mcp-server)** — Connect AI agents to Streamkap via the Model Context Protocol * **[Terraform Provider](/streamkap-provider-for-terraform)** — Infrastructure as code for Streamkap resources Looking to use AI agents with Streamkap? See [Agents](/agents) for a guide on all integration paths. All programmatic access requires credentials — either a standalone [API Token](/api-tokens) or a [Project Key](/project-keys) (which can bundle API access, Kafka access, and MCP tool scoping into a single credential file). # API Quickstart Source: https://docs.streamkap.com/api-quickstart Get started with the Streamkap REST API: authentication, common workflows, and best practices. The Streamkap REST API lets you programmatically manage sources, destinations, pipelines, and transforms. This guide covers authentication and common workflows to get you up and running quickly. For full endpoint details, request/response schemas, and an interactive playground, see the [API Reference](/api-reference/authentication/access-token) tab. ## Authentication The Streamkap API uses bearer tokens for authentication. The flow is: 1. **Create API credentials** (Client ID and Secret) in the Streamkap UI -- see [API Tokens](/api-tokens) for instructions. 2. **Exchange credentials for an access token** by calling the `/auth/access-token` endpoint. 3. **Use the access token** as a Bearer token in the `Authorization` header for all subsequent requests. ### Get an Access Token ```bash cURL theme={null} curl -X POST "https://api.streamkap.com/auth/access-token" \ -H "Content-Type: application/json" \ -d '{ "client_id": "YOUR_CLIENT_ID", "secret": "YOUR_CLIENT_SECRET" }' ``` ```python Python theme={null} import requests response = requests.post( "https://api.streamkap.com/auth/access-token", json={ "client_id": "YOUR_CLIENT_ID", "secret": "YOUR_CLIENT_SECRET" } ) data = response.json() access_token = data["accessToken"] refresh_token = data["refreshToken"] print(f"Access token expires: {data['expires']}") ``` ```javascript JavaScript theme={null} const response = await fetch("https://api.streamkap.com/auth/access-token", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ client_id: "YOUR_CLIENT_ID", secret: "YOUR_CLIENT_SECRET", }), }); const data = await response.json(); const accessToken = data.accessToken; const refreshToken = data.refreshToken; ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { body, _ := json.Marshal(map[string]string{ "client_id": "YOUR_CLIENT_ID", "secret": "YOUR_CLIENT_SECRET", }) resp, err := http.Post( "https://api.streamkap.com/auth/access-token", "application/json", bytes.NewBuffer(body), ) if err != nil { panic(err) } defer resp.Body.Close() var data map[string]interface{} json.NewDecoder(resp.Body).Decode(&data) fmt.Println("Access token:", data["accessToken"]) } ``` The response includes: | Field | Description | | -------------- | ---------------------------------------- | | `accessToken` | Bearer token to use in API requests | | `refreshToken` | Token to obtain a new access token | | `expires` | Expiration timestamp of the access token | | `expiresIn` | Seconds until the access token expires | ### Make Authenticated Requests Include the access token in the `Authorization` header for all API calls: ```bash cURL theme={null} curl -X GET "https://api.streamkap.com/sources" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" ``` ```python Python theme={null} import requests headers = {"Authorization": "Bearer YOUR_ACCESS_TOKEN"} response = requests.get("https://api.streamkap.com/sources", headers=headers) sources = response.json() ``` ```javascript JavaScript theme={null} const response = await fetch("https://api.streamkap.com/sources", { headers: { Authorization: "Bearer YOUR_ACCESS_TOKEN" }, }); const sources = await response.json(); ``` ```go Go theme={null} req, _ := http.NewRequest("GET", "https://api.streamkap.com/sources", nil) req.Header.Set("Authorization", "Bearer YOUR_ACCESS_TOKEN") resp, err := http.DefaultClient.Do(req) ``` ### Refresh an Access Token When your access token expires, use the refresh token to obtain a new one without re-authenticating with your client credentials: ```bash theme={null} curl -X POST "https://api.streamkap.com/auth/access-token/refresh" \ -H "Content-Type: application/json" \ -d '{ "refresh_token": "YOUR_REFRESH_TOKEN" }' ``` *** ## Common Workflows ### 1. Create a Complete Pipeline Build an end-to-end CDC pipeline by creating a source, destination, and then linking them in a pipeline. Create a source by specifying the connector type and its configuration. The `connector` field identifies the source type (e.g., `postgresql`, `mysql`, `mongodb`), and `config` contains connector-specific settings. ```bash theme={null} curl -X POST "https://api.streamkap.com/sources" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "my-postgresql-source", "connector": "postgresql", "config": { "database.hostname": "db.example.com", "database.port": "5432", "database.user": "cdc_user", "database.password": "YOUR_DB_PASSWORD", "database.dbname": "my_database", "table.include.list": "public.orders,public.customers" } }' ``` Save the returned `id` value -- you will need it when creating the pipeline. Create a destination where your CDC data will be delivered. As with sources, the `connector` field identifies the destination type and `config` holds its settings. ```bash theme={null} curl -X POST "https://api.streamkap.com/destinations" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "my-snowflake-destination", "connector": "snowflake", "config": { "snowflake.url.name": "your-account.snowflakecomputing.com", "snowflake.user.name": "STREAMKAP_USER", "snowflake.private.key": "YOUR_PRIVATE_KEY", "snowflake.database.name": "MY_DATABASE", "snowflake.schema.name": "PUBLIC" } }' ``` Save the returned `id` value for the next step. Link the source and destination together by referencing their IDs. ```bash theme={null} curl -X POST "https://api.streamkap.com/pipelines" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "my-cdc-pipeline", "source": { "id": "SOURCE_ID_HERE", "connector": "postgresql", "name": "my-postgresql-source" }, "destination": { "id": "DESTINATION_ID_HERE", "connector": "snowflake", "name": "my-snowflake-destination" }, "snapshot_new_tables": true, "tags": [] }' ``` The pipeline starts automatically after creation. The source will begin an initial snapshot of the configured tables. Connector configurations vary by type. Refer to the source and destination documentation pages for the full list of configuration options for each connector. ### 2. Monitor Pipeline Health Check the status and performance of your running pipelines. Retrieve the current state of a pipeline: ```bash theme={null} curl -X GET "https://api.streamkap.com/pipelines/PIPELINE_ID" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" ``` Get latency, throughput, and lag metrics for a pipeline: ```bash theme={null} curl -X GET "https://api.streamkap.com/pipelines/PIPELINE_ID/metrics" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" ``` Key metrics to watch: * **latency** -- End-to-end delay from source change to destination delivery * **recordsLag** -- Number of records waiting to be processed List consumer groups and inspect their lag to identify bottlenecks: ```bash theme={null} # List all consumer groups curl -X GET "https://api.streamkap.com/consumer-groups" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" # Get details for a specific consumer group curl -X GET "https://api.streamkap.com/consumer-groups/GROUP_ID" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" ``` Monitor source connector health and snapshot progress: ```bash theme={null} curl -X GET "https://api.streamkap.com/sources/SOURCE_ID/metrics" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" ``` The response includes snapshot-related fields such as `snapshotStatus`, `snapshotState`, `SnapshotCompleted`, and `SnapshotRunning`. ### 3. Trigger a Snapshot Trigger an incremental snapshot to re-read data from specific tables without resetting the entire source connector. Specify the source ID and optionally the topic names (tables) to snapshot. If `topic_names` is omitted, all tables configured on the source will be snapshotted. ```bash theme={null} curl -X POST "https://api.streamkap.com/sources/execute_incremental_snapshot" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "source_id": "SOURCE_ID_HERE", "topic_names": ["my_database.public.orders"] }' ``` You can also include optional `additional_conditions` to filter which rows are snapshotted: ```bash theme={null} curl -X POST "https://api.streamkap.com/sources/execute_incremental_snapshot" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "source_id": "SOURCE_ID_HERE", "topic_names": ["my_database.public.orders"], "additional_conditions": [ { "data_collection": "public.orders", "filter": "created_at > '\''2025-01-01'\''" } ] }' ``` Check the source metrics to track the snapshot: ```bash theme={null} curl -X GET "https://api.streamkap.com/sources/SOURCE_ID/metrics" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" ``` Look for `SnapshotRunning` (number of tables currently being snapshotted) and `SnapshotCompleted` (number of tables that have finished). To cancel a running incremental snapshot: ```bash theme={null} curl -X POST "https://api.streamkap.com/sources/stop_incremental_snapshot" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "source_id": "SOURCE_ID_HERE" }' ``` ### 4. Add Tables to an Existing Source Add new tables to a running source without modifying the original table list, using the `table.include.list.user.defined` configuration parameter. ```bash theme={null} curl -X GET "https://api.streamkap.com/sources/SOURCE_ID" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" ``` Note the current `name`, `connector`, and `config` values from the response. Use `PUT /sources/{source_id}` to update the source configuration. Add the new tables via the `table.include.list.user.defined` parameter while keeping all existing configuration values. ```bash theme={null} curl -X PUT "https://api.streamkap.com/sources/SOURCE_ID" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "my-postgresql-source", "connector": "postgresql", "config": { "database.hostname": "db.example.com", "database.port": "5432", "database.user": "cdc_user", "database.password": "YOUR_DB_PASSWORD", "database.dbname": "my_database", "table.include.list": "public.orders,public.customers", "table.include.list.user.defined": "public.products,public.inventory" } }' ``` For a Terraform-based approach to adding tables, see [Adding Tables to an Existing Source](/terraform-resources#adding-tables-to-an-existing-source). *** ## API Response Patterns ### Standard Responses All API responses return JSON. Successful responses typically include the resource object directly: ```json theme={null} { "id": "abc123", "name": "my-postgresql-source", "connector": "postgresql", "config": { ... }, "created_at": "2025-06-15T10:30:00Z" } ``` ### Paginated Responses List endpoints support pagination via `page` and `page_size` query parameters: ```bash theme={null} # Get page 2 with 20 items per page curl -X GET "https://api.streamkap.com/sources?page=2&page_size=20" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" ``` | Parameter | Default | Description | | ----------- | ------- | --------------------------------------- | | `page` | `1` | Page number (1-indexed) | | `page_size` | `10` | Number of items per page | | `sort` | -- | Sort field (e.g., `name`, `created_at`) | | `sort_dir` | `asc` | Sort direction: `asc` or `desc` | ### Error Responses Validation errors return a `422` status code with details about the issue: ```json theme={null} { "detail": [ { "loc": ["body", "name"], "msg": "field required", "type": "value_error.missing" } ] } ``` Other common HTTP status codes: | Status | Meaning | | ------ | -------------------------------------------- | | `200` | Success | | `401` | Unauthorized -- invalid or expired token | | `404` | Resource not found | | `422` | Validation error -- check the `detail` field | Detailed request/response schemas for every endpoint are available in the [API Reference](/api-reference/authentication/access-token). *** ## Rate Limits and Best Practices API rate limits are managed by Streamkap and are designed to accommodate normal usage patterns. If you encounter `429 Too Many Requests` responses or believe your use case requires higher limits, contact [Streamkap support](mailto:support@streamkap.com) with details about your integration and expected request volume. **API versioning** The Streamkap API is currently unversioned. All endpoints use a single base URL (`https://api.streamkap.com`). If breaking changes are introduced in the future, they will be communicated in advance through release notes and direct customer notification, with a deprecation period to allow migration. * **Use pagination** for list endpoints. Avoid fetching all resources in a single request by using `page` and `page_size` parameters. * **Cache responses** where appropriate. Source and destination configurations change infrequently, so you can safely cache them for short periods. * **Handle retries with exponential backoff.** If a request fails with a `5xx` status code, wait before retrying. Start with a 1-second delay and double it on each retry, up to a maximum of 30 seconds. * **Refresh tokens proactively.** Use the refresh token endpoint before your access token expires rather than waiting for a `401` error. * **Minimize unnecessary polling.** When monitoring pipeline health, use a reasonable interval (e.g., every 30--60 seconds) rather than polling continuously. *** ## SDKs and Tools ### Terraform Provider The [Streamkap Terraform Provider](/streamkap-provider-for-terraform) enables infrastructure-as-code management of sources, destinations, pipelines, and transforms. See the full [Terraform Configuration guide](/terraform-configuration) to get started. ### OpenAPI Specification The Streamkap API is documented with an OpenAPI 3.1 specification. You can use the spec to generate client libraries for any language using tools like [OpenAPI Generator](https://openapi-generator.tech/) or [Speakeasy](https://www.speakeasy.com/). The spec is accessible in the [API Reference](/api-reference/authentication/access-token) tab and powers the interactive endpoint playground. *** ## Next Steps * [API Tokens](/api-tokens) -- Create and manage API credentials * [API Reference](/api-reference/authentication/access-token) -- Full interactive endpoint documentation * [Terraform Provider](/streamkap-provider-for-terraform) -- Manage infrastructure as code * [Terraform Resources](/terraform-resources) -- Source, destination, and pipeline resource examples * [Snapshots](/snapshots) -- Learn how initial and incremental snapshots work # Get Observability Agents Source: https://docs.streamkap.com/api-reference/agents-observability/get-observability-agents /openapi/openapi.json get /agentic/observability/agents Agent activity rollup over the selected window (default last 24h). One row per Streamkap agent (joined with MongoDB ``agents`` for the friendly name) plus one per ``(project_key_id, client_id)`` external pseudo-agent. Streamkap counts combine MCP-server calls (``streamkap_mcp_log``) with agent-runtime trace activity (``agent_traces``), so an agent that only processes records still surfaces a real count. The FE passes its selected range so the sidebar matches the active view. Sorted newest-active-first. # Get Observability External Verify Source: https://docs.streamkap.com/api-reference/agents-observability/get-observability-external-verify /openapi/openapi.json get /agentic/observability/external/verify First call newer than ``since``, or ``{pending: true}`` if none. ``since`` is REQUIRED - without it the query would match any historical call from this PK+client and immediately mark verify as successful for a returning user reconnecting. The FE sets ``since`` to the timestamp at which the user clicked into the verify step. # Get Observability Histogram Source: https://docs.streamkap.com/api-reference/agents-observability/get-observability-histogram /openapi/openapi.json get /agentic/observability/histogram Status-stacked call-rate histogram. Same filter shape as ``/sessions``. Default range is last 24h. # Get Observability Iteration Detail Source: https://docs.streamkap.com/api-reference/agents-observability/get-observability-iteration-detail /openapi/openapi.json get /agentic/observability/iterations/{iteration_id} Ordered operations for one trace iteration (reads ``agent_traces``). Ordered by ``timestamp`` then a chat → tool_call → output tiebreak (``sequence_number`` is not yet emitted). Tenant scoping makes a cross-tenant ``iteration_id`` return an empty list — same as "not found", no existence leak. # Get Observability Iterations Source: https://docs.streamkap.com/api-reference/agents-observability/get-observability-iterations /openapi/openapi.json get /agentic/observability/iterations Per-iteration trace rollup, newest-first (reads ``agent_traces``). One row per ``iteration_id`` — all operations of one input-record's processing chain. ``agent_traces`` is Streamkap-agent only (no external pseudo-agents), so ``agent_id`` passes straight through with no entity resolution. Tenant scoping at the WHERE layer; a cross-tenant agent_id returns an empty page, not a leak. # Get Observability Iterations Histogram Source: https://docs.streamkap.com/api-reference/agents-observability/get-observability-iterations-histogram /openapi/openapi.json get /agentic/observability/iterations/histogram Operation-volume histogram over the trace stream. Default range 24h. Status-stacked bucket shape matches the MCP histogram so the FE reuses one renderer; ``agent_traces`` has no ``denied`` status (always 0). # Get Observability Logs Source: https://docs.streamkap.com/api-reference/agents-observability/get-observability-logs /openapi/openapi.json get /agentic/observability/logs Per-job log lines for one agent or KB. Replaces the namespace-scoped kubectl tail with a per-``job_name`` ClickHouse query. ``agent_id`` resolves to the prefix ``agent__`` or ``kb__`` via the doc's ``job_type``; the prefix LIKE catches every redeploy version of the entity so log history survives re-deploys. Returns 404 when the id doesn't belong to this tenant + service (or the doc's ``job_type`` isn't agent / KB). Returns 503 with a structured ``kind`` when the ``flink_logs`` table is missing or a column has drifted. # Get Observability Logs Exceptions Source: https://docs.streamkap.com/api-reference/agents-observability/get-observability-logs-exceptions /openapi/openapi.json get /agentic/observability/logs/exceptions Recent Flink-side exceptions for the entity's current job. Pulls from the Flink REST ``/jobs/{jid}/exceptions`` endpoint, not ClickHouse — exceptions are JM state, not a log row stream. Returns ``exceptions=[]`` (NOT 404) when the entity has no live Flink job — a draft agent / KB and a recently-cancelled one are both valid scopes with nothing to surface. # Get Observability Logs Histogram Source: https://docs.streamkap.com/api-reference/agents-observability/get-observability-logs-histogram /openapi/openapi.json get /agentic/observability/logs/histogram Level-stacked log histogram for one agent or KB. Same scope rules as :func:`get_observability_logs` (404 on cross-scope id, 503 when the table isn't deployed). The histogram surfaces all four levels regardless of the line viewer's current level filter — the user is meant to see the full level distribution so they can pick a filter floor. # Get Observability Session Spans Source: https://docs.streamkap.com/api-reference/agents-observability/get-observability-session-spans /openapi/openapi.json get /agentic/observability/sessions/{session_id} All spans within one session, ordered by ``(timestamp, call_sequence)``. Tenant + service scoping happens via the WHERE clause - there's no separate pre-check because a session_id from another tenant or sibling service simply returns 0 rows (empty list), which the FE treats the same as "session not found / expired". No leak. # Get Observability Session Transcript Source: https://docs.streamkap.com/api-reference/agents-observability/get-observability-session-transcript /openapi/openapi.json get /agentic/observability/sessions/{session_id}/transcript Full transcript timeline for one session. Reads ``streamkap_agent_transcript`` (the Flink-runtime transcript stream). A row is one chat call, tool call, or final output emission; they share an ``iteration_id`` when they belong to the same input record's processing chain. Rows are returned oldest-first. Tenant + service scoping enforced at the query layer — a session id that belongs to another tenant or sibling service simply returns an empty ``rows`` list, identical to "session not found / expired". No existence leak across tenants. # Get Observability Sessions Source: https://docs.streamkap.com/api-reference/agents-observability/get-observability-sessions /openapi/openapi.json get /agentic/observability/sessions Cursor-paginated sessions list. Filter precedence: 1. ``agent_id`` (Streamkap or external:pk:cid synthetic form) 2. ``project_key_id`` + ``client_id`` (external pseudo-agent) 3. No entity filter - all-tenant-activity pseudo-row. The FE never sends both flavours - but if it does, ``agent_id`` wins. ``kind`` and ``entity_kind`` are reserved-slot dispatch params: today only ``kind=tool_call`` against ``entity_kind=streamkap|external`` returns data. Other valid combinations return an empty page until their backing tables / emitters land. Unknown values raise 400. # Get Observability Tools Source: https://docs.streamkap.com/api-reference/agents-observability/get-observability-tools /openapi/openapi.json get /agentic/observability/tools Distinct ``tool_name`` values within the requested filter scope. Powers the FE TraceHeader tool-filter dropdown. Without this endpoint, the FE has to derive available tools from spans the user has manually expanded — most users never expanded enough sessions to see the filter populate. Returns a flat sorted list capped server-side. Same filter precedence as ``/sessions`` (``agent_id`` wins; else ``(project_key_id, client_id)``; else fleet-wide). Default range is the last 24h, matching the rollup window. # Cancel Agent Source: https://docs.streamkap.com/api-reference/agents/cancel-agent /openapi/openapi.json post /agents/{job_id}/cancel Gracefully cancel a running agent WITH savepoint. Creates a savepoint before stopping - use this when you want to resume later. The savepoint path is stored and used automatically on redeploy. # Create Agent From Config Source: https://docs.streamkap.com/api-reference/agents/create-agent-from-config /openapi/openapi.json post /agents/config Create a config-based agent, optionally deploying it. Set deploy=false to save as draft without deploying. # Create Agent Http Connection Source: https://docs.streamkap.com/api-reference/agents/create-agent-http-connection /openapi/openapi.json post /agents/connections/http Create one HTTP connection. ``id`` is server-generated. Header values are KMS-encrypted at rest and full-mask on response. ``Authorization`` is allowed in headers (unlike External MCP, which owns its own auth shape via ``authMode``). # Create Agent Llm Connection Source: https://docs.streamkap.com/api-reference/agents/create-agent-llm-connection /openapi/openapi.json post /agents/connections/llm Create one LLM connection. ``id`` is server-generated. Validates against the same ``AgentLlmConnection`` model the bulk PUT uses; capability-matrix / length / type errors surface as the standard FastAPI 422 envelope. Duplicate ``name`` within the tenant returns 409 with the same shape the bulk PUT uses for surfaceable conflicts. # Create Agent Mcp Connection Source: https://docs.streamkap.com/api-reference/agents/create-agent-mcp-connection /openapi/openapi.json post /agents/connections/mcp Create one external MCP connection. Streamkap-source rows are rejected (422): they're managed via the Project Key agentic toggle, not Settings. Discriminator (``authMode``) + URL SSRF guard run inline before the row is persisted. # Create Agent Vector Store Connection Source: https://docs.streamkap.com/api-reference/agents/create-agent-vector-store-connection /openapi/openapi.json post /agents/connections/vectorStore Create one vector store connection. ``id`` is server-generated. # Delete Agent Source: https://docs.streamkap.com/api-reference/agents/delete-agent /openapi/openapi.json delete /agents/{job_id} Soft-delete an agent (cancels it first if running). # Delete Agent Http Connection Source: https://docs.streamkap.com/api-reference/agents/delete-agent-http-connection /openapi/openapi.json delete /agents/connections/http/{connection_id} Hard-delete one HTTP connection by id. # Delete Agent Llm Connection Source: https://docs.streamkap.com/api-reference/agents/delete-agent-llm-connection /openapi/openapi.json delete /agents/connections/llm/{connection_id} Hard-delete one LLM connection by id. Per the FE handoff (Option B): no referential check against agents using this connection. Agents that picked it surface a banner on next edit (same idiom as Pinecone destination deletion). # Delete Agent Mcp Connection Source: https://docs.streamkap.com/api-reference/agents/delete-agent-mcp-connection /openapi/openapi.json delete /agents/connections/mcp/{name} Hard-delete one external MCP connection by name. # Delete Agent Vector Store Connection Source: https://docs.streamkap.com/api-reference/agents/delete-agent-vector-store-connection /openapi/openapi.json delete /agents/connections/vectorStore/{connection_id} Hard-delete one vector store connection by id. # Deploy Jar Job Source: https://docs.streamkap.com/api-reference/agents/deploy-jar-job /openapi/openapi.json post /agents/jar Deploy a JAR-based agent. Upload a JAR file as multipart and specify main_class + optional args. Form fields: name, main_class, parallelism (default 1), job_args (JSON string). # Deploy Pyflink Job Source: https://docs.streamkap.com/api-reference/agents/deploy-pyflink-job /openapi/openapi.json post /agents/pyflink Deploy a PyFlink agent. Either upload a Python file as multipart OR provide inline python_code as a form field. Form fields: name, parallelism (default 1), job_args (JSON string), python_code (alternative to file). # Discover Mcp Tools Source: https://docs.streamkap.com/api-reference/agents/discover-mcp-tools /openapi/openapi.json post /agents/mcp/tools Discover available tools from an MCP server. Three callable shapes - see ``DiscoverMcpToolsRequest`` docs for full details: - **Saved-id:** ``{savedConnectionId}`` -> BE reads the stored row and composes headers server-side; the browser never sees the token. ``savedConnectionName`` is accepted as a deprecated alias for one release cycle. - **Inline with masked secret:** ``{serverUrl, headers: {...: "********"}}`` -> BE matches the serverUrl against saved settings and substitutes the stored secret for every masked header value. - **Inline plaintext:** ``{serverUrl, headers}`` - used on first-save "Test" click before the row exists. URL is validated (SSRF guard) on every path. Per-tenant rate limit: 30 req/min -> 429 with ``Retry-After``. A 300s in-process cache (M10) keyed by ``(serverUrl, headers-hash)`` short-circuits repeat calls; ``X-MCP-Cache: hit|miss`` is emitted on every response. Cache hits still consume rate-limit budget - the cache is not a bypass. # Get Agent Source: https://docs.streamkap.com/api-reference/agents/get-agent /openapi/openapi.json get /agents/{job_id} Get an agent with live status from Flink REST. # Get Agent Cluster Source: https://docs.streamkap.com/api-reference/agents/get-agent-cluster /openapi/openapi.json get /agents/cluster Return cluster-level Flink info for the agent runtime - slots + version. Powers the agentic dashboard header (total/available task slots, Flink version). Sourced from Flink REST ``/overview``; kebab-case keys are translated to the camelCase response contract. Agents and transforms share the same tenant Flink cluster, so this probe accepts either ``read:agents`` or ``read:transforms`` (OR semantics) and backs the cluster-availability note on both deploy surfaces. When the tenant's cluster is not provisioned the upstream REST call 404s; that is reported as ``available=False`` with zeroed fields (HTTP 200) so the dashboard can render a "cluster not available" note instead of erroring. # Get Agent Code Source: https://docs.streamkap.com/api-reference/agents/get-agent-code /openapi/openapi.json get /agents/{job_id}/code Get the stored code for a PyFlink agent. # Get Agent Config Source: https://docs.streamkap.com/api-reference/agents/get-agent-config /openapi/openapi.json get /agents/{job_id}/config Get the stored config for a config-based agent. # Get Agent Config History Entry Source: https://docs.streamkap.com/api-reference/agents/get-agent-config-history-entry /openapi/openapi.json get /agents/{job_id}/config/history/{version} Return the masked full config for a specific history version. Stored snapshots are re-validated through ``CreateAgentConfigRequest`` on the response path. If the validator has tightened since the snapshot was written, surface the error as 422 so the FE can explain the drift - same shape the rollback endpoint uses. Unhandled, the error would otherwise land as a 500. # Get Agent Connections Source: https://docs.streamkap.com/api-reference/agents/get-agent-connections /openapi/openapi.json get /agents/connections Get the tenant's saved agent connections. Returns the bulk shape (LLM / MCP / HTTP connection arrays). Secrets are masked (``sk-a****xyzw`` partial mask for apiKey/clientSecret; ``********`` for bearerToken/headerValue and every value inside a ``headers`` dict), so ``read:agents`` is the correct gate - a team member with read-only access can see which connections exist to reason about agent configuration without being able to mutate them. The unmasked plaintext is only reachable via the internal ``_load_agent_connections_decrypted`` helper used at deploy time - never via an HTTP endpoint. # Get Agent Logs Source: https://docs.streamkap.com/api-reference/agents/get-agent-logs /openapi/openapi.json get /agents/{job_id}/logs Tailed ``[AGENT]``-prefixed Flink TM logs + recent job exceptions. The ``write:agents`` gate is intentional: agent log lines may contain customer payloads that flowed through prompts and tool args. Tightening the audit surface here mirrors how the Streamkap MCP project key blob is gated. Per-line scoping is namespace-wide for now - when multiple agents share a TaskManager, lines from other agents may appear. Documented limitation until the Java runtime emits per-line agent IDs. ``since`` is an optional ISO 8601 cutoff for incremental polling on the unified observability surface. The underlying tail window is Nx4 the requested ``tail`` count; if ``since`` is far enough back that the window doesn't reach it, the response is still bounded by ``tail`` (the caller can widen ``tail`` to compensate). # Get Agent Models Source: https://docs.streamkap.com/api-reference/agents/get-agent-models /openapi/openapi.json get /agents/models Return the curated LLM model list per provider. Shape: ``{provider: [{"value": model_id, "label": display_name}, ...]}``. First-paint fallback for the Deploy form dropdown, before the user enters an API key. Once a key is entered, prefer ``POST /agents/models/live`` - that returns the provider's live list including org-enabled models and fine-tunes the curated list can't know about. # Get Agent Prompt Templates Source: https://docs.streamkap.com/api-reference/agents/get-agent-prompt-templates /openapi/openapi.json get /agents/prompt-templates Return the system-prompt templates per agent type. Lets the FE preview the exact string Flink will prepend at runtime (no drift risk from duplicating the strings in frontend constants). Keys mirror ``AgentTypeEnum``: workflow / react. # Get Agent Sample Record Source: https://docs.streamkap.com/api-reference/agents/get-agent-sample-record /openapi/openapi.json post /agents/sample-record Pull the latest record from the topic that matches ``topicPattern``. Used by the wizard's persistent test pane to pre-fill the Monaco editor with a concrete sample, so the user doesn't have to hand-craft one. Returns 200 in every "no record" case (regex matches no topic, every matched topic is empty, deserialization fails) so the FE can render inline state without retry logic. ``read:agents`` is the right gate - this is observation, not config mutation, and the response is the same record any team member with Kafka topic-read access could pull manually. Per-tenant rate limit: 30 req/min/bucket -> 429 with ``Retry-After``. # Get Agent Status Source: https://docs.streamkap.com/api-reference/agents/get-agent-status /openapi/openapi.json get /agents/{job_id}/status Get lightweight status for an agent (DB + live Flink state only). # List Agent Config History Source: https://docs.streamkap.com/api-reference/agents/list-agent-config-history /openapi/openapi.json get /agents/{job_id}/config/history List a config-based agent's config-change history, newest first. Response is bounded by the server-side retention window (50 rows per agent by default) and carries a per-row ``summary.changedFields`` so the FE sidebar stays cheap - full before/after diffs come from two ``GET /history/{version}`` detail reads. # List Agent Models Live Source: https://docs.streamkap.com/api-reference/agents/list-agent-models-live /openapi/openapi.json post /agents/models/live Return the provider's live model list using the caller's API key. The key is forwarded directly to the provider's ``/v1/models`` endpoint (or Ollama's ``/api/tags``) and not persisted. Response shape matches ``GET /agents/models`` per provider so the FE can swap the static fallback for the live list without changing the dropdown contract. 422 if ``apiKey`` is missing for a non-Ollama provider. 400 with the provider's own error message on invalid key / connection failure. Per-tenant rate limit: 30 req/min/bucket -> 429 with ``Retry-After``. # List Agents Source: https://docs.streamkap.com/api-reference/agents/list-agents /openapi/openapi.json get /agents List all agents for the current tenant/service. # Patch Agent Http Connection Source: https://docs.streamkap.com/api-reference/agents/patch-agent-http-connection /openapi/openapi.json patch /agents/connections/http/{connection_id} Partial-update one HTTP connection by id. Per-header masked sentinel preserves the existing encrypted Binary; a new header value encrypts + replaces. Removing a key from the headers dict in the patch removes it from storage (the merge is field-level, not a deep merge of the headers object). # Patch Agent Llm Connection Source: https://docs.streamkap.com/api-reference/agents/patch-agent-llm-connection /openapi/openapi.json patch /agents/connections/llm/{connection_id} Partial-update one LLM connection by id. ``apiKey: "********"`` (or partial ``sk-a****xyz``) -> no-op on that field. ``apiKey: null`` -> clear. Other fields merge over the existing row, then the merged row re-validates against ``AgentLlmConnection``. Returns the masked persisted shape. # Patch Agent Mcp Connection Source: https://docs.streamkap.com/api-reference/agents/patch-agent-mcp-connection /openapi/openapi.json patch /agents/connections/mcp/{name} Partial-update one external MCP connection by name. ``name`` is URL-encoded by the FE so quirks like spaces survive. Switching ``authMode`` mid-patch swaps the discriminated-union variant; the merged row re-validates fully so the FE can't end up with an "unknown" auth mode mid-flight. # Patch Agent Vector Store Connection Source: https://docs.streamkap.com/api-reference/agents/patch-agent-vector-store-connection /openapi/openapi.json patch /agents/connections/vectorStore/{connection_id} Partial-update one vector store connection by id. # Redeploy Agent Source: https://docs.streamkap.com/api-reference/agents/redeploy-agent /openapi/openapi.json post /agents/{job_id}/redeploy Redeploy a cancelled/failed agent. Re-uploads stored code to the Flink pod and starts the job. Set with_savepoint=true (default) to resume from last savepoint, or false to start fresh. Body is optional - omit it to accept defaults. # Rollback Agent Config Source: https://docs.streamkap.com/api-reference/agents/rollback-agent-config /openapi/openapi.json post /agents/{job_id}/config/rollback Roll a config-based agent back to a historical version. Writes a new version N+1 whose config equals the target's stored snapshot, records ``change_kind='rollback'`` + ``rollback_source_version`` on the new history row, and redeploys the agent. Never a pointer swap, never destructive. If current validators reject the historical config (validator tightening between then and now), returns 422 with Pydantic's error text so the FE can highlight the offending fields. # Stop Agent Source: https://docs.streamkap.com/api-reference/agents/stop-agent /openapi/openapi.json post /agents/{job_id}/stop Immediately stop a running agent WITHOUT savepoint. No savepoint is created - next deploy starts fresh. Use this when you don't need to preserve state (e.g., after a code change). # Suggest Agent System Prompt Source: https://docs.streamkap.com/api-reference/agents/suggest-agent-system-prompt /openapi/openapi.json post /agents/system-prompt-suggestion Generate a starter ``customInstructions`` block for the wizard. Same two credential shapes as ``/agents/validate-llm``: - **Inline** - raw ``apiKey`` (+ optional ``baseUrl``) supplied directly. - **Saved connection** - ``savedConnectionId`` references a stored ``llmConnection``; BE loads decrypted ``apiKey`` / ``baseUrl`` / ``provider`` server-side so the browser never holds the plaintext. Composes a meta-system-prompt + a structured user message describing the agent (type, name, description, topics, fields, tools, output schema, optional existing draft) and calls the user's own LLM. The runtime ``build_system_prompt`` already emits the per-agentType base template + schema field listing + tool signatures at deploy time, so the LLM is constrained to produce only the user-facing instructions slot - the meta-prompt enforces that explicitly. Nothing is persisted. Per-tenant rate limit: dedicated ``BUCKET_PROMPT_SUGGEST`` bucket (30/min/tenant) separate from ``validate-llm``, so heavy "Generate" usage (humans iterating on prompt drafts) doesn't eat into the credential-check budget. Provider failures (timeouts, 4xx, schema errors) surface as 400 with the provider's error message so the FE's existing error-extraction works unchanged. # Test Run Agent Source: https://docs.streamkap.com/api-reference/agents/test-run-agent /openapi/openapi.json post /agents/test-run Run an agent against a single sample record without deploying to Flink. Synchronous, hard-capped at 15s wall-clock. Calls the LLM provider directly + dispatches MCP and HTTP tools live; transform tools and memory are intentionally not exercised (the Flink runtime owns them). Per-tenant rate limit: shares the validate-llm bucket (30 req/min/tenant) -> 429. Provider failures (timeouts, schema errors, unsupported provider) land as a structured ``error`` object on the envelope (HTTP still 200) so the FE can render the partial timeline alongside the error context. Only the input-token cap raises (422) - at that point we can't even start the run. # Update Agent Code Source: https://docs.streamkap.com/api-reference/agents/update-agent-code /openapi/openapi.json put /agents/{job_id}/code Update the stored code for a PyFlink agent and optionally redeploy. # Update Agent Config Source: https://docs.streamkap.com/api-reference/agents/update-agent-config /openapi/openapi.json put /agents/{job_id}/config Update a config-based agent's config and redeploy. Merges the provided fields into the existing config, then triggers savepoint -> cancel -> redeploy with the new config. The caller's email is recorded on the paired history row (M3) - for admin-app mode this is the admin's email, not the tenant user's. # Update Agent Connections Source: https://docs.streamkap.com/api-reference/agents/update-agent-connections /openapi/openapi.json put /agents/connections Bulk save the tenant's agent connections. Secrets are encrypted at rest. Service-layer ``ValueError`` (external MCP URL validation, intra-document duplicate id guard) surfaces as 422 with the message so the FE can point at the offending row. Unhandled, these would land as 500. # Validate Llm Connection Source: https://docs.streamkap.com/api-reference/agents/validate-llm-connection /openapi/openapi.json post /agents/validate-llm Validate an LLM provider API key and model availability. Two callable shapes: - **Inline** - raw ``apiKey`` + optional ``baseUrl`` supplied directly. Used when the user is pasting a fresh key in the form. - **Saved connection** - ``savedConnectionId`` references a stored ``llmConnection``; BE loads the decrypted ``apiKey`` + ``baseUrl`` + ``provider`` server-side so the browser never holds the plaintext. Calls the provider's free list-models endpoint to check key validity and model existence. The "apiKey required unless ollama or saved" rule is enforced by the request model's ``@model_validator``. Per-tenant rate limit: 30 req/min/bucket -> 429 with ``Retry-After``. # Delete Subscriber Credentials Source: https://docs.streamkap.com/api-reference/alerts/delete-subscriber-credentials /openapi/openapi.json delete /alerts/subscribers/{subscriberId}/credentials # Get Subscriber Preferences Source: https://docs.streamkap.com/api-reference/alerts/get-subscriber-preferences /openapi/openapi.json get /alerts/subscribers/{subscriberId}/preferences # Get Subscribers Source: https://docs.streamkap.com/api-reference/alerts/get-subscribers /openapi/openapi.json get /alerts/subscribers/{subscriberId} # Summarise Alert Source: https://docs.streamkap.com/api-reference/alerts/summarise-alert /openapi/openapi.json post /alerts/summary Generate AI-powered summary for an alert. Takes the alert message and context from Courier, enriches with entity configuration, and returns a structured explanation with troubleshooting guidance. # Update Subscriber Credentials Source: https://docs.streamkap.com/api-reference/alerts/update-subscriber-credentials /openapi/openapi.json put /alerts/subscribers/{subscriberId}/credentials # Update Subscribers Source: https://docs.streamkap.com/api-reference/alerts/update-subscribers /openapi/openapi.json put /alerts/subscribers/{subscriberId} # Access Token Source: https://docs.streamkap.com/api-reference/authentication/access-token /openapi/openapi.json post /auth/access-token Exchange credentials for a JWT access token. Accepts either: - `client_id` + `secret` (classic API token flow) - `project_key`: a Streamkap Project Key credential file (raw JSON or base64-encoded) For `project_key` input, the API credentials are extracted from the bundle and used to issue the JWT. Kafka-only Project Keys (no API credentials) cannot be exchanged for a JWT. # Create Client Credential Source: https://docs.streamkap.com/api-reference/authentication/create-client-credential /openapi/openapi.json post /auth/client-credentials # Delete Client Credential Source: https://docs.streamkap.com/api-reference/authentication/delete-client-credential /openapi/openapi.json delete /auth/client-credentials/{client_id} # Get Mcp Tools Source: https://docs.streamkap.com/api-reference/authentication/get-mcp-tools /openapi/openapi.json get /auth/tools List available MCP tools with profile membership. # Get Permissions Source: https://docs.streamkap.com/api-reference/authentication/get-permissions /openapi/openapi.json get /auth/permissions # Get Roles Source: https://docs.streamkap.com/api-reference/authentication/get-roles /openapi/openapi.json get /auth/roles # List Client Credentials Source: https://docs.streamkap.com/api-reference/authentication/list-client-credentials /openapi/openapi.json get /auth/client-credentials # Refresh Access Token Source: https://docs.streamkap.com/api-reference/authentication/refresh-access-token /openapi/openapi.json post /auth/access-token/refresh Refresh access token. # Switch Tenant Source: https://docs.streamkap.com/api-reference/authentication/switch-tenant /openapi/openapi.json post /auth/switch-tenant Switch the caller's active tenant. Marks `tenant_id` as the caller's active tenant on the server side. The current JWT is NOT rotated — the caller must refresh their access token after this call before subsequent requests will be scoped to the new tenant. Returns `204 No Content` on success. The caller can only switch into tenants they belong to; other targets return 403. # Update Client Credential Source: https://docs.streamkap.com/api-reference/authentication/update-client-credential /openapi/openapi.json patch /auth/client-credentials/{client_id} # Whoami Source: https://docs.streamkap.com/api-reference/authentication/whoami /openapi/openapi.json get /auth/whoami Return the caller's verified identity: tenant, service, and credential linkage. Lightweight endpoint for clients to read backend-verified context without trusting locally-stored credential files. Works for all token types (Project Key, API token, user token). # Cancel Scale Operation Source: https://docs.streamkap.com/api-reference/cluster-scaling/cancel-scale-operation /openapi/openapi.json post /cluster/scale/{operation_id}/cancel Request cancellation. Controller checks between steps and stops if cancelled. Only works before step 4 (point of no return - pods already deleted). # Get Cluster Status Source: https://docs.streamkap.com/api-reference/cluster-scaling/get-cluster-status /openapi/openapi.json get /cluster/status Get current cluster status by fetching live state from EKS + K8s. Reads tier definitions from the cluster_configs collection, then queries the actual cluster to determine current tier, memory, and replicas. BYOC services without a cluster_configs record get a live-only view (no tier catalog, ``scaling_available: false``) that degrades to 'unknown' values instead of failing. # Get Scale Operation Status Source: https://docs.streamkap.com/api-reference/cluster-scaling/get-scale-operation-status /openapi/openapi.json get /cluster/scale/{operation_id} Get the current status of a scaling operation. Frontend polls this every 5 seconds. # Is Cluster Scaling Enabled Source: https://docs.streamkap.com/api-reference/cluster-scaling/is-cluster-scaling-enabled /openapi/openapi.json get /cluster/enabled Lightweight capability check for the cluster view. ``enabled``: the current service is BYOC, so the cluster info view is available. ``scaling_available``: a cluster_configs record also exists, so scale operations are possible. # Scale Cluster Source: https://docs.streamkap.com/api-reference/cluster-scaling/scale-cluster /openapi/openapi.json post /cluster/scale Create a scaling operation record. The skap-scaling controller picks it up and executes. Only one scaling operation can run at a time per service. Requires a BYOC service. # Get Consumer Group Detail Source: https://docs.streamkap.com/api-reference/consumer-groups/get-consumer-group-detail /openapi/openapi.json get /consumer-groups/{group_id} Get detailed information about a specific consumer group. Returns all members and paginated topic-partition assignments with member attribution. Use include_assignments=false for large consumer groups to skip assignment details. Pagination applies to topics, not members (all members are returned). Security: Consumer groups are tenant-isolated via k8s_namespace. Each tenant has their own Kafka cluster, so consumer groups from other tenants are not accessible. # List Consumer Groups Source: https://docs.streamkap.com/api-reference/consumer-groups/list-consumer-groups /openapi/openapi.json get /consumer-groups List consumer groups for the tenant with pagination. Excludes internal/system consumer groups. # Reset Consumer Group Offsets Source: https://docs.streamkap.com/api-reference/consumer-groups/reset-consumer-group-offsets /openapi/openapi.json post /consumer-groups/{group_id}/reset-offsets Reset offsets for selected topics in a consumer group. Supports multiselect topics and multiple reset strategies: - earliest: Reset to beginning of topic - latest: Reset to end of topic (skip all messages) - timestamp: Reset to specific timestamp - offset: Reset to specific offset value # Batch fetch topics for multiple entities Source: https://docs.streamkap.com/api-reference/dashboard/batch-fetch-topics-for-multiple-entities /openapi/openapi.json post /dashboard/lineage/topics/batch Returns enriched topics grouped by entity ID. Use for polling refresh or expand-all when groups are already loaded. Accepts 1-100 entity IDs. # Get dashboard health summary Source: https://docs.streamkap.com/api-reference/dashboard/get-dashboard-health-summary /openapi/openapi.json get /dashboard/health Returns a high-level health overview including broken connectors, failed transforms, active snapshots, and topic health metrics. # Get dashboard lineage groups Source: https://docs.streamkap.com/api-reference/dashboard/get-dashboard-lineage-groups /openapi/openapi.json get /dashboard/lineage Returns lineage groups (sources, destinations, or transforms) with aggregated metrics. Use the group_by parameter to select the grouping dimension. # Get dashboard lineage topics Source: https://docs.streamkap.com/api-reference/dashboard/get-dashboard-lineage-topics /openapi/openapi.json get /dashboard/lineage/topics Returns detailed topic-level lineage information including source, transforms, destinations, and per-topic metrics. Supports filtering and pagination. # Get latest QA row counts for topics Source: https://docs.streamkap.com/api-reference/dashboard/get-latest-qa-row-counts-for-topics /openapi/openapi.json post /dashboard/qa-counts Returns per-topic source_count, destination_count, and shard_counts from the most recent metrics_qa collection. Accepts lineage topic names (e.g., source_XXX.demo.shard1.Orders) and matches against metrics_qa topic_ids. # Get Organisation Statistics Source: https://docs.streamkap.com/api-reference/dashboard/get-organisation-statistics /openapi/openapi.json get /dashboard/organisation/statistics Fetch organisation statistics for the authenticated user with proper permissions. # Bulk Delete Destinations Source: https://docs.streamkap.com/api-reference/destinations/bulk-delete-destinations /openapi/openapi.json delete /destinations/bulk/delete Delete multiple destinations in parallel. Supports two modes: 1. Explicit IDs: Pass a list of destination IDs to delete 2. Select all: Set select_all=true with optional filters to delete all matching destinations Returns partial success results - continues processing even if individual deletes fail. # Bulk Reset Destinations Source: https://docs.streamkap.com/api-reference/destinations/bulk-reset-destinations /openapi/openapi.json post /destinations/bulk/reset Reset multiple destinations in parallel (stops, deletes offsets, resumes). Supports two modes: 1. Explicit IDs: Pass a list of destination IDs 2. Select all: Set select_all=true with optional filters Returns partial success results - continues processing even if individual resets fail. # Bulk Resume Destinations Source: https://docs.streamkap.com/api-reference/destinations/bulk-resume-destinations /openapi/openapi.json put /destinations/bulk/resume Resume multiple destinations in parallel. Supports two modes: 1. Explicit IDs: Pass a list of destination IDs 2. Select all: Set select_all=true with optional filters Returns partial success results - continues processing even if individual resumes fail. # Bulk Stop Destinations Source: https://docs.streamkap.com/api-reference/destinations/bulk-stop-destinations /openapi/openapi.json put /destinations/bulk/stop Stop multiple destinations in parallel. Supports two modes: 1. Explicit IDs: Pass a list of destination IDs 2. Select all: Set select_all=true with optional filters Returns partial success results - continues processing even if individual stops fail. # Create New Destination Source: https://docs.streamkap.com/api-reference/destinations/create-new-destination /openapi/openapi.json post /destinations spec: /specs/20_Destinations/spec.md#create-destination # Delete Existing Destination Source: https://docs.streamkap.com/api-reference/destinations/delete-existing-destination /openapi/openapi.json delete /destinations/{destination_id} # Deploy Destination Source: https://docs.streamkap.com/api-reference/destinations/deploy-destination /openapi/openapi.json post /destinations/{destination_id}/deploy Deploy a PENDING destination, transitioning it to ACTIVE. # Destination Configs Source: https://docs.streamkap.com/api-reference/destinations/destination-configs /openapi/openapi.json get /destinations/common_configurations # Destination Invite Source: https://docs.streamkap.com/api-reference/destinations/destination-invite /openapi/openapi.json post /destinations/invite # Explain Destination Error Source: https://docs.streamkap.com/api-reference/destinations/explain-destination-error /openapi/openapi.json post /destinations/{destination_id}/explain-error Explain a Broken destination's failure from its task trace, config, and optionally recent logs. # Get Destination Source: https://docs.streamkap.com/api-reference/destinations/get-destination /openapi/openapi.json get /destinations/{destination_id} spec: /specs/20_Destinations/spec.md#verify-running # Get Destination Config Comparison Source: https://docs.streamkap.com/api-reference/destinations/get-destination-config-comparison /openapi/openapi.json get /destinations/{destination_id}/config-history/compare-to-original Compare a destination's current config against its original recorded config (secret-masked diff). # Get Destination Config History Source: https://docs.streamkap.com/api-reference/destinations/get-destination-config-history /openapi/openapi.json get /destinations/{destination_id}/config-history Retrieve configuration change history for a destination with AI-generated summaries. # Get Destination Configuration Source: https://docs.streamkap.com/api-reference/destinations/get-destination-configuration /openapi/openapi.json get /destinations/configuration # Get destination metrics (POST) Source: https://docs.streamkap.com/api-reference/destinations/get-destination-metrics-post /openapi/openapi.json post /destinations/metrics POST alternative for GET /destinations/metrics. Use when URL length limits are exceeded (e.g., 100+ IDs). Supports both query parameters and request body. Body parameters take precedence over query parameters. # Get Destinations Source: https://docs.streamkap.com/api-reference/destinations/get-destinations /openapi/openapi.json get /destinations # Get Destinations Connectors Source: https://docs.streamkap.com/api-reference/destinations/get-destinations-connectors /openapi/openapi.json get /destinations/connectors List available destination connector types and their configuration schemas. Returns connector definitions grouped by display name. Each connector includes a `config` array describing every configuration property - use properties where `user_defined` is `true` to build the `config` object for the **POST /destinations** and **PUT /destinations/{destination_id}** endpoints. Pass `connector_code` (e.g., `snowflake`, `bigquery`, `clickhouse`) to retrieve the schema for a single connector type. Omit it to retrieve all available connectors. Each config property includes: - `name` - the dot-notation key to use in the `config` object - `description` - human-readable explanation - `required` - whether the property is mandatory - `display_name` - short label - `value.control` - input type (`string`, `password`, `one-select`, etc.) - `value.default` - default value, if any # Get Metrics For Destination Source: https://docs.streamkap.com/api-reference/destinations/get-metrics-for-destination /openapi/openapi.json get /destinations/{destination_id}/metrics Retrieve metrics for a specific destination based on the provided parameters. This endpoint returns metrics data for a given destination ID, allowing flexibility in the type of metrics returned (timeseries, timesummary, or latest), the time interval, and the time range. The response includes metadata about the metrics and is tailored to the specified time type. ### Parameters: - **destination_id** (path): The unique identifier of the destination for which metrics are requested. - **time_type** (query): Specifies the type of metrics to return: - `timeseries`: Metrics over a series of time intervals. - `timesummary`: Aggregated summary of metrics over the specified period. - `latest`: The most recent metrics available. - **time_interval** (query): The numeric interval between metrics data points (e.g., 1, 2, etc.). - **time_unit** (query): The unit of time for the interval (e.g., minute, hour, day, week, month). Combined with `time_interval` to define the granularity (e.g., 1 hour, 2 days). - **timestamp_from** (query, optional): The start time of the metrics data in UTC (ISO 8601 format, e.g., `2025-03-02T07:10:15Z`). Defaults to 24 hours before the current time if not provided. - **timestamp_to** (query, optional): The end time of the metrics data in UTC (ISO 8601 format). Defaults to the current time if not provided. ### Authentication: - Requires a valid user token with the `read:destination_metrics` permission. ### Returns: Depending on the `time_type`: - `DestinationTimeseriesMetricsWithMetadata`: For `timeseries` metrics. - `DestinationTimesummaryMetricsWithMetadata`: For `timesummary` metrics. - `DestinationLatestMetricsWithMetadata`: For `latest` metrics. ### Errors: - **400 Bad Request**: Invalid parameters or unexpected errors during processing. - **404 Not Found**: Metrics not found for the specified destination or connector plugin issues. - **500 Internal Server Error**: Connector plugin not found or misconfigured. ### Example Request: ```http https://api.streamkap.com/destinations/67ad6e0af08a2365c729ba79/metrics?time_type=latest&time_interval=2&time_unit=hour×tamp_from=2025-03-02T07:10:15Z×tamp_to=2025-03-04T07:10:15Z ``` ### Example Response: ```json { "data": { "connector_status": "Active", "latency": 0.0, "partitionCount": 1.0, "putBatchMaxTimeMs": 0.0, "recordsLag": 0.0, "sinkRecordReadTotal": 101.0, "recordsConsumedTotal": null, "sinkRecordSendTotal": 65.0, "bytesConsumedTotal": null, }, "metadata": { "connector_status": { "name": "Destination State", "unit": "enum" }, "latency": { "name": "Destination Latency", "unit": "milliseconds" }, "sinkRecordReadTotal": { "name": "Events (Received)", "unit": "count" }, "sinkRecordSendTotal": { "name": "Events (Sent)", "unit": "count" }, "partitionCount": { "name": "Partition Count", "unit": "count" }, "putBatchMaxTimeMs": { "name": "Put Batch Max Time", "unit": "milliseconds" }, "bytesConsumedTotal": { "name": "Volume", "unit": "bytes" }, "recordsConsumedTotal": { "name": "Consumed Events", "unit": "count" }, "recordsLag": { "name": "Lag in Events", "unit": "count" } } } ``` # Get Metrics For Destinations Source: https://docs.streamkap.com/api-reference/destinations/get-metrics-for-destinations /openapi/openapi.json get /destinations/metrics # Get Sample Scripts For Destinations Source: https://docs.streamkap.com/api-reference/destinations/get-sample-scripts-for-destinations /openapi/openapi.json get /destinations/sample_scripts # List All Destinations Brief Source: https://docs.streamkap.com/api-reference/destinations/list-all-destinations-brief /openapi/openapi.json get /destinations/brief # Pause Existing Destination Source: https://docs.streamkap.com/api-reference/destinations/pause-existing-destination /openapi/openapi.json put /destinations/{destination_id}/pause # Reset Existing Destination Source: https://docs.streamkap.com/api-reference/destinations/reset-existing-destination /openapi/openapi.json post /destinations/{destination_id}/reset Reset a destination connector by stopping it, deleting offsets, and resuming it. This will treat the connector as brand new and start consuming from the beginning. # Restart Destination Task Api Source: https://docs.streamkap.com/api-reference/destinations/restart-destination-task-api /openapi/openapi.json post /destinations/{destination_id}/restart_task # Restart Existing Destination Source: https://docs.streamkap.com/api-reference/destinations/restart-existing-destination /openapi/openapi.json post /destinations/{destination_id}/restart # Resume Existing Destination Source: https://docs.streamkap.com/api-reference/destinations/resume-existing-destination /openapi/openapi.json put /destinations/{destination_id}/resume # Search destinations (POST) Source: https://docs.streamkap.com/api-reference/destinations/search-destinations-post /openapi/openapi.json post /destinations/search POST alternative for GET /destinations. Use when URL length limits are exceeded (e.g., many filter values). Supports both query parameters and request body. Body parameters take precedence over query parameters. # Stop Existing Destination Source: https://docs.streamkap.com/api-reference/destinations/stop-existing-destination /openapi/openapi.json put /destinations/{destination_id}/stop # Update Bulk Destinations Source: https://docs.streamkap.com/api-reference/destinations/update-bulk-destinations /openapi/openapi.json put /destinations/bulk_update Update tags for multiple destinations in bulk. Supports two modes: 1. Explicit IDs: Pass a list of destination IDs to update 2. Select all: Set select_all=true with optional filters # Update Existing Destination Source: https://docs.streamkap.com/api-reference/destinations/update-existing-destination /openapi/openapi.json put /destinations/{destination_id} Update an existing destination connector. The `config` object contains connector-specific properties using **dot notation**. Use the **GET /destinations/connectors** endpoint with a `connector_code` query parameter to retrieve the full configuration schema for a specific connector type. # Cancel Flink Job Source: https://docs.streamkap.com/api-reference/flink-jobs/cancel-flink-job /openapi/openapi.json post /flink_jobs/{job_id}/cancel Cancel a running Flink job. # Delete Flink Job Source: https://docs.streamkap.com/api-reference/flink-jobs/delete-flink-job /openapi/openapi.json delete /flink_jobs/{job_id} Soft-delete a Flink job (cancels it first if running). # Deploy Jar Job Source: https://docs.streamkap.com/api-reference/flink-jobs/deploy-jar-job /openapi/openapi.json post /flink_jobs/jar Deploy a JAR-based Flink job. Upload a JAR file as multipart and specify main_class + optional args. Form fields: name, main_class, parallelism (default 1), job_args (JSON string). # Deploy Pyflink Job Source: https://docs.streamkap.com/api-reference/flink-jobs/deploy-pyflink-job /openapi/openapi.json post /flink_jobs/pyflink Deploy a PyFlink job. Either upload a Python file as multipart OR provide inline python_code as a form field. Form fields: name, parallelism (default 1), job_args (JSON string), python_code (alternative to file). # Get Flink Job Source: https://docs.streamkap.com/api-reference/flink-jobs/get-flink-job /openapi/openapi.json get /flink_jobs/{job_id} Get a Flink job with live status from Flink REST. # Get Flink Job Status Source: https://docs.streamkap.com/api-reference/flink-jobs/get-flink-job-status /openapi/openapi.json get /flink_jobs/{job_id}/status Get lightweight status for a Flink job (DB + live Flink state only). # List Flink Jobs Source: https://docs.streamkap.com/api-reference/flink-jobs/list-flink-jobs /openapi/openapi.json get /flink_jobs/ List all Flink jobs for the current tenant/service. # Restart Flink Job Source: https://docs.streamkap.com/api-reference/flink-jobs/restart-flink-job /openapi/openapi.json post /flink_jobs/{job_id}/restart Restart a cancelled or failed Flink job. # Create New Kafka User Source: https://docs.streamkap.com/api-reference/kafka-access/create-new-kafka-user /openapi/openapi.json post /kafka-access/kafka-users # Delete Kafka User Source: https://docs.streamkap.com/api-reference/kafka-access/delete-kafka-user /openapi/openapi.json delete /kafka-access/kafka-users/{username} Delete a Kafka user (and its proxy, ACLs, SCRAM credentials). If the user is linked to a Project Key the request still succeeds - the Project Key's Kafka credentials become invalid but the API credentials (if any) keep working. The response includes a warning message so the caller can surface it to the user. This is intentional: the previous 409 created a deadlock for DELETE_FAILED PKs where neither the PK nor its linked Kafka user could be removed. # List Kafka Users Source: https://docs.streamkap.com/api-reference/kafka-access/list-kafka-users /openapi/openapi.json get /kafka-access/kafka-users # Update Kafka User Source: https://docs.streamkap.com/api-reference/kafka-access/update-kafka-user /openapi/openapi.json put /kafka-access/kafka-users/{username} # Cancel Knowledge Base Source: https://docs.streamkap.com/api-reference/knowledge-bases/cancel-knowledge-base /openapi/openapi.json post /knowledge-bases/{kb_id}/cancel Cancel a running knowledge base (graceful, with savepoint). # Create Knowledge Base Source: https://docs.streamkap.com/api-reference/knowledge-bases/create-knowledge-base /openapi/openapi.json post /knowledge-bases/config Create a knowledge base pipeline, optionally deploying it. Set deploy=false to save as draft without deploying to Flink. # Delete Knowledge Base Source: https://docs.streamkap.com/api-reference/knowledge-bases/delete-knowledge-base /openapi/openapi.json delete /knowledge-bases/{kb_id} Soft-delete a knowledge base. # Get Knowledge Base Source: https://docs.streamkap.com/api-reference/knowledge-bases/get-knowledge-base /openapi/openapi.json get /knowledge-bases/{kb_id} Get a single knowledge base by ID. # List Knowledge Bases Source: https://docs.streamkap.com/api-reference/knowledge-bases/list-knowledge-bases /openapi/openapi.json get /knowledge-bases List all knowledge bases for the current tenant + service. # Retrieve Knowledge Base Source: https://docs.streamkap.com/api-reference/knowledge-bases/retrieve-knowledge-base /openapi/openapi.json post /knowledge-bases/{kb_id}/retrieve Embed ``query`` and return the top-k matching chunks from the KB. Auth: ``write:agents``. Chunks are customer data, same threat model as ``/agents/{id}/logs``. Tenant scoping: a ``kb_id`` from another tenant returns 404 just like the read endpoint — no existence leak. Rate limit: ``BUCKET_KB_RETRIEVAL`` at 30 req/min/tenant — same budget as the other outbound-call endpoints (MCP discovery, validate-llm, models-live). Dual-surface model: this endpoint is one of three KB retrieval paths. The Java agent runtime queries Pinecone directly at deploy time via its own mirror (off the hot path through this BE); the streamkap-tools MCP server exposes ``streamkap_kb_retrieve`` and proxies through this endpoint for third-party agents. # Stop Knowledge Base Source: https://docs.streamkap.com/api-reference/knowledge-bases/stop-knowledge-base /openapi/openapi.json post /knowledge-bases/{kb_id}/stop Stop a running knowledge base (immediate, no savepoint). # Get Logs Source: https://docs.streamkap.com/api-reference/logs/get-logs /openapi/openapi.json post /logs Get logs # Get Logs Count Source: https://docs.streamkap.com/api-reference/logs/get-logs-count /openapi/openapi.json post /logs/count Get count of new logs since a specific timestamp. Lightweight endpoint for real-time polling. Recommended polling interval: 5 seconds. Use this to show a "N new logs" badge in the UI. # Get Logs Histogram Source: https://docs.streamkap.com/api-reference/logs/get-logs-histogram /openapi/openapi.json post /logs/histogram Get histogram data for log counts bucketed by time. Returns time-series data suitable for rendering a histogram chart. # Summarise Log Source: https://docs.streamkap.com/api-reference/logs/summarise-log /openapi/openapi.json post /logs/summary Generate and store a structured summary for a single log entry. # Get Tenant Trees Source: https://docs.streamkap.com/api-reference/other/get-tenant-trees /openapi/openapi.json get /other/tenant/trees Get hierarchical trees of tenants. # Bulk Delete Pipelines Source: https://docs.streamkap.com/api-reference/pipelines/bulk-delete-pipelines /openapi/openapi.json delete /pipelines/bulk/delete Delete multiple pipelines in parallel. Supports two modes: 1. Explicit IDs: Pass a list of pipeline IDs to delete 2. Select all: Set select_all=true with optional filters to delete all matching pipelines Returns partial success results - continues processing even if individual deletes fail. # Create New Pipeline Source: https://docs.streamkap.com/api-reference/pipelines/create-new-pipeline /openapi/openapi.json post /pipelines spec: /specs/30_Topics_Pipelines_Streaming/spec.md#create-pipeline # Delete Existing Pipeline Source: https://docs.streamkap.com/api-reference/pipelines/delete-existing-pipeline /openapi/openapi.json delete /pipelines/{pipeline_id} # Get Logs For Pipelines Source: https://docs.streamkap.com/api-reference/pipelines/get-logs-for-pipelines /openapi/openapi.json get /pipelines/logs # Get Metrics For Pipeline Source: https://docs.streamkap.com/api-reference/pipelines/get-metrics-for-pipeline /openapi/openapi.json get /pipelines/{pipeline_id}/metrics Retrieve metrics for a specific pipeline based on the provided parameters. This endpoint returns latest metrics data for a given pipeline ID - timesummary and timeseries will be supported soon, the time interval, and the time range. The response includes metadata about the metrics and is tailored to the specified time type. ### Parameters: - **pipeline_id** (path): The unique identifier of the pipeline for which metrics are requested. - **time_interval** (query): The numeric interval between metrics data points (e.g., 1, 2, etc.). - **time_unit** (query): The unit of time for the interval (e.g., minute, hour, day, week, month). Combined with `time_interval` to define the granularity (e.g., 1 hour, 2 days). - **timestamp_from** (query, optional): The start time of the metrics data in UTC (ISO 8601 format, e.g., `2025-03-02T07:10:15Z`). Defaults to 24 hours before the current time if not provided. - **timestamp_to** (query, optional): The end time of the metrics data in UTC (ISO 8601 format). Defaults to the current time if not provided. ### Authentication: - Requires a valid user token with the `read:pipeline_metrics` permission. ### Returns: - `PipelineLatestMetricsWithMetadata`: `latest` metrics. ### Errors: - **400 Bad Request**: Invalid parameters or unexpected errors during processing. - **404 Not Found**: Metrics not found for the specified pipeline or connector plugin issues. - **500 Internal Server Error**: Connector plugin not found or misconfigured. ### Example Request: ```http https://api.streamkap.com/pipelines/67bca128e1519efb914e6b51/metrics?time_interval=2&time_unit=hour×tamp_from=2025-03-02T07:10:15Z×tamp_to=2025-03-04T07:10:15Z ``` ### Example Response: ```json { "data": { "latency": 1, "recordsLag": 0, "status": "Active" }, "metadata": { "latency": { "name": "Pipeline Latency", "unit": "milliseconds" }, "status": { "name": "Pipeline Status", "unit": "milliseconds" }, "recordsLag": { "name": "Lag in Events", "unit": "count" } } } ``` # Get Metrics For Pipelines Source: https://docs.streamkap.com/api-reference/pipelines/get-metrics-for-pipelines /openapi/openapi.json get /pipelines/metrics # Get Pipeline Source: https://docs.streamkap.com/api-reference/pipelines/get-pipeline /openapi/openapi.json get /pipelines/{pipeline_id} # Get pipeline metrics (POST) Source: https://docs.streamkap.com/api-reference/pipelines/get-pipeline-metrics-post /openapi/openapi.json post /pipelines/metrics POST alternative for GET /pipelines/metrics. Use when URL length limits are exceeded (e.g., 100+ IDs). Supports both query parameters and request body. Body parameters take precedence over query parameters. # Get Pipelines Source: https://docs.streamkap.com/api-reference/pipelines/get-pipelines /openapi/openapi.json get /pipelines # List All Pipelines Brief Source: https://docs.streamkap.com/api-reference/pipelines/list-all-pipelines-brief /openapi/openapi.json get /pipelines/brief # Search pipelines (POST) Source: https://docs.streamkap.com/api-reference/pipelines/search-pipelines-post /openapi/openapi.json post /pipelines/search POST alternative for GET /pipelines. Use when URL length limits are exceeded (e.g., many filter values). Supports both query parameters and request body. Body parameters take precedence over query parameters. # Trigger Pipeline Row Level Audit Source: https://docs.streamkap.com/api-reference/pipelines/trigger-pipeline-row-level-audit /openapi/openapi.json post /pipelines/row-level-audit spec: /specs/90_Audit_SelfHealing/spec.md#trigger-pipeline-row-level-audit # Update Bulk Pipelines Source: https://docs.streamkap.com/api-reference/pipelines/update-bulk-pipelines /openapi/openapi.json put /pipelines/bulk_update Update tags for multiple pipelines in bulk. Supports two modes: 1. Explicit IDs: Pass a list of pipeline IDs to update 2. Select all: Set select_all=true with optional filters # Update Existing Pipeline Source: https://docs.streamkap.com/api-reference/pipelines/update-existing-pipeline /openapi/openapi.json put /pipelines/{pipeline_id} # Create Project Key Source: https://docs.streamkap.com/api-reference/project-keys/create-project-key /openapi/openapi.json post /project-keys Create a Project Key with API credentials and optional Kafka access. Returns a credential file (JSON) with plaintext secrets. This is a one-time delivery - secrets are masked in all subsequent responses. Optionally creates a Kafka user with SCRAM credentials, K8s proxy, and ACLs. # Delete Project Key Source: https://docs.streamkap.com/api-reference/project-keys/delete-project-key /openapi/openapi.json delete /project-keys/{project_key_id} Delete a Project Key and all constituent resources (API credential, Kafka user, K8s proxy, ACLs). # Disable Project Key Agentic Source: https://docs.streamkap.com/api-reference/project-keys/disable-project-key-agentic /openapi/openapi.json post /project-keys/{project_key_id}/disable-agentic Toggle agentic use OFF for a Project Key. Erases ``agentic_secret_blob`` and clears the flag. Does NOT rotate the API credential - the PK keeps working for non-agentic uses (raw API calls, Kafka). Idempotent on already-disabled PKs. Rate limit: 5/min/tenant. Audit-logged with action=disable / disable_noop. # Enable Project Key Agentic Source: https://docs.streamkap.com/api-reference/project-keys/enable-project-key-agentic /openapi/openapi.json post /project-keys/{project_key_id}/enable-agentic Toggle agentic use ON for a Project Key (the agentic toggle). Atomic: rotates the API client credential, encrypts the new credential file blob with KMS, stores it on the PK row, sets ``agentic_enabled=True``. Idempotent on already-enabled PKs (no double rotation). Rate limit: 5/min/tenant (each call burns one Frontegg credential rotation). Returns 429 with ``Retry-After`` on bucket exhaustion. Audit-logged with action=enable / enable_noop, actor email + sub, old/new state, tenant_id, project_key_id, timestamp. # Get Project Key Source: https://docs.streamkap.com/api-reference/project-keys/get-project-key /openapi/openapi.json get /project-keys/{project_key_id} Get Project Key detail including Kafka ACLs and whitelist IPs for edit modal hydration. # Get Project Key Mcp Config Source: https://docs.streamkap.com/api-reference/project-keys/get-project-key-mcp-config /openapi/openapi.json get /project-keys/{project_key_id}/mcp-config Returns authoritative MCP tool scoping config for server-side enforcement. Called by the MCP server on session start to verify tool_profile, allowed_tools, and blocked_tools - preventing credential file tampering. # List Project Keys Source: https://docs.streamkap.com/api-reference/project-keys/list-project-keys /openapi/openapi.json get /project-keys List all Project Keys for the current tenant. Returns summaries with no secrets. # Update Project Key Source: https://docs.streamkap.com/api-reference/project-keys/update-project-key /openapi/openapi.json patch /project-keys/{project_key_id} Update a Project Key's name, description, roles, tool scoping, or Kafka ACLs. Supports additive capability transitions: - Send `kafka_config` on an API-only PK to add Kafka access. Response will include `new_kafka_credentials` with the plaintext Kafka password (shown once). - Send `role_ids` or `permission_ids` on a Kafka-only PK to add API credentials. Response will include `new_api_credentials` with the plaintext client_secret (shown once). Returns 400 if the key is in creating/deleting/delete_failed state. role_ids and permission_ids are mutually exclusive. # Get Schema By Id Source: https://docs.streamkap.com/api-reference/schema-registry/get-schema-by-id /openapi/openapi.json get /schema-registry/schemas/{schema_id} Get a schema by its global ID. # Get Schema By Version Source: https://docs.streamkap.com/api-reference/schema-registry/get-schema-by-version /openapi/openapi.json get /schema-registry/subjects/{subject}/versions/{version} Get a specific schema version for a subject. Version can be a number or 'latest'. # List Schema Subjects Source: https://docs.streamkap.com/api-reference/schema-registry/list-schema-subjects /openapi/openapi.json get /schema-registry/subjects List schema subjects for the tenant with pagination. Excludes internal subjects. Optional: include_metadata=true to get latest version info in a single request. # List Subject Versions Source: https://docs.streamkap.com/api-reference/schema-registry/list-subject-versions /openapi/openapi.json get /schema-registry/subjects/{subject}/versions List all version numbers for a specific schema subject. # Get All Service Metadata Source: https://docs.streamkap.com/api-reference/services/get-all-service-metadata /openapi/openapi.json get /services/all Fetch metadata for all tenant services. # Switch Source: https://docs.streamkap.com/api-reference/services/switch /openapi/openapi.json put /services/switch Switch service for the tenant. # Bulk Delete Sources Source: https://docs.streamkap.com/api-reference/sources/bulk-delete-sources /openapi/openapi.json delete /sources/bulk/delete Delete multiple sources in parallel. Supports two modes: 1. Explicit IDs: Pass a list of source IDs to delete 2. Select all: Set select_all=true with optional filters to delete all matching sources Returns partial success results - continues processing even if individual deletes fail. # Bulk Execute Snapshot Sources Source: https://docs.streamkap.com/api-reference/sources/bulk-execute-snapshot-sources /openapi/openapi.json post /sources/bulk/snapshot Execute snapshot on multiple sources in parallel. Supports both incremental (default) and blocking snapshot types via the snapshot_type field: - **incremental**: Uses watermarking to capture data while streaming continues. - **blocking**: Pauses streaming during snapshot. Required for keyless tables. Executed with lower concurrency and staggered timing to avoid overwhelming Kafka Connect. Supports two modes for source selection: 1. Explicit IDs: Pass a list of source IDs 2. Select all: Set select_all=true with optional filters Returns partial success results - continues processing even if individual snapshots fail (e.g. 409 when a snapshot is already running for a source). # Bulk Parallel Snapshot Sources Source: https://docs.streamkap.com/api-reference/sources/bulk-parallel-snapshot-sources /openapi/openapi.json post /sources/bulk/parallel-snapshot spec: /specs/40_Snapshots/spec.md#bulk-parallel-snapshots Execute Fast Parallel (parallel-chunk) snapshot on multiple sources. Mirrors the per-source ``POST /sources/execute_parallel_snapshot`` semantics fanned out across all targeted sources: seeds user intent into ``Mongo.sources.snapshotting_tables``; the in-process orchestrator picks up on its next cycle. No KC signal-table writes, no row/table locks. Supports two modes for source selection: 1. Explicit IDs: Pass a list of source IDs 2. Select all: Set select_all=true with optional filters Returns partial-success results — continues even when individual sources fail (e.g. regex source with no ``table.include.list``). # Bulk Reset Sources Source: https://docs.streamkap.com/api-reference/sources/bulk-reset-sources /openapi/openapi.json post /sources/bulk/reset Reset multiple sources in parallel (stops, deletes offsets, resumes). Supports two modes: 1. Explicit IDs: Pass a list of source IDs 2. Select all: Set select_all=true with optional filters Returns partial success results - continues processing even if individual resets fail. # Bulk Resume Sources Source: https://docs.streamkap.com/api-reference/sources/bulk-resume-sources /openapi/openapi.json put /sources/bulk/resume Resume multiple sources in parallel. Supports two modes: 1. Explicit IDs: Pass a list of source IDs 2. Select all: Set select_all=true with optional filters Returns partial success results - continues processing even if individual resumes fail. # Bulk Stop Snapshot Sources Source: https://docs.streamkap.com/api-reference/sources/bulk-stop-snapshot-sources /openapi/openapi.json post /sources/bulk/stop-snapshot Stop snapshot on multiple sources in parallel. Automatically detects the running snapshot type (incremental or blocking) and uses the appropriate cancellation method: - Incremental: Cancels via signal table, streaming continues - Blocking: Restarts the connector, streaming resumes after restart Uses lower concurrency to avoid overwhelming Kafka Connect. Supports two modes for source selection: 1. Explicit IDs: Pass a list of source IDs 2. Select all: Set select_all=true with optional filters Returns partial success results - continues processing even if individual stop operations fail. # Bulk Stop Sources Source: https://docs.streamkap.com/api-reference/sources/bulk-stop-sources /openapi/openapi.json put /sources/bulk/stop Stop multiple sources in parallel. Supports two modes: 1. Explicit IDs: Pass a list of source IDs 2. Select all: Set select_all=true with optional filters Returns partial success results - continues processing even if individual stops fail. # Cancel Snapshot Source: https://docs.streamkap.com/api-reference/sources/cancel-snapshot /openapi/openapi.json post /sources/cancel_snapshot Cancel an in-progress snapshot with automatic type detection. This endpoint automatically detects the type of snapshot currently running and uses the appropriate cancellation method: - **Incremental/Filtered snapshots**: Streaming continues. Progress is preserved and can be resumed later. - **Blocking snapshots**: Streaming resumes after cancellation. Partial progress is lost - must re-trigger if needed. Returns 404 if no snapshot is currently running for the specified source. # Create New Source Source: https://docs.streamkap.com/api-reference/sources/create-new-source /openapi/openapi.json post /sources Create a new source connector. The `config` object contains connector-specific properties using **dot notation** (e.g., `table.include.list`, `schema.include.list`, `database.hostname`). Use the **GET /sources/connectors** endpoint with a `connector_code` query parameter to retrieve the full configuration schema for a specific connector type. # Delete Existing Source Source: https://docs.streamkap.com/api-reference/sources/delete-existing-source /openapi/openapi.json delete /sources/{source_id} # Deploy Source Source: https://docs.streamkap.com/api-reference/sources/deploy-source /openapi/openapi.json post /sources/{source_id}/deploy Deploy a PENDING source, transitioning it to ACTIVE. # Execute Blocking Snapshot Source: https://docs.streamkap.com/api-reference/sources/execute-blocking-snapshot /openapi/openapi.json post /sources/execute_blocking_snapshot Execute a blocking snapshot for the specified source. Blocking snapshots pause streaming during the snapshot operation and use Debezium's initial snapshot process. They are useful for: - Snapshotting keyless tables (which incremental snapshots cannot handle) - Snapshotting large tables faster than incremental snapshots Warning: Streaming will be paused during the snapshot operation. Note: additional_conditions filters are not supported for blocking snapshots. # Execute Incremental Snapshot Source: https://docs.streamkap.com/api-reference/sources/execute-incremental-snapshot /openapi/openapi.json post /sources/execute_incremental_snapshot # Execute Parallel Snapshot Source: https://docs.streamkap.com/api-reference/sources/execute-parallel-snapshot /openapi/openapi.json post /sources/execute_parallel_snapshot # Execute Snapshot Source: https://docs.streamkap.com/api-reference/sources/execute-snapshot /openapi/openapi.json post /sources/snapshot Execute a snapshot for the specified source with configurable snapshot type. This unified endpoint supports both incremental and blocking snapshots: - **incremental** (default): Uses watermarking to capture data in chunks while streaming continues. Supports additional_conditions for filtered snapshots. Requires primary keys. - **blocking**: Pauses streaming during the snapshot operation. Required for keyless tables. Faster for large tables but blocks streaming until complete. Note: additional_conditions filters are not supported for blocking snapshots. For stopping/cancelling snapshots, use the cancel_snapshot endpoint which auto-detects the running snapshot type and uses the appropriate cancellation method. # Explain Source Error Source: https://docs.streamkap.com/api-reference/sources/explain-source-error /openapi/openapi.json post /sources/{source_id}/explain-error Explain a Broken source's failure from its task trace, config, and optionally recent logs. # Get Metrics For Source Source: https://docs.streamkap.com/api-reference/sources/get-metrics-for-source /openapi/openapi.json get /sources/{source_id}/metrics Retrieve metrics for a specific source based on the provided parameters. This endpoint returns metrics data for a given source ID, allowing flexibility in the type of metrics returned (timeseries, timesummary, or latest), the time interval, and the time range. The response includes metadata about the metrics and is tailored to the specified time type. ### Parameters: - **source_id** (path): The unique identifier of the source for which metrics are requested. - **time_type** (query): Specifies the type of metrics to return: - `timeseries`: Metrics over a series of time intervals. - `timesummary`: Aggregated summary of metrics over the specified period. - `latest`: The most recent metrics available. - **time_interval** (query): The numeric interval between metrics data points (e.g., 1, 2, etc.). - **time_unit** (query): The unit of time for the interval (e.g., minute, hour, day, week, month). Combined with `time_interval` to define the granularity (e.g., 1 hour, 2 days). - **timestamp_from** (query, optional): The start time of the metrics data in UTC (ISO 8601 format, e.g., `2025-03-02T07:10:15Z`). Defaults to 24 hours before the current time if not provided. - **timestamp_to** (query, optional): The end time of the metrics data in UTC (ISO 8601 format). Defaults to the current time if not provided. ### Authentication: - Requires a valid user token with the `read:source_metrics` permission. ### Returns: Depending on the `time_type`: - `SourceTimeseriesMetricsWithMetadata`: For `timeseries` metrics. - `SourceTimesummaryMetricsWithMetadata`: For `timesummary` metrics. - `SourceLatestMetricsWithMetadata`: For `latest` metrics. ### Errors: - **400 Bad Request**: Invalid parameters or unexpected errors during processing. - **404 Not Found**: Metrics not found for the specified source or connector plugin issues. - **500 Internal Server Error**: Connector plugin not found or misconfigured. ### Example Request: ```http https://api.streamkap.com/sources/67ad6e0af08a2365c729ba79/metrics?time_type=latest&time_interval=2&time_unit=hour×tamp_from=2025-03-02T07:10:15Z×tamp_to=2025-03-04T07:10:15Z ``` ### Example Response: ```json { "data": { "SnapshotTotalNumberOfEventsSeen": 20254, "SnapshotMilliSecondsSinceLastEvent": 0, "StreamingMilliSecondsBehindSource": 0, "StreamingTotalNumberOfDeleteEventsSeen": 312, "StreamingTotalNumberOfUpdateEventsSeen": 0, "StreamingTotalNumberOfCreateEventsSeen": 73, "StreamingMilliSecondsSinceLastEvent": 0, "StreamingLastEvent": "position: {ord: 14}, key: {"id" : ""ceaafb88-2a60-4bd9-8515-719ba64ea05c-close""}", "recordRetryTotal": null, "byteTotal": 9018776, "sourceRecordWriteTotal": 28977, "SnapshotRunning": 0, "SnapshotCompleted": 0, "StreamingConnected": 1, "recordSendTotal": null, "state": "Streaming", "streamingState": "connected", "snapshotStatus": [ { "status": "completed", "topic_id": "source_67a5c35e64af6fd83f4c17a6.Test.test_data1", "submit_timestamp": null }, { "status": "completed", "topic_id": "source_67a5c35e64af6fd83f4c17a6.Test.test_data2", "submit_timestamp": null } ], "snapshotState": "completed", "recordWrittenTotal": null, "connector_status": "Active", "latency": 0 }, "metadata": { "state": { "name": "Enabled State", "unit": "enum" }, "streamingState": { "name": "Streaming State", "unit": "enum" }, "snapshotStatus": { "name": "Snapshot Status", "unit": "enum" }, "snapshotState": { "name": "Snapshot State", "unit": "enum" }, "connector_status": { "name": "Source State", "unit": "enum" }, "latency": { "name": "Source Latency", "unit": "milliseconds" }, "byteTotal": { "name": "Volume", "unit": "bytes" }, "recordRetryTotal": { "name": "Retried", "unit": "count" }, "recordSendTotal": { "name": "Received", "unit": "count" }, "recordWrittenTotal": { "name": "Events Written", "unit": "count" }, "SnapshotTotalNumberOfEventsSeen": { "name": "Events (Snapshot)", "unit": "count" }, "SnapshotRunning": { "name": "Snapshot Running?", "unit": "boolean" }, "SnapshotCompleted": { "name": "Snapshot Completed?", "unit": "boolean" }, "SnapshotMilliSecondsSinceLastEvent": { "name": "Time Since Last Snapshot Event", "unit": "milliseconds" }, "StreamingMilliSecondsBehindSource": { "name": "Latency Behind Source", "unit": "milliseconds" }, "StreamingConnected": { "name": "Streaming Connected?", "unit": "boolean" }, "StreamingTotalNumberOfDeleteEventsSeen": { "name": "Events (Delete)", "unit": "count" }, "StreamingTotalNumberOfUpdateEventsSeen": { "name": "Events (Update)", "unit": "count" }, "StreamingTotalNumberOfCreateEventsSeen": { "name": "Events (Create)", "unit": "count" }, "StreamingMilliSecondsSinceLastEvent": { "name": "Time Since Last Streaming Event", "unit": "milliseconds" }, "StreamingLastEvent": { "name": "Last Streaming Event", "unit": "id" }, "sourceRecordWriteTotal": { "name": "Events Written", "unit": "count" } } } ``` # Get Metrics For Sources Source: https://docs.streamkap.com/api-reference/sources/get-metrics-for-sources /openapi/openapi.json get /sources/metrics # Get Source Source: https://docs.streamkap.com/api-reference/sources/get-source /openapi/openapi.json get /sources/{source_id} # Get Source Config Comparison Source: https://docs.streamkap.com/api-reference/sources/get-source-config-comparison /openapi/openapi.json get /sources/{source_id}/config-history/compare-to-original Compare a source's current config against its original recorded config (secret-masked diff). # Get Source Config History Source: https://docs.streamkap.com/api-reference/sources/get-source-config-history /openapi/openapi.json get /sources/{source_id}/config-history Retrieve configuration change history for a source with AI-generated summaries. # Get Source Configuration Source: https://docs.streamkap.com/api-reference/sources/get-source-configuration /openapi/openapi.json get /sources/configuration # Get source metrics (POST) Source: https://docs.streamkap.com/api-reference/sources/get-source-metrics-post /openapi/openapi.json post /sources/metrics POST alternative for GET /sources/metrics. Use when URL length limits are exceeded (e.g., 100+ IDs). Supports both query parameters and request body. Body parameters take precedence over query parameters. # Get Sources Source: https://docs.streamkap.com/api-reference/sources/get-sources /openapi/openapi.json get /sources # Get Sources Connectors Source: https://docs.streamkap.com/api-reference/sources/get-sources-connectors /openapi/openapi.json get /sources/connectors List available source connector types and their configuration schemas. Returns connector definitions grouped by display name. Each connector includes a `config` array describing every configuration property - use properties where `user_defined` is `true` to build the `config` object for the **POST /sources** and **PUT /sources/{source_id}** endpoints. Pass `connector_code` (e.g., `postgresql`, `mysql`, `mongodb`) to retrieve the schema for a single connector type. Omit it to retrieve all available connectors. Each config property includes: - `name` - the dot-notation key to use in the `config` object (e.g., `database.hostname.user.defined`) - `description` - human-readable explanation - `required` - whether the property is mandatory - `display_name` - short label - `value.control` - input type (`string`, `password`, `one-select`, etc.) - `value.default` - default value, if any # List All Sources Brief Source: https://docs.streamkap.com/api-reference/sources/list-all-sources-brief /openapi/openapi.json get /sources/brief # Pause Existing Source Source: https://docs.streamkap.com/api-reference/sources/pause-existing-source /openapi/openapi.json put /sources/{source_id}/pause # Reset Existing Source Source: https://docs.streamkap.com/api-reference/sources/reset-existing-source /openapi/openapi.json post /sources/{source_id}/reset Reset a source connector by stopping it, deleting offsets, and resuming it. This will treat the connector as brand new and perform an initial snapshot. # Restart Existing Source Source: https://docs.streamkap.com/api-reference/sources/restart-existing-source /openapi/openapi.json post /sources/{source_id}/restart # Restart Source Task Api Source: https://docs.streamkap.com/api-reference/sources/restart-source-task-api /openapi/openapi.json post /sources/{source_id}/restart_task # Resume Existing Source Source: https://docs.streamkap.com/api-reference/sources/resume-existing-source /openapi/openapi.json put /sources/{source_id}/resume # Search sources (POST) Source: https://docs.streamkap.com/api-reference/sources/search-sources-post /openapi/openapi.json post /sources/search POST alternative for GET /sources. Use when URL length limits are exceeded (e.g., many filter values). Supports both query parameters and request body. Body parameters take precedence over query parameters. # Source Configs Source: https://docs.streamkap.com/api-reference/sources/source-configs /openapi/openapi.json get /sources/common_configurations # Source Invite Source: https://docs.streamkap.com/api-reference/sources/source-invite /openapi/openapi.json post /sources/invite # Stop Blocking Snapshot Source: https://docs.streamkap.com/api-reference/sources/stop-blocking-snapshot /openapi/openapi.json post /sources/stop_blocking_snapshot Stop a blocking snapshot for the specified source. This endpoint stops an in-progress blocking snapshot by restarting the connector. The snapshot will be marked as cancelled and streaming will resume. # Stop Existing Source Source: https://docs.streamkap.com/api-reference/sources/stop-existing-source /openapi/openapi.json put /sources/{source_id}/stop # Stop Incremental Snapshot Source: https://docs.streamkap.com/api-reference/sources/stop-incremental-snapshot /openapi/openapi.json post /sources/stop_incremental_snapshot # Update Bulk Sources Source: https://docs.streamkap.com/api-reference/sources/update-bulk-sources /openapi/openapi.json put /sources/bulk_update Update tags for multiple sources in bulk. Supports two modes: 1. Explicit IDs: Pass a list of source IDs to update 2. Select all: Set select_all=true with optional filters # Update Existing Source Source: https://docs.streamkap.com/api-reference/sources/update-existing-source /openapi/openapi.json put /sources/{source_id} Update an existing source connector. The `config` object contains connector-specific properties using **dot notation** (e.g., `table.include.list`, `schema.include.list`, `database.hostname`). To add tables, include the full `table.include.list` value with the new tables appended. Use the **GET /sources/connectors** endpoint with a `connector_code` query parameter to retrieve the full configuration schema for a specific connector type. # Create Tag Source: https://docs.streamkap.com/api-reference/tags/create-tag /openapi/openapi.json post /tags Create a new tag. # Delete Tag Source: https://docs.streamkap.com/api-reference/tags/delete-tag /openapi/openapi.json delete /tags/{tag_id} Delete a tag. # Get Tags By Type Source: https://docs.streamkap.com/api-reference/tags/get-tags-by-type /openapi/openapi.json get /tags/{tag_type} Get tags by tag_type. # Get Tags For Specific Tenant Source: https://docs.streamkap.com/api-reference/tags/get-tags-for-specific-tenant /openapi/openapi.json get /tags Get tags by tag_ids and tag_type. # Search tags (POST) Source: https://docs.streamkap.com/api-reference/tags/search-tags-post /openapi/openapi.json post /tags/search POST alternative for GET /tags. Use when URL length limits are exceeded (e.g., 100+ tag IDs). Supports both query parameters and request body. Body parameters take precedence over query parameters. # Update Tag Source: https://docs.streamkap.com/api-reference/tags/update-tag /openapi/openapi.json put /tags/{tag_id} Update a tag. # Browse topics for SQL Studio Source: https://docs.streamkap.com/api-reference/topics/browse-topics-for-sql-studio /openapi/openapi.json get /topics/catalog Get a browsable catalog of topics with schema metadata for the Query Playground. # Browse topics grouped by source Source: https://docs.streamkap.com/api-reference/topics/browse-topics-grouped-by-source /openapi/openapi.json get /topics/catalog/grouped Get topics grouped by source connector for the SQL Studio tree sidebar. # Bulk Snapshot Topics Source: https://docs.streamkap.com/api-reference/topics/bulk-snapshot-topics /openapi/openapi.json post /topics/bulk/snapshot Execute snapshot for selected topics, grouped by their source connector. Accepts topic DB IDs, resolves them to their source connectors, and triggers per-source snapshots scoped to only the selected topics (via topic_names). Only topics produced by source connectors can be snapshotted - topics from transforms, destinations, or manually created topics are skipped. Supports both incremental (default) and blocking snapshot types: - **incremental**: Uses watermarking to capture data while streaming continues. - **blocking**: Pauses streaming during snapshot. Required for keyless tables. Results are reported per source connector, since snapshots are source-level operations. Returns partial success results - continues processing even if individual snapshots fail. # Bulk Stop Snapshot Topics Source: https://docs.streamkap.com/api-reference/topics/bulk-stop-snapshot-topics /openapi/openapi.json post /topics/bulk/stop-snapshot Stop snapshot for sources identified by the selected topics. Accepts topic DB IDs, resolves them to their source connectors, and stops any running snapshots on those sources. Note: Because snapshots run at the source level, stopping affects all topics for that source, not just the selected ones. Automatically detects the running snapshot type (incremental or blocking) and uses the appropriate cancellation method: - Incremental: Cancels via signal table, streaming continues. - Blocking: Restarts the connector, streaming resumes after restart. Returns partial success results - continues processing even if individual stop operations fail. # Create a new Kafka topic Source: https://docs.streamkap.com/api-reference/topics/create-a-new-kafka-topic /openapi/openapi.json post /topics # Delete Topic Api Source: https://docs.streamkap.com/api-reference/topics/delete-topic-api /openapi/openapi.json delete /topics/{topic_id} # Execute SQL query against Kafka topics Source: https://docs.streamkap.com/api-reference/topics/execute-sql-query-against-kafka-topics /openapi/openapi.json post /topics/query Parse and execute a SQL query against a Kafka topic. # Get a single topic message value Source: https://docs.streamkap.com/api-reference/topics/get-a-single-topic-message-value /openapi/openapi.json get /topics/messages/value Fetch the full value of a single topic message on demand (e.g. for oversized messages). # Get comprehensive topic information Source: https://docs.streamkap.com/api-reference/topics/get-comprehensive-topic-information /openapi/openapi.json get /topics/{topic_id} # Get latest raw metrics for a topic Source: https://docs.streamkap.com/api-reference/topics/get-latest-raw-metrics-for-a-topic /openapi/openapi.json get /topics/{topic_id}/raw-metrics Returns the latest raw metric values (per partition / task / context) from the ClickHouse `metrics` and `metrics_qa` tables for a single topic — bypassing the materialized-view aggregation that powers the lineage / observability surfaces. Intended for debugging discrepancies between what raw CH holds and what the UI renders. Returns one row per (metric_name, partition, task, context); the caller is responsible for aggregating across partitions if needed (sum for lag, max for latency, etc.). # Get schema for a single topic Source: https://docs.streamkap.com/api-reference/topics/get-schema-for-a-single-topic /openapi/openapi.json get /topics/catalog/schema Lazy-fetch schema fields for a single topic (called on tree node expand). # Get topic configurations Source: https://docs.streamkap.com/api-reference/topics/get-topic-configurations /openapi/openapi.json get /topics/configs # Get topic messages Source: https://docs.streamkap.com/api-reference/topics/get-topic-messages /openapi/openapi.json get /topics/messages # Get topic metrics Source: https://docs.streamkap.com/api-reference/topics/get-topic-metrics /openapi/openapi.json get /topics/metrics # Get topic statistics Source: https://docs.streamkap.com/api-reference/topics/get-topic-statistics /openapi/openapi.json get /topics/statistics # List all topics Source: https://docs.streamkap.com/api-reference/topics/list-all-topics /openapi/openapi.json get /topics/details # List topics grouped by connector/entity Source: https://docs.streamkap.com/api-reference/topics/list-topics-grouped-by-connectorentity /openapi/openapi.json get /topics/details/grouped Get topics grouped by entity_id (connector instances) with nested pagination. Each group represents a source/transform/destination entity and contains its topics. Supports dual pagination (groups and topics within groups) and dual sorting. Supports multiple entity types via comma-separated values (e.g., entity_type=sources,transforms). Note: group_id requires exactly one entity_type to be specified. # Per-topic Kafka metadata for the topics list Source: https://docs.streamkap.com/api-reference/topics/per-topic-kafka-metadata-for-the-topics-list /openapi/openapi.json post /topics/table_metrics Returns per-topic Kafka broker metadata (partition_count, replication_factor, retention_ms) and lastMessageTimestamp, keyed by topic_id. `sources`/`transforms` entities must carry matching `topic_ids` and `topic_db_ids` (used for ownership verification); such entities missing `topic_db_ids` return HTTP 400. `destinations` are exempt (send `topic_db_ids: []`) — they are verified against the destination's topic_map. Topic names must match `[a-zA-Z0-9._-]{1,249}`; up to 1000 per entity. All `kafka.*` fields are nullable — `null` means the broker did not return metadata for that topic, which is distinct from a legitimate `0`. Values may be up to ~60 s stale for `partition_count` / `replication_factor` / `retention_ms` and up to ~10 s stale for `lastMessageTimestamp`. # Publish a record to a Streamkap/KC internal topic (local + admin app only) Source: https://docs.streamkap.com/api-reference/topics/publish-a-record-to-a-streamkapkc-internal-topic-local-+-admin-app-only /openapi/openapi.json post /topics/internal/publish # Search topics (POST) Source: https://docs.streamkap.com/api-reference/topics/search-topics-post /openapi/openapi.json post /topics/details/search POST alternative for GET /topics/details. Use when URL length limits are exceeded (e.g., 100+ entity IDs). Supports both query parameters and request body. Body parameters take precedence over query parameters. # Update Bulk Topics Source: https://docs.streamkap.com/api-reference/topics/update-bulk-topics /openapi/openapi.json put /topics/bulk_update Update tags for multiple topics in bulk. Supports two modes: 1. Explicit IDs: Pass a list of topic IDs to update 2. Select all: Set select_all=true with optional filters # Update Topic Api Source: https://docs.streamkap.com/api-reference/topics/update-topic-api /openapi/openapi.json put /topics/{topic_id} # Bulk Clone Transforms Source: https://docs.streamkap.com/api-reference/transforms/bulk-clone-transforms /openapi/openapi.json post /transforms/bulk/clone Clone multiple transforms with new names in parallel. Supports two modes: 1. Explicit items: Provide list of items with id and name for each clone 2. Select all: Set select_all=true with optional filters and name_suffix Returns partial success results - continues processing even if individual clones fail. # Bulk Delete Transforms Source: https://docs.streamkap.com/api-reference/transforms/bulk-delete-transforms /openapi/openapi.json delete /transforms/bulk/delete Delete multiple transforms in parallel. Supports two modes: 1. Explicit IDs: Pass a list of transform IDs to delete 2. Select all: Set select_all=true with optional filters to delete all matching transforms Returns partial success results - continues processing even if individual deletes fail. # Clear Output Topics Source: https://docs.streamkap.com/api-reference/transforms/clear-output-topics /openapi/openapi.json put /transforms/{transforms_id}/clear-output-topics/{output_topic_pattern} # Clone Transform Source: https://docs.streamkap.com/api-reference/transforms/clone-transform /openapi/openapi.json post /transforms/{transform_id}/clone # Create New Transform Source: https://docs.streamkap.com/api-reference/transforms/create-new-transform /openapi/openapi.json post /transforms # Create New Transform With Full Process Source: https://docs.streamkap.com/api-reference/transforms/create-new-transform-with-full-process /openapi/openapi.json post /transforms/full-process # Delete Existing Transform Source: https://docs.streamkap.com/api-reference/transforms/delete-existing-transform /openapi/openapi.json delete /transforms # Deploy Job Live Source: https://docs.streamkap.com/api-reference/transforms/deploy-job-live /openapi/openapi.json put /transforms/{transforms_id}/deploy-job-live/{version_id} # Deploy Job Preview Source: https://docs.streamkap.com/api-reference/transforms/deploy-job-preview /openapi/openapi.json put /transforms/{transforms_id}/deploy-job-preview/{version_id} # Get Errors Transform Source: https://docs.streamkap.com/api-reference/transforms/get-errors-transform /openapi/openapi.json get /transforms/{transforms_id}/errors # Get Full Transforms Source: https://docs.streamkap.com/api-reference/transforms/get-full-transforms /openapi/openapi.json get /transforms # Get Full Transforms With Id Source: https://docs.streamkap.com/api-reference/transforms/get-full-transforms-with-id /openapi/openapi.json get /transforms/{transform_id} # Get Jobs Status Source: https://docs.streamkap.com/api-reference/transforms/get-jobs-status /openapi/openapi.json get /transforms/{transforms_id}/job_status # Get Output Topics For Transform Source: https://docs.streamkap.com/api-reference/transforms/get-output-topics-for-transform /openapi/openapi.json get /transforms/{transforms_id}/output-topics # Get Topics For Pattern Source: https://docs.streamkap.com/api-reference/transforms/get-topics-for-pattern /openapi/openapi.json get /transforms/topics-for-pattern/{topic_pattern}/{java_replacement_pattern} # Get Transform Checkpoints Source: https://docs.streamkap.com/api-reference/transforms/get-transform-checkpoints /openapi/openapi.json get /transforms/{transform_id}/checkpoints Get checkpoint statistics and state size information for a transform job. Returns checkpoint counts, summary statistics (state size, duration, etc.), latest checkpoint details, and checkpoint history. Args: transform_id: The transform ID Returns: TransformCheckpointsInfo: Checkpoint statistics and history None: If transform job not found or no checkpoints available # Get Transform Configs Source: https://docs.streamkap.com/api-reference/transforms/get-transform-configs /openapi/openapi.json get /transforms/configs # Get Transform Details Source: https://docs.streamkap.com/api-reference/transforms/get-transform-details /openapi/openapi.json get /transforms/details # Get Transform Dlq Errors Source: https://docs.streamkap.com/api-reference/transforms/get-transform-dlq-errors /openapi/openapi.json get /transforms/{transform_id}/dlq Get DLQ errors for transform - retrieves the first DLQ error from the topic and shows: timestamp, error message, message offset, link to the DLQ topic. Returns: TransformDLQError: First DLQ error with parsed error message None: If transform not found, DLQ topic empty, or access error # Get Transform Metrics Source: https://docs.streamkap.com/api-reference/transforms/get-transform-metrics /openapi/openapi.json get /transforms/metrics # Get Transforms Implementation Details Source: https://docs.streamkap.com/api-reference/transforms/get-transforms-implementation-details /openapi/openapi.json get /transforms/{transforms_id}/implementation_details # List All Transforms Brief Source: https://docs.streamkap.com/api-reference/transforms/list-all-transforms-brief /openapi/openapi.json get /transforms/brief # Put Transforms Implementation Details Source: https://docs.streamkap.com/api-reference/transforms/put-transforms-implementation-details /openapi/openapi.json put /transforms/{transforms_id}/implementation_details # Run Unit Tests Source: https://docs.streamkap.com/api-reference/transforms/run-unit-tests /openapi/openapi.json put /transforms/{transforms_id}/run-unit-tests/{version_id} # Search transforms (POST) Source: https://docs.streamkap.com/api-reference/transforms/search-transforms-post /openapi/openapi.json post /transforms/search POST alternative for GET /transforms. Use when URL length limits are exceeded (e.g., 100+ tag IDs). Supports both query parameters and request body. Body parameters take precedence over query parameters. # Set Input Output Topics Source: https://docs.streamkap.com/api-reference/transforms/set-input-output-topics /openapi/openapi.json put /transforms/{transforms_id}/set-input-output-topics/{topic_pattern}/{java_replacement_pattern} # Update Bulk Transforms Source: https://docs.streamkap.com/api-reference/transforms/update-bulk-transforms /openapi/openapi.json put /transforms/bulk_update Update tags for multiple transforms in bulk. Supports two modes: 1. Explicit IDs: Pass a list of transform IDs to update 2. Select all: Set select_all=true with optional filters # Update Transform Source: https://docs.streamkap.com/api-reference/transforms/update-transform /openapi/openapi.json put /transforms # Get Usage Export Source: https://docs.streamkap.com/api-reference/usage/get-usage-export /openapi/openapi.json get /billing/usage/export Export usage metrics as a CSV. # Get Usage Metrics Source: https://docs.streamkap.com/api-reference/usage/get-usage-metrics /openapi/openapi.json get /billing/usage/metrics Fetch detailed usage metrics for the organisation. # Get usage metrics (POST) Source: https://docs.streamkap.com/api-reference/usage/get-usage-metrics-post /openapi/openapi.json post /billing/usage/metrics POST alternative for GET /billing/usage/metrics. Use when URL length limits are exceeded (e.g., 100+ tenant IDs). Supports both query parameters and request body. Body parameters take precedence over query parameters. # Get Usage Summary Source: https://docs.streamkap.com/api-reference/usage/get-usage-summary /openapi/openapi.json get /billing/usage/summary Fetch usage summary for the billing page. # API Tokens Source: https://docs.streamkap.com/api-tokens How to generate and manage your Streamkap API tokens ## Create an API Token Before you can use the Streamkap REST API, you'll need to generate an API token. API tokens are scoped to **Projects** and **Roles** in the Streamkap platform. To generate an API token: * Navigate to your project and go to **Project Settings**. * Click on the **API** tab to access API Tokens. * Click **Create API Token**: * Enter a description e.g. 'Read-only API access' * Choose the role(s) it applies to e.g. Read Only * Click **Create Token**. Create API Token dialog with description and role selection Once the API token is created, you will have one chance to copy and save its Token somewhere safe. Once you have done that, click **Done**. Generated API Token showing Client ID and Token secret Once you have a **Client ID** and **Token**, you can make a call to the **Get Access Token** REST API endpoint (under the **API Reference** tab) with those values to get your REST API access and refresh tokens. ## Manage API Tokens Tokens can't be moved between Projects or have the Roles scope changed after they're created. To create, view and delete API Tokens: * Navigate to your project and go to **Project Settings**. * Click on the **API** tab to access API Tokens. The Token (or 'secret') is only shown **once**. If you have lost or forgotten an existing Tokens' secret, you will need to delete it and create another. API Tokens list showing Client IDs, descriptions, roles, and creation info # Audit Logs Source: https://docs.streamkap.com/audit-logs Full audit logs available for security compliance Streamkap maintains comprehensive audit logs for security compliance and activity tracking. All user actions, authentication events, and configuration changes are recorded. ## Key Features * Complete activity history for all users * Searchable and filterable log entries * Exportable for compliance reporting * Tracks authentication events, API usage, and configuration changes ## Accessing Audit Logs For detailed instructions on viewing and using audit logs, see [View Audit Logs](/view-audit-logs). # AWS Cross-Account IAM Setup Source: https://docs.streamkap.com/aws-cross-account-iam Configure IAM roles for Streamkap to securely access your AWS resources using cross-account role assumption. Several Streamkap connectors need access to resources in your AWS account. Instead of sharing long-lived access keys, Streamkap uses **cross-account IAM role assumption** -- a secure, auditable mechanism built into AWS. ## When You Need Cross-Account Access Cross-account IAM configuration is required when Streamkap needs to interact with AWS resources that live in **your** AWS account: * **S3 destination** -- writing CDC data to your S3 bucket * **Apache Iceberg with AWS Glue catalog** -- managing Glue catalog tables and writing to your S3 storage * **Redshift** -- loading data into a Redshift cluster in your account * **BYOC deployments** -- provisioning and managing infrastructure in your AWS sub-account * **Any AWS resource** not hosted in Streamkap's managed account Some connectors (such as [S3](/s3)) also support AWS Access Key / Secret Access Key authentication. Cross-account IAM role assumption is the recommended approach for production environments because it avoids long-lived credentials. ## How It Works Cross-account role assumption follows a trust-based model: 1. You create an **IAM role** in your AWS account with the permissions Streamkap needs. 2. You attach a **trust policy** to that role that allows Streamkap's AWS account to assume it. 3. Streamkap calls `sts:AssumeRole` to obtain temporary credentials scoped to that role. 4. Streamkap uses those temporary credentials to access your resources. This means no permanent credentials are exchanged. Streamkap receives short-lived session tokens that automatically expire, and every action is logged in AWS CloudTrail under the assumed role. ## Step-by-Step Setup 1. Sign in to the [AWS IAM Console](https://console.aws.amazon.com/iam/). 2. Navigate to **Roles** in the sidebar. 3. Click **Create role**. 4. Under **Trusted entity type**, select **Custom trust policy**. 5. Paste the trust policy from the [Trust Policy Template](#trust-policy-template) section below. 6. Click **Next**. Attach one or more permission policies depending on which Streamkap connectors you are using. See the [Permission Policies by Resource](#permission-policies-by-resource) section for ready-to-use templates. 1. On the **Add permissions** page, click **Create policy** (opens in a new tab). 2. Switch to the **JSON** tab and paste the appropriate policy. 3. Name the policy descriptively (e.g., `StreamkapS3Access`, `StreamkapGlueAccess`). 4. Return to the role creation tab and attach the newly created policy. 5. Click **Next**. 1. Enter a **Role name** (e.g., `StreamkapConnectorAccess`). 2. Review the trust policy and permissions. 3. Click **Create role**. 4. On the role detail page, copy the **Role ARN** (e.g., `arn:aws:iam:::role/StreamkapConnectorAccess`). During connector configuration in the Streamkap app, paste the Role ARN into the **AWS IAM Role** field. For BYOC deployments, share the Role ARN with the Streamkap team directly. ## Trust Policy Template The trust policy controls **who** can assume the role. The following template grants Streamkap's AWS account the ability to assume the role, with an external ID condition to prevent [confused deputy](https://docs.aws.amazon.com/IAM/latest/UserGuide/confused-deputy.html) attacks. ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::300973880807:root" }, "Action": "sts:AssumeRole", "Condition": { "StringEquals": { "sts:ExternalId": "" } } } ] } ``` Streamkap's AWS account ID is `300973880807`. The external ID will be provided during connector setup in the Streamkap app. If you need these values before starting the connector configuration, contact [Streamkap support](https://streamkap.com/contact). For some connectors (such as [AWS Glue](/aws-glue)), the trust policy principal may reference a specific IAM role rather than the account root. In that case, use: ```json theme={null} "Principal": { "AWS": "arn:aws:iam::300973880807:role/kafkaConnectTenantAccessRole" } ``` The Streamkap app will indicate which principal to use during connector setup. ## Permission Policies by Resource Attach only the permissions required for your specific use case. Each tab below provides a minimal policy template scoped to specific resources. Required for the [S3 destination](/s3) connector and any connector that writes data to your S3 bucket (including [Apache Iceberg](/apache-iceberg)). Replace `` with your actual S3 bucket name. ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Sid": "StreamkapS3Access", "Effect": "Allow", "Action": [ "s3:PutObject", "s3:GetObject", "s3:DeleteObject", "s3:ListBucket", "s3:AbortMultipartUpload", "s3:ListMultipartUploadParts", "s3:ListBucketMultipartUploads", "s3:GetBucketLocation" ], "Resource": [ "arn:aws:s3:::", "arn:aws:s3:::/*" ] } ] } ``` Required for [Apache Iceberg with AWS Glue catalog](/aws-glue). This policy grants access to manage Glue catalog metadata and the underlying S3 storage. Replace ``, ``, ``, and `` with your actual values. ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Sid": "StreamkapGlueCatalogAccess", "Effect": "Allow", "Action": [ "glue:GetDatabase", "glue:GetDatabases", "glue:CreateTable", "glue:GetTable", "glue:GetTables", "glue:UpdateTable", "glue:DeleteTable", "glue:CreatePartition", "glue:GetPartition", "glue:GetPartitions", "glue:BatchCreatePartition", "glue:UpdatePartition", "glue:DeletePartition", "glue:BatchDeletePartition" ], "Resource": [ "arn:aws:glue:::catalog", "arn:aws:glue:::database/", "arn:aws:glue:::table//*" ] }, { "Sid": "StreamkapGlueS3Access", "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket", "s3:AbortMultipartUpload", "s3:GetBucketLocation" ], "Resource": [ "arn:aws:s3:::", "arn:aws:s3:::/*" ] } ] } ``` Required for the [Redshift destination](/redshift) when using IAM-based authentication. This policy grants the ability to obtain temporary database credentials. Replace ``, ``, ``, ``, and `` with your actual values. **Redshift Provisioned** ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Sid": "StreamkapRedshiftAccess", "Effect": "Allow", "Action": [ "redshift:GetClusterCredentials", "redshift:DescribeClusters" ], "Resource": [ "arn:aws:redshift:::cluster:", "arn:aws:redshift:::dbname:/", "arn:aws:redshift:::dbuser:/" ] } ] } ``` **Redshift Serverless** ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Sid": "StreamkapRedshiftServerlessAccess", "Effect": "Allow", "Action": [ "redshift-serverless:GetCredentials", "redshift-serverless:GetWorkgroup", "redshift-serverless:GetNamespace" ], "Resource": [ "arn:aws:redshift-serverless:::workgroup/*" ] } ] } ``` BYOC deployments require a broader set of permissions for provisioning and managing infrastructure. Refer to the [AWS BYOC](/aws-kafka-byoc) page for the complete `StreamkapProvisionAccess` and `StreamkapDeprovisionAccess` policy documents. BYOC IAM roles use a different trust policy and permission set compared to connector-level roles. Do not combine BYOC provisioning permissions with connector access permissions in the same role. ## Security Best Practices Follow these guidelines to keep your cross-account IAM configuration secure: * **Always use an external ID condition.** The external ID in the trust policy prevents [confused deputy](https://docs.aws.amazon.com/IAM/latest/UserGuide/confused-deputy.html) attacks, where another AWS customer could trick Streamkap into accessing your resources. * **Scope permissions to specific resources.** Avoid using `"Resource": "*"` in permission policies. Always specify resource-level ARNs (bucket names, database names, cluster identifiers) to limit what the role can access. * **Use separate roles for separate concerns.** If you use multiple Streamkap connectors (e.g., S3 and Glue), consider whether a single role with combined permissions or separate roles per connector better fits your security posture. * **Enable CloudTrail logging.** AWS CloudTrail records every API call made by the assumed role. Enable CloudTrail in your account to maintain a full audit trail of Streamkap's actions. * **Review permissions periodically.** As your Streamkap usage evolves, review the attached permission policies to ensure they still follow the principle of least privilege. * **Do not attach AWS managed policies with broad access** (e.g., `AmazonS3FullAccess`). Use the minimal custom policies provided in this guide. ## Troubleshooting This typically means the permission policy is missing required actions or the resource ARNs are incorrect. 1. Verify the **bucket name** or **Glue database name** in the policy matches your actual resource. 2. Confirm both the bucket ARN (`arn:aws:s3:::bucket-name`) and the object ARN (`arn:aws:s3:::bucket-name/*`) are included. 3. Check that all required actions are listed. For example, S3 writes require both `s3:PutObject` and `s3:AbortMultipartUpload`. 4. In the AWS Console, use **IAM Policy Simulator** or check **CloudTrail** logs to identify the exact denied action. Streamkap cannot assume the role if the trust policy is misconfigured. 1. Verify the **Principal** in the trust policy uses the correct Streamkap account ID (`300973880807`). 2. Check that the **external ID** matches the value provided by Streamkap during connector setup. 3. Ensure there are no typos in the trust policy JSON. Note: the double colon in `arn:aws:iam::300973880807:root` is correct — IAM is a global service, so the region field is empty. 4. If using a role-based principal (e.g., for Glue), confirm the full role ARN is correct: `arn:aws:iam::300973880807:role/kafkaConnectTenantAccessRole`. If Streamkap can perform some operations but not others, the permission policy is likely incomplete. 1. Compare your policy against the templates in the [Permission Policies by Resource](#permission-policies-by-resource) section. 2. Check for missing actions. For example, Glue catalog operations require both table-level and partition-level actions. 3. Verify the `Resource` ARN patterns cover all required resources. A policy scoped to a specific table will fail when the connector tries to create a new table. 4. Review CloudTrail for `AccessDenied` events to identify the specific missing action and resource. Temporary credentials obtained through role assumption have a maximum session duration. 1. Check the **Maximum session duration** setting on your IAM role (default is 1 hour, configurable up to 12 hours). Streamkap automatically refreshes credentials, but very short durations may cause issues. 2. Verify the IAM role has not been modified or had policies detached since initial setup. 3. Check if AWS Service Control Policies (SCPs) in your organization have changed and are now blocking the required actions. **BYOC-specific IAM requirements:** BYOC deployments have additional IAM requirements for infrastructure provisioning. Refer to the [AWS BYOC](/aws-kafka-byoc) page for the complete setup instructions, including CloudFormation templates. **Session tags and permission boundaries:** Streamkap does not currently use IAM session tags or permission boundaries during role assumption. If your organization enforces permission boundaries, ensure the boundary policy permits the actions listed in the permission policies above. # AWS Glue Source: https://docs.streamkap.com/aws-glue Stream data into AWS Glue ## Prerequisites * An AWS user with sufficient privileges to create and configure IAM users and roles. ## AWS Glue Setup ### 1. Create IAM User It is recommended to create a separate IAM user and role with minimum necessary access. * Go to the [Amazon IAM Console](https://console.aws.amazon.com/iam/). * Click **Users** in the sidebar. * Click **Add users**. * Enter a **User name**: e.g., `streamkap_user` * Set the **Access type**: * ✅ Check “Access key – Programmatic access” * ❌ Uncheck “Console access” (not needed) Click **Next**. * Choose **Attach policies directly** and create a custom policy: * Click **Create policy** (opens in new tab). * Go to the JSON tab and paste this policy, ensuring you replace any `<...>` placeholders as required: ```JSON JSON theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "glue:GetDatabase", "glue:GetDatabases", "glue:CreateTable", "glue:GetTable", "glue:GetTables", "glue:UpdateTable", "glue:DeleteTable", "glue:CreatePartition", "glue:GetPartition", "glue:GetPartitions", "glue:BatchCreatePartition", "glue:UpdatePartition", "glue:DeletePartition", "glue:BatchDeletePartition" ], "Resource": [ "arn:aws:glue:::catalog", "arn:aws:glue:::database/", "arn:aws:glue:::table/*/*" ] }, { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::", "arn:aws:s3:::/*" ] } ] } ``` For the Connector to access AWS Glue, it needs to be able to **assume** the user's role. Please grant the following Trust policy: ```JSON JSON theme={null} { "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::300973880807:root" }, "Action": "sts:AssumeRole" } ] } ``` ## Streamkap Setup ### 1. Create the Destination * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Destinations). * Choose **Iceberg**. * **Name:** Enter a name for your connector. ### 2. Connection Settings * **Catalog Type:** The type of Iceberg catalog. * **`GLUE`**: * **AWS IAM Role:** AWS IAM role (e.g., `arn:aws:iam:::role/`) * **Region:** The AWS region to be used. * **S3 Bucket Path:** Path to the storage location for the Iceberg tables. * **Schema:** An Iceberg table name prefix—equivalent to a database schema (e.g., public, sales, analytics). ### 3. Ingestion Settings * **Ingestion Mode**: Specifies the strategy used to insert events into the Iceberg tables. **Changing ingestion mode** `append` and `upsert` modes use different, incompatible methods for loading data into the Iceberg tables. If - for whatever reason - you want to change modes for an existing Iceberg Connector, please create a **new** Iceberg Destination instead i.e. a separate destination for `insert`, and for `upsert`. * **`upsert` mode**: * **Primary key fields:** Optional. A comma-separated list of field names to use as record identifiers when a primary key's not present. Click **Save**. # AWS BYOC Source: https://docs.streamkap.com/aws-kafka-byoc Configure the IAM role and permissions Streamkap needs to provision a BYOC data plane, an EKS cluster and VPC running Kafka and Flink, in your AWS account. Please read through [Bring Your Own Cloud (BYOC)](/bring-your-own-cloud-byoc) to understand BYOC in more detail. # Deployment Options * Turnkey / default - creates a VPN and cluster for a fully managed install * BYO-VPN - requires the customer to provide a VPN # Overview This document guides you through giving Streamkap access to provision software to your AWS account by creating an IAM role that trusted Streamkap IAM principal. Access is limited to the permissions defined in the following IAM policies: ```json StreamkapProvisionAccess.json theme={null} { "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Resource": "*", "Action": [ "ec2:DescribeAddressesAttribute", "ec2:CreateNetworkAclEntry", "ecr:UntagResource", "eks:ListAccessEntries", "eks:CreateAccessEntry", "eks:DescribeAccessEntry", "eks:UpdateAccessEntry", "eks:AssociateAccessPolicy", "eks:DisassociateAccessPolicy", "eks:CreateAddon", "eks:DescribeAddon", "eks:UpdateAddon", "eks:DeleteAddon", "eks:DescribeAddonConfiguration", "eks:DescribeAddonVersions", "eks:ListAddons", "eks:ListAssociatedAccessPolicies", "eks:CreateCluster", "eks:UpdateClusterVersion", "eks:DescribeCluster", "eks:CreateNodegroup", "eks:DescribeNodegroup", "eks:UpdateNodegroupVersion", "eks:TagResource", "eks:UntagResource", "eks:ListTagsForResource", "eks:DescribeUpdate", "eks:UpdateNodegroupConfig", "eks:CreatePodIdentityAssociation", "eks:Describe*", "ec2:CreateNetworkInterface", "ec2:Describe*", "iam:UntagPolicy", "iam:UntagRole", "kms:UntagResource", "logs:UntagResource", "logs:ListTagsForResource", "ec2:AllocateAddress", "ec2:AssociateRouteTable", "ec2:AttachInternetGateway", "ec2:AuthorizeSecurityGroupEgress", "ec2:AuthorizeSecurityGroupIngress", "ec2:CreateInternetGateway", "ec2:CreateLaunchTemplate", "ec2:CreateLaunchTemplateVersion", "ec2:CreateNatGateway", "ec2:CreateRoute", "ec2:CreateRouteTable", "ec2:CreateSecurityGroup", "ec2:CreateSubnet", "ec2:CreateTags", "ec2:CreateVpc", "ec2:DescribeAddresses", "ec2:DescribeAvailabilityZones", "ec2:DescribeInternetGateways", "ec2:DescribeLaunchTemplateVersions", "ec2:DescribeLaunchTemplates", "ec2:DescribeNatGateways", "ec2:DescribeNetworkAcls", "ec2:DescribeRouteTables", "ec2:DescribeSecurityGroupReferences", "ec2:DescribeSecurityGroupRules", "ec2:DescribeSecurityGroups", "ec2:DescribeSubnets", "ec2:DescribeTags", "ec2:DescribeVpcAttribute", "ec2:DescribeVpcClassicLink", "ec2:DescribeVpcClassicLinkDnsSupport", "ec2:DescribeVpcs", "ec2:ModifyLaunchTemplate", "ec2:ModifySubnetAttribute", "ec2:ModifyVpcAttribute", "ec2:RevokeSecurityGroupEgress", "ec2:RunInstances", "ec2:ModifyNetworkInterfaceAttribute", "ec2:ModifyInstanceAttribute", "ec2:TerminateInstances", "ec2:GetConsoleOutput", "ec2:DescribeInstances", "ec2:AttachNetworkInterface", "ec2:AssociateAddress", "ecr:CreateRepository", "ecr:DescribeRepositories", "ecr:ListTagsForResource", "ecr:TagResource", "iam:AttachRolePolicy", "iam:CreateOpenIDConnectProvider", "iam:CreatePolicy", "iam:CreatePolicyVersion", "iam:CreateRole", "iam:CreateServiceLinkedRole", "iam:GetOpenIDConnectProvider", "iam:GetPolicy", "iam:GetPolicyVersion", "iam:GetRole", "iam:GetRolePolicy", "iam:ListAttachedRolePolicies", "iam:ListRolePolicies", "iam:ListPolicyVersions", "iam:PassRole", "iam:PutRolePolicy", "iam:TagOpenIDConnectProvider", "iam:TagPolicy", "iam:TagRole", "iam:UpdateAssumeRolePolicy", "iam:RemoveRoleFromInstanceProfile", "iam:CreateInstanceProfile", "iam:AddRoleToInstanceProfile", "iam:UpdateRole", "iam:DetachRolePolicy", "iam:ListInstanceProfilesForRole", "kms:CreateAlias", "kms:CreateGrant", "kms:CreateKey", "kms:DescribeKey", "kms:GetKeyPolicy", "kms:GetKeyRotationStatus", "kms:ListAliases", "kms:ListResourceTags", "kms:PutKeyPolicy", "kms:TagResource", "kms:UpdateAlias", "kms:EnableKeyRotation", "logs:CreateLogGroup", "logs:DescribeLogGroups", "logs:ListTagsLogGroup", "logs:PutRetentionPolicy", "logs:TagLogGroup", "logs:TagResource", "logs:UntagResource", "acm:*", "elasticloadbalancing:*", "cloudformation:Describe*", "cloudformation:EstimateTemplateCost", "cloudformation:Get*", "cloudformation:List*", "cloudformation:ValidateTemplate", "cloudformation:Detect*", "route53:ChangeResourceRecordSets", "route53:ChangeTagsForResource", "route53:CreateHostedZone", "route53:GetChange", "route53:GetHostedZone", "route53:ListResourceRecordSets", "route53:ListTagsForResource", "s3:GetObject", "s3:ListBucket", "s3:PutObject", "s3:DeleteObject", "s3:CreateBucket", "s3:PutBucketPolicy", "s3:GetBucketPolicy", "s3:PutBucketTagging", "s3:GetBucketTagging", "s3:GetBucketAcl", "s3:PutBucketAcl", "s3:PutBucketOwnershipControls", "s3:GetBucketOwnershipControls", "s3:PutBucketVersioning", "s3:GetBucketVersioning", "s3:AbortMultipartUpload", "s3:GetBucketLocation", "s3:GetBucketCors", "s3:GetBucketWebsite", "s3:GetAccelerateConfiguration", "s3:GetBucketLogging", "s3:GetBucketObjectLockConfiguration", "s3:GetBucketRequestPayment", "s3:GetEncryptionConfiguration", "s3:GetLifecycleConfiguration", "s3:GetReplicationConfiguration", "s3:PutLifecycleConfiguration", "s3:PutBucketPublicAccessBlock", "s3:GetBucketPublicAccessBlock", "s3:PutEncryptionConfiguration", "eks:ListNodegroups", "eks:ListInsights", "eks:ListPodIdentityAssociations", "eks:UpdatePodIdentityAssociation", "ec2:ModifyVolume", "sqs:CreateQueue", "sqs:SetQueueAttributes", "sqs:GetQueueAttributes", "sqs:GetQueueUrl", "sqs:TagQueue", "sqs:ListQueueTags", "sqs:ListQueues", "events:PutRule", "events:PutTargets", "events:RemoveTargets", "events:DescribeRule", "events:ListTargetsByRule", "events:ListRules", "events:ListTagsForResource", "events:TagResource", "iam:GetInstanceProfile", "iam:TagInstanceProfile", "iam:ListInstanceProfiles" ] } ] } ``` ```json StreamkapDeprovisionAccess.json theme={null} { "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Resource": "*", "Action": [ "ec2:DeleteNetworkAclEntry", "eks:DeleteAddon", "eks:DeleteCluster", "eks:DescribeCluster", "eks:DeleteNodegroup", "eks:DescribeNodegroup", "ec2:DeleteInternetGateway", "ec2:DeleteLaunchTemplate", "ec2:DeleteLaunchTemplateVersions", "ec2:DeleteNatGateway", "ec2:DeleteNetworkInterface", "ec2:DeleteRoute", "ec2:DeleteRouteTable", "ec2:DeleteSecurityGroup", "ec2:DeleteSubnet", "ec2:DeleteTags", "ec2:DeleteVpc", "ec2:DescribeAddresses", "ec2:DescribeInternetGateways", "ec2:DescribeLaunchTemplateVersions", "ec2:DescribeLaunchTemplates", "ec2:DescribeNatGateways", "ec2:DescribeNetworkAcls", "ec2:DescribeNetworkInterfaces", "ec2:DescribeRouteTables", "ec2:DescribeSecurityGroupRules", "ec2:DescribeSecurityGroups", "ec2:DescribeSubnets", "ec2:DescribeTags", "ec2:DescribeVpcAttribute", "ec2:DescribeVpcClassicLink", "ec2:DescribeVpcClassicLinkDnsSupport", "ec2:DescribeVpcs", "ec2:DetachInternetGateway", "ec2:DetachNetworkInterface", "ec2:DisassociateAddress", "ec2:DisassociateRouteTable", "ec2:ReleaseAddress", "ec2:RevokeSecurityGroupIngress", "ec2:ModifyNetworkInterfaceAttribute", "ec2:ModifyInstanceAttribute", "ec2:TerminateInstances", "ec2:GetConsoleOutput", "ec2:DescribeInstances", "ec2:AttachNetworkInterface", "ec2:AssociateAddress", "ecr:DeleteRepository", "ecr:DescribeRepositories", "ecr:ListTagsForResource", "iam:DeleteOpenIDConnectProvider", "iam:DeletePolicy", "iam:DeletePolicyVersion", "iam:DeleteRole", "iam:DeleteRolePolicy", "iam:DetachRolePolicy", "iam:GetOpenIDConnectProvider", "iam:GetPolicy", "iam:GetPolicyVersion", "iam:GetRole", "iam:GetRolePolicy", "iam:ListAttachedRolePolicies", "iam:ListInstanceProfilesForRole", "iam:ListPolicyVersions", "iam:ListRolePolicies", "kms:DeleteAlias", "kms:DescribeKey", "kms:GetKeyPolicy", "kms:GetKeyRotationStatus", "kms:ListAliases", "kms:ListResourceTags", "kms:RetireGrant", "kms:ScheduleKeyDeletion", "logs:DeleteLogGroup", "logs:DescribeLogGroups", "logs:ListTagsLogGroup", "route53:ChangeTagsForResource", "route53:DeleteHostedZone", "route53:GetDNSSEC", "route53:GetHostedZone", "route53:ListResourceRecordSets", "route53:ListTagsForResource", "s3:GetObject", "s3:ListBucket", "s3:DeleteBucket", "eks:DeletePodIdentityAssociation", "eks:DeleteAccessEntry", "iam:DeleteInstanceProfile", "sqs:DeleteQueue", "events:DeleteRule", "events:RemoveTargets" ] } ] } ``` Please note that this will happen in a sub-account/subnet that is connected to your main hub. Here are the steps: 1. Create an IAM policy granting permission to provision an install. 2. Create an IAM policy granting permision to deprovision an install. 3. Create an IAM role that uses those policies, and grants access to the vendor via a trust policy. 4. Share the IAM role’s ARN with Streamkap. Once the role is created and acknowledged by Streamkap team, we will handle the deployment. In the final result, you should see the BYOC deployment is added as a new [project](/projects) in your Streamkap account. # Using AWS Console ## 1. Create the Provision Policy * In the AWS console, navigate to the **IAM** control panel. * Navigate to “**Policies**”. * Click the orange “**Create Policy**” policy button * Look for the “Policy editor”, where “visual” will be selected. Select “JSON”. * Replace the **entire policy contents** JSON with the contents of the `StreamkapProvisionAccess.json` mentioned above * Below the editor, click the orange “**Next**” button. * Name the policy `StreamkapProvisionAccess`, and click “Create policy” at the bottom. ## 2. Create the Deprovision Policy * In the AWS console, navigate to the **IAM** control panel. Navigate to “**Policies**”. Click the orange “**Create Policy**” policy button Look for the “Policy editor” component group, where “visual” will be selected. Select “JSON”. Replace the **entire policy contents** JSON with the IAM policy `StreamkapDeprovisionAccess.json`as mentioned above * Below the editor, click the orange “**Next**” button. * Name the policy `StreamkapDeprovisionAccess`, and click “Create policy” at the bottom. ## 3. Create the Access Role * Return to the IAM dashboard and navigate to “**Roles**” * Click the orange “**Create role**” button. * Under “trusted entity type”, select “**Custom trust policy**” * This will reveal a JSON editor field with the heading “**Custom trust policy”**. * Replace the **entire trust policy** contents with the following. ```JSON JSON theme={null} { "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::300973880807:root" }, "Action": "sts:AssumeRole" } ] } ``` * Click “Next” at the bottom of the page. * Search for the `StreamkapProvisionAccess` and `StreamkapDeprovisionAccess` policies and check the boxes. * Click “**Next**” * Name the role `StreamkapInstallAccess` (or another name of your choice). The page should look like this: * Click “**Create role**” * On the role page, locate the **ARN** field and make note of the value. It should take the form `arn:aws:iam::{some number}:role/StreamkapInstallAccess` . * Copy the **role ARN** from earlier and sent it back to us. # Using Cloudformation ```yaml CreateStreamkapRole.yaml theme={null} AWSTemplateFormatVersion: "2010-09-09" Resources: StreamkapProvisionAccessPolicy: Type: "AWS::IAM::ManagedPolicy" Properties: ManagedPolicyName: "StreamkapProvisionAccess" PolicyDocument: Version: "2012-10-17" Statement: - Sid: "" Effect: "Allow" Action: - "ec2:DescribeAddressesAttribute" - "ec2:CreateNetworkAclEntry" - "ecr:UntagResource" - "eks:ListAccessEntries" - "eks:CreateAccessEntry" - "eks:DescribeAccessEntry" - "eks:UpdateAccessEntry" - "eks:AssociateAccessPolicy" - "eks:DisassociateAccessPolicy" - "eks:CreateAddon" - "eks:DescribeAddon" - "eks:UpdateAddon" - "eks:DeleteAddon" - "eks:DescribeAddonConfiguration" - "eks:DescribeAddonVersions" - "eks:ListAddons" - "eks:ListAssociatedAccessPolicies" - "eks:CreateCluster" - "eks:UpdateClusterVersion" - "eks:DescribeCluster" - "eks:CreateNodegroup" - "eks:DescribeNodegroup" - "eks:UpdateNodegroupVersion" - "eks:TagResource" - "eks:UntagResource" - "eks:ListTagsForResource" - "eks:DescribeUpdate" - "eks:UpdateNodegroupConfig" - "eks:CreatePodIdentityAssociation" - "eks:Describe*" - "ec2:CreateNetworkInterface" - "ec2:Describe*" - "iam:UntagPolicy" - "iam:UntagRole" - "kms:UntagResource" - "logs:UntagResource" - "logs:ListTagsForResource" - "ec2:AllocateAddress" - "ec2:AssociateRouteTable" - "ec2:AttachInternetGateway" - "ec2:AuthorizeSecurityGroupEgress" - "ec2:AuthorizeSecurityGroupIngress" - "ec2:CreateInternetGateway" - "ec2:CreateLaunchTemplate" - "ec2:CreateLaunchTemplateVersion" - "ec2:CreateNatGateway" - "ec2:CreateRoute" - "ec2:CreateRouteTable" - "ec2:CreateSecurityGroup" - "ec2:CreateSubnet" - "ec2:CreateTags" - "ec2:CreateVpc" - "ec2:DescribeAddresses" - "ec2:DescribeAvailabilityZones" - "ec2:DescribeInternetGateways" - "ec2:DescribeLaunchTemplateVersions" - "ec2:DescribeLaunchTemplates" - "ec2:DescribeNatGateways" - "ec2:DescribeNetworkAcls" - "ec2:DescribeRouteTables" - "ec2:DescribeSecurityGroupReferences" - "ec2:DescribeSecurityGroupRules" - "ec2:DescribeSecurityGroups" - "ec2:DescribeSubnets" - "ec2:DescribeTags" - "ec2:DescribeVpcAttribute" - "ec2:DescribeVpcClassicLink" - "ec2:DescribeVpcClassicLinkDnsSupport" - "ec2:DescribeVpcs" - "ec2:ModifyLaunchTemplate" - "ec2:ModifySubnetAttribute" - "ec2:ModifyVpcAttribute" - "ec2:RevokeSecurityGroupEgress" - "ec2:RunInstances" - "ec2:ModifyNetworkInterfaceAttribute" - "ec2:ModifyInstanceAttribute" - "ec2:TerminateInstances" - "ec2:GetConsoleOutput" - "ec2:DescribeInstances" - "ec2:AttachNetworkInterface" - "ec2:AssociateAddress" - "ecr:CreateRepository" - "ecr:DescribeRepositories" - "ecr:ListTagsForResource" - "ecr:TagResource" - "iam:AttachRolePolicy" - "iam:CreateOpenIDConnectProvider" - "iam:CreatePolicy" - "iam:CreatePolicyVersion" - "iam:CreateRole" - "iam:CreateServiceLinkedRole" - "iam:GetOpenIDConnectProvider" - "iam:GetPolicy" - "iam:GetPolicyVersion" - "iam:GetRole" - "iam:GetRolePolicy" - "iam:ListAttachedRolePolicies" - "iam:ListRolePolicies" - "iam:ListPolicyVersions" - "iam:PassRole" - "iam:PutRolePolicy" - "iam:TagOpenIDConnectProvider" - "iam:TagPolicy" - "iam:TagRole" - "iam:UpdateAssumeRolePolicy" - "iam:RemoveRoleFromInstanceProfile" - "iam:CreateInstanceProfile" - "iam:AddRoleToInstanceProfile" - "iam:UpdateRole" - "iam:DetachRolePolicy" - "iam:ListInstanceProfilesForRole" - "kms:CreateAlias" - "kms:CreateGrant" - "kms:CreateKey" - "kms:DescribeKey" - "kms:GetKeyPolicy" - "kms:GetKeyRotationStatus" - "kms:ListAliases" - "kms:ListResourceTags" - "kms:PutKeyPolicy" - "kms:TagResource" - "kms:UpdateAlias" - "kms:EnableKeyRotation" - "logs:CreateLogGroup" - "logs:DescribeLogGroups" - "logs:ListTagsLogGroup" - "logs:PutRetentionPolicy" - "logs:TagLogGroup" - "logs:TagResource" - "logs:UntagResource" - "acm:*" - "elasticloadbalancing:*" - "cloudformation:Describe*" - "cloudformation:EstimateTemplateCost" - "cloudformation:Get*" - "cloudformation:List*" - "cloudformation:ValidateTemplate" - "cloudformation:Detect*" - "route53:ChangeResourceRecordSets" - "route53:ChangeTagsForResource" - "route53:CreateHostedZone" - "route53:GetChange" - "route53:GetHostedZone" - "route53:ListResourceRecordSets" - "route53:ListTagsForResource" - "s3:GetObject" - "s3:ListBucket" - "s3:PutObject" - "s3:DeleteObject" - "s3:CreateBucket" - "s3:PutBucketPolicy" - "s3:GetBucketPolicy" - "s3:PutBucketTagging" - "s3:GetBucketTagging" - "s3:GetBucketAcl" - "s3:PutBucketAcl" - "s3:PutBucketOwnershipControls" - "s3:GetBucketOwnershipControls" - "s3:PutBucketVersioning" - "s3:GetBucketVersioning" - "s3:AbortMultipartUpload" - "s3:GetBucketLocation" - "s3:GetBucketCors" - "s3:GetBucketWebsite" - "s3:GetAccelerateConfiguration" - "s3:GetBucketLogging" - "s3:GetBucketObjectLockConfiguration" - "s3:GetBucketRequestPayment" - "s3:GetEncryptionConfiguration" - "s3:GetLifecycleConfiguration" - "s3:GetReplicationConfiguration" - "s3:PutLifecycleConfiguration" - "s3:PutBucketPublicAccessBlock" - "s3:GetBucketPublicAccessBlock" - "s3:PutEncryptionConfiguration" - "eks:ListNodegroups" - "eks:ListInsights" - "eks:ListPodIdentityAssociations" - "eks:UpdatePodIdentityAssociation" - "ec2:ModifyVolume" - "sqs:CreateQueue" - "sqs:SetQueueAttributes" - "sqs:GetQueueAttributes" - "sqs:GetQueueUrl" - "sqs:TagQueue" - "sqs:ListQueueTags" - "sqs:ListQueues" - "events:PutRule" - "events:PutTargets" - "events:RemoveTargets" - "events:DescribeRule" - "events:ListTargetsByRule" - "events:ListRules" - "events:ListTagsForResource" - "events:TagResource" - "iam:GetInstanceProfile" - "iam:TagInstanceProfile" - "iam:ListInstanceProfiles" Resource: "*" StreamkapDeprovisionAccessPolicy: Type: "AWS::IAM::ManagedPolicy" Properties: ManagedPolicyName: "StreamkapDeprovisionAccess" PolicyDocument: Version: "2012-10-17" Statement: - Sid: "" Effect: "Allow" Action: - "ec2:DeleteNetworkAclEntry" - "eks:DeleteAddon" - "eks:DeleteCluster" - "eks:DescribeCluster" - "eks:DeleteNodegroup" - "eks:DescribeNodegroup" - "ec2:DeleteInternetGateway" - "ec2:DeleteLaunchTemplate" - "ec2:DeleteLaunchTemplateVersions" - "ec2:DeleteNatGateway" - "ec2:DeleteNetworkInterface" - "ec2:DeleteRoute" - "ec2:DeleteRouteTable" - "ec2:DeleteSecurityGroup" - "ec2:DeleteSubnet" - "ec2:DeleteTags" - "ec2:DeleteVpc" - "ec2:DescribeAddresses" - "ec2:DescribeInternetGateways" - "ec2:DescribeLaunchTemplateVersions" - "ec2:DescribeLaunchTemplates" - "ec2:DescribeNatGateways" - "ec2:DescribeNetworkAcls" - "ec2:DescribeNetworkInterfaces" - "ec2:DescribeRouteTables" - "ec2:DescribeSecurityGroupRules" - "ec2:DescribeSecurityGroups" - "ec2:DescribeSubnets" - "ec2:DescribeTags" - "ec2:DescribeVpcAttribute" - "ec2:DescribeVpcClassicLink" - "ec2:DescribeVpcClassicLinkDnsSupport" - "ec2:DescribeVpcs" - "ec2:DetachInternetGateway" - "ec2:DetachNetworkInterface" - "ec2:DisassociateAddress" - "ec2:DisassociateRouteTable" - "ec2:ReleaseAddress" - "ec2:RevokeSecurityGroupIngress" - "ec2:ModifyNetworkInterfaceAttribute" - "ec2:ModifyInstanceAttribute" - "ec2:TerminateInstances" - "ec2:GetConsoleOutput" - "ec2:DescribeInstances" - "ec2:AttachNetworkInterface" - "ec2:AssociateAddress" - "ecr:DeleteRepository" - "ecr:DescribeRepositories" - "ecr:ListTagsForResource" - "iam:DeleteOpenIDConnectProvider" - "iam:DeletePolicy" - "iam:DeletePolicyVersion" - "iam:DeleteRole" - "iam:DeleteRolePolicy" - "iam:DetachRolePolicy" - "iam:GetOpenIDConnectProvider" - "iam:GetPolicy" - "iam:GetPolicyVersion" - "iam:GetRole" - "iam:GetRolePolicy" - "iam:ListAttachedRolePolicies" - "iam:ListInstanceProfilesForRole" - "iam:ListPolicyVersions" - "iam:ListRolePolicies" - "kms:DeleteAlias" - "kms:DescribeKey" - "kms:GetKeyPolicy" - "kms:GetKeyRotationStatus" - "kms:ListAliases" - "kms:ListResourceTags" - "kms:RetireGrant" - "kms:ScheduleKeyDeletion" - "logs:DeleteLogGroup" - "logs:DescribeLogGroups" - "logs:ListTagsLogGroup" - "route53:ChangeTagsForResource" - "route53:DeleteHostedZone" - "route53:GetDNSSEC" - "route53:GetHostedZone" - "route53:ListResourceRecordSets" - "route53:ListTagsForResource" - "s3:GetObject" - "s3:ListBucket" - "s3:DeleteBucket" - "eks:DeletePodIdentityAssociation" - "eks:DeleteAccessEntry" - "iam:DeleteInstanceProfile" - "sqs:DeleteQueue" - "events:DeleteRule" - "events:RemoveTargets" Resource: "*" StreamkapInstallAccessRole: Type: "AWS::IAM::Role" Properties: RoleName: "StreamkapInstallAccess" AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Sid: "" Effect: "Allow" Principal: AWS: "arn:aws:iam::300973880807:root" Action: "sts:AssumeRole" ManagedPolicyArns: - !Ref StreamkapProvisionAccessPolicy - !Ref StreamkapDeprovisionAccessPolicy ``` # AWS Private Link Source: https://docs.streamkap.com/aws-private-link AWS PrivateLink allows two AWS environments to connect while routing traffic within AWS. This prevents traffic being exposed to the internet while also reducing egress costs. Learn more about" AWS PrivateLink can also be used to access on-premise databases by also utilising AWS Direct Connect. Learn more about [AWS Direct Connect](https://aws.amazon.com/directconnect/) AWS PrivateLink can be used for all sources and destinations within AWS. # Create the Endpoint Service Here is a link to AWS Private Link Docs on creating an [Endpoint Service](https://docs.aws.amazon.com/vpc/latest/privatelink/create-endpoint-service.html) 1. Create Network Load Balancer (NLB) within your VPC and configure it for each subnet/availability zone if it does not exist already 2. Create a VPC endpoint service and choose your Network Load Balancer (NLB) 3. Safelist Streamkap's AWS VPC Account ID and ARN (arn:aws:iam::300973880807:root). This will allow inbound access from our AWS PrivateLink. Here is the VPC mapping to use for your region. | AWS Region | VPC | | -------------- | --------------------- | | us-west-2 | vpc-0b96d65e5bb090974 | | us-east-1 | vpc-00b9ec8b5308e675a | | ap-southeast-2 | vpc-0cc5853e497801cc5 | | eu-west-1 | vpc-0a28eb61f7edfcaa0 | 4. Send the service name generated here to Streamkap so that we setup our AWS PrivateLink to talk to yours. 5. Update Acceptance Settings to your preference. If you disable it, Streamkap will be able to connect automatically without permission. You have already safelisted our account in earlier steps 6. Send the service name to Streamkap to generate the connection # IP & Port Forwarding Network Load Balancers (NLB) can route traffic to an EC2 instance, an IP address, or a Lambda function through target groups. AWS RDS Aurora databases give you an endpoint to access your database. This endpoint resolves to an IP address but unfortunately, this internal IP is subject to change. There are a couple of options available to work with this. ## Port Forwarder You must deploy an EC2 instance that is configured to do port forwarding (accepting requests from the NLB and forwarding those requests to the RDS database). Here is a sample script that you can use to set up the EC2 port forwarding instance: ### All Except Amazon Linux 2023 ```bash Bash theme={null} #!/bin/bash PREVLOGFILE=/root/ip.txt # Note the below section of the code is important in the event of a server restart. if test -f "$PREVLOGFILE"; then truncate -s 0 $PREVLOGFILE echo "State file $PREVLOGFILE has been emptied" fi python -m SimpleHTTPServer 801 & # NOTE: USE PORT 801 FOR PARAMETER BELOW echo 1 -> /proc/sys/net/ipv4/ip_forward export RDS_ENDPOINT=<> #NOTE: DO NOT INCLUDE THE <<>> CHARACTERS, NO QUOTATION MARKS. export RDS_PORT=<> #NOTE: DO NOT INCLUDE THE <<>> CHARACTERS, NO QUOTATION MARKS. iptables -t nat -A POSTROUTING -j MASQUERADE while true do LOGFILE=/root/ip.txt Current_IP=$(dig +short $RDS_ENDPOINT | tail -n1) #NOTE: THE "/ TAIL -n1" piece is critical to ensure only the IP address of the RDS instnce is picked. if [ $LOGFILE = "" ] ; then iptables -I INPUT -i eth1 -s $Current_IP -j ACCEPT echo $Current_IP > $LOGFILE else Old_IP=$(cat $LOGFILE) if [ "$Current_IP" = "$Old_IP" ] ; then echo "IP address has not changed ($Old_IP -> $Current_IP)" else iptables -t nat -D PREROUTING -p tcp --dport 80 -j DNAT --to-destination $Old_IP:$RDS_PORT iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination $Current_IP:$RDS_PORT sysctl net.ipv4.ip_forward=1 iptables-save echo $Current_IP > $LOGFILE echo "IP address has changed ($Old_IP -> $Current_IP)" fi fi sleep 5 done ``` ### Amazon Linux 2023 ```bash Bash theme={null} #!/bin/bash PREVLOGFILE=/root/ip.txt # Note the below section of the code is important in the event of a server restart. if test -f "$PREVLOGFILE"; then truncate -s 0 $PREVLOGFILE echo "State file $PREVLOGFILE has been emptied" fi python3 -m http.server 801 & # NOTE: USE PORT 801 FOR PARAMETER BELOW echo 1 -> /proc/sys/net/ipv4/ip_forward export RDS_ENDPOINT=<> #NOTE: DO NOT INCLUDE THE <<>> CHARACTERS, NO QUOTATION MARKS. export RDS_PORT=<> #NOTE: DO NOT INCLUDE THE <<>> CHARACTERS, NO QUOTATION MARKS. iptables -t nat -A POSTROUTING -j MASQUERADE while true do LOGFILE=/root/ip.txt Current_IP=$(dig +short $RDS_ENDPOINT | tail -n1) #NOTE: THE "/ TAIL -n1" piece is critical to ensure only the IP address of the RDS instnce is picked. if [ $LOGFILE = "" ] ; then iptables -I INPUT -i eth1 -s $Current_IP -j ACCEPT echo $Current_IP > $LOGFILE else Old_IP=$(cat $LOGFILE) if [ "$Current_IP" = "$Old_IP" ] ; then echo "IP address has not changed ($Old_IP -> $Current_IP)" else iptables -t nat -D PREROUTING -p tcp --dport 80 -j DNAT --to-destination $Old_IP:$RDS_PORT iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination $Current_IP:$RDS_PORT sysctl net.ipv4.ip_forward=1 iptables-save echo $Current_IP > $LOGFILE echo "IP address has changed ($Old_IP -> $Current_IP)" fi fi sleep 5 done content_copy ``` Once you’ve finished setting up the port forwarding instance, configure the NLB listener and target group to route traffic to the portforwarder EC2 instance. ## Lambda Function It's possible to deploy a lambda function check the current IP address and update the NLB target group accordingly. To use the RDS IP address in your NLB target group, do the following: 1. Run nslookup with the domain name of RDS endpoint as the input to find the IP address: * `nslookup DNS_ENDPOINT` 2. Configure your NLB target group with the IP address from above 3. Deploy a lambda function to periodically perform nslookup on the RDS endpoint to see if the IP address has changed and update the target group with the new IP address. # Azure Blob Storage Source: https://docs.streamkap.com/azure-blob-storage # Prerequisites * An Azure account granted the `Storage Blob Data Contributor` (for Shared Access Signature generation) roles, or the `Storage Account Key Operator Service Role` (for Account key access) [built-in](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles/storage) role, or a custom role with permissions to: * `Microsoft.Storage/storageAccounts/blobServices/generateUserDelegationKey/action` * `Microsoft.Storage/storageAccounts/blobServices/containers/blobs/write` * `Microsoft.Storage/storageAccounts/listkeys/action` * An Azure storage account If you have not yet created an Azure storage account, please follow Microsoft's [Create a Azure storage account](https://learn.microsoft.com/en-gb/azure/storage/common/storage-account-create?tabs=azure-portal#create-a-storage-account) guide # Limitations * Currently our Connector does not renew SAS tokens automatically. If and when credentials expire, you will need to generate new credentials and update your Azure Blob Storage Destination's credentials in the Streamkap app to resume Pipelines. # Azure Blob Storage Setup ## Set up a blob container (optional) You can use an existing blob container, but you may wish to create a new one to completely isolate Streamkap generated files from the rest of your files. To create a new blob container: 1. Log in to the [Azure Portal](https://portal.azure.com/#home) 2. At the top of the page, select **Storage accounts** and click on a storage account 3. In the navigation pane for the storage account, scroll to the **Data storage** section and select **Containers** 4. Within the Containers pane, click the `+ Container` button to open the New container pane 5. Provide a **Name** for your new container 6. Once you're happy with the configuration, click the `Create` button You will need the container name for Streamkap Setup later on. ## Get connection details Currently our Connector supports 2 types of Azure Storage credentials: * **Shared Access Signatures** (SAS) which provide granular control over access to the storage account and its data * **Account access keys** which are generated when Azure creates a storage account and provide full access to the account and its data ### Account signed SAS (recommended) You can use any type of SAS with our Connector, but we recommend an Account SAS as it offers longer expiration times while still providing granular control over access to the storage account and its data. 1. Log in to the [Azure Portal](https://portal.azure.com/#home) 2. At the top of the page, select **Storage accounts** and click on a storage account 3. In the navigation pane go to **Storage browser** and select **Blob containers** 1. For the blob container Streamkap should use, click the `...` button on the right and then `Generate SAS` 2. For the Signing method, choose **Account key** 3. For Permissions, select `Write` 4. For the Start and expiry date/time, set a reasonably long time period (or as long as your security policies allow) to reduce how often you have to generate new SAS tokens and update your Azure Blob Storage Destinations in the Streamkap app. Azure also recommends setting the start date to at least 15 minutes in the past to avoid clock skew issues 5. For the Allowed IP addresses, enter the [Streamkap IP Addresses](/streamkap-ip-addresses) 6. Click the `Generate SAS token and URL` button 7. For the **Blob SAS URL**, click the `Copy to clipboard` button You will need that URL to complete Streamkap Setup later on. ### Account access key You can use an Account access key, which is generated when Azure creates a storage account. However, because it provides full access to the account and its data, it's not recommended or essential for our Connector. 1. Log in to the [Azure Portal](https://portal.azure.com/#home) 2. At the top of the page, select **Storage accounts** and click on a storage account 3. In the navigation pane, under the Security + networking section go to **Access keys** 1. For either of the keys, click the `Show` button next to its connection string 2. Click the `Copy` button to copy the connection string to the clipboard You will need that connection string to complete Streamkap Setup later on. ### User delegated SAS (not recommended) **Granting User delegated SAS permissions** When generating a User delegated SAS in the Azure Portal you may see the following warning message: > You don't have permissions to grant write access. You can still create a shared access signature, but you'll need an RBAC role with additional permissions before you can grant that level of access to your signature recipient. If so, you need to ensure that *your* Azure user has been assigned to a built-in Azure role or custom role that has the equivalent permissions to those which you are granting. For granting `Write` access (required by our Connector), your Azure user would need to be assigned to `Storage Blob Data Contributor` role or a custom role with the `Microsoft.Storage/storageAccounts/blobServices/containers/blobs/write` permission You can use a User delegated SAS. However, because the maximum expiration period is 7 days and our Connector does not currently renew SAS tokens automatically, it's not recommended for our Connector. 1. Log in to the [Azure Portal](https://portal.azure.com/#home). 2. At the top of the page, select **Storage accounts** and click on a storage account. 3. In the navigation pane go to **Storage browser** and select **Blob containers**. 1. For the blob container Streamkap should use, click the `...` button on the right and then `Generate SAS`. 2. For the Signing method, choose **User delegation key**. 3. For Permissions, select `Write`. 4. For the Start and expiry date/time, set a reasonably long time period (or as long as your security policies allow) to reduce how often you have to generate new SAS tokens and update your Azure Blob Storage Destinations .in the Streamkap app. Azure also recommends setting the start date to at least 15 minutes in the past to avoid clock skew issues. 5. For the Allowed IP addresses, enter the [Streamkap IP Addresses](/streamkap-ip-addresses). 6. Click the `Generate SAS token and URL` button. 7. For the **Blob SAS URL**, click the `Copy to clipboard` button. # Streamkap Setup 1. Go to [Destinations](https://app.streamkap.com/connectors/add?tab=Destinations) and choose **Azure Blob Storage**. 2. Input the following information: 1. **Name**: A unique and memorable name for this Connector. 2. **Azure Connection String**: The connection string (for Account access keys) or Blob SAS URL (for Shared Access Signatures). 3. **Blob container name**: The name of an existing blob container to use. 4. **Format** (default: `JSON`): The format to use when writing the data to file storage. 1. **Include headers** (`CSV` format only): Include or exclude column name header row per file. 5. **Storage directory**: The top level directory for storing the data e.g. `myfolder/subfolder`. 6. **Filename template** (default: `{{topic}}--{{partition}}--{{start_offset}}`): The format of the filename. You can combine any of the elements below using other text or characters, including dashes (`-`) and underscores (`_`). | Element | Description | | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `{{topic}}` | The Streamkap topic name. For example, a PostgreSQL Source table `web.salesorders` topic’s name would be `salesorders`. | | `{{partition}}` | The partition number of the records in the file, typically `0`. Streamkap topics and their data can be partitioned for better performance in certain scenarios. For example, a topic `salesorders` has 10 partitions, `0` through to `9`. | | `{{start_offset}}` | The offset number of the first record in the file. Every record streamed has an incrementing offset number. For example, a topic `salesorders` has 1000 records, offsets `0` through to `999`. Note that in the case of a multi-partitioned topic, offset numbers are not unique across partitions. | 7. **Flush size** (default: `1000`): Number of records to write per file. 8. **File size** (default: `65536`): Minimum size (in bytes) per file. Records are held in memory until this file size is met or the **Rotate interval** is exceeded. 9. **Rotate interval** (default: `-1` disabled): Maximum time (in milliseconds) to wait before writing records held in memory to file. This ignores the flush and file size settings. 10. **Compression type** (default: `none`): The compression algorithm to use when writing each file. Depending on the **Format**, some compression algorithms may not be available. 3. Click the `Save` button. # Azure BYOC Source: https://docs.streamkap.com/azure-byoc Please read through [Bring Your Own Cloud (BYOC)](/bring-your-own-cloud-byoc) to understand BYOC in more detail. # Deployment Options * Turnkey / default - creates a VPN and cluster for a fully managed install * BYO-VPN - requires the customer to provide a VPN # Create Service Principle Installs are created and provisioned by offering a Service Principal, that streamkap can use to provision the sandbox. Contributor role can be used instead of Owner in a shared deployment. To create an Azure service principal locally: ```sql SQL theme={null} $ az login $ az account list (to get the subscription ID) $ az ad sp create-for-rbac --name --role Owner --scopes /subscriptions/ ``` # Architecture Diagram # Azure MySQL Database Source: https://docs.streamkap.com/azure-mysql-database Configure MySQL change data capture from Azure Database for MySQL to Streamkap, including server parameters, replication user grants, and heartbeat options. ## Prerequisites * MySQL version ≥ 5.7 * MySQL [binlog](https://dev.mysql.com/doc/refman/8.0/en/binary-log.html) enabled * A database user with sufficient privileges to configure the database, including enabling binary logging and creating users The following table summarizes all permissions the Streamkap user needs. The setup steps on each source connector page walk through granting each one. **Core permissions (always required):** | Permission | Scope | Purpose | | -------------------- | ------------ | -------------------------------------------------------- | | `REPLICATION CLIENT` | `*.*` | Read binlog metadata and positions | | `REPLICATION SLAVE` | `*.*` | Read binlog events for CDC streaming | | `RELOAD` | `*.*` | Flush operations required for consistent snapshots | | `SHOW DATABASES` | `*.*` | Discover available databases and schemas | | `SELECT` | `{schema}.*` | Read table data during initial and incremental snapshots | **Snapshot signal table (required if GTID is not enabled):** | Permission | Scope | Purpose | | ---------- | ---------------------------- | -------------------------- | | `SELECT` | `streamkap.streamkap_signal` | Read signal table state | | `INSERT` | `streamkap.streamkap_signal` | Trigger snapshot signals | | `UPDATE` | `streamkap.streamkap_signal` | Update signal table state | | `DELETE` | `streamkap.streamkap_signal` | Clean up processed signals | **Heartbeat table (required if heartbeats are enabled):** | Permission | Scope | Purpose | | ---------- | ------------------------------- | ------------------------------ | | `SELECT` | `streamkap.streamkap_heartbeat` | Read heartbeat state | | `INSERT` | `streamkap.streamkap_heartbeat` | Write heartbeat records | | `UPDATE` | `streamkap.streamkap_heartbeat` | Update heartbeat timestamps | | `DELETE` | `streamkap.streamkap_heartbeat` | Clean up old heartbeat records | If GTID is enabled (default on Google Cloud SQL), the signal table is not required. If using **read-only** mode with GTID enabled, the signal table permissions are not needed. The heartbeat table still requires permissions for monitoring and diagnostics. **Combined GRANT statements:** ```sql SQL theme={null} -- Core permissions (always required) GRANT REPLICATION CLIENT, RELOAD, SHOW DATABASES, REPLICATION SLAVE ON *.* TO 'streamkap_user'@'%'; GRANT SELECT ON {schema}.* TO 'streamkap_user'@'%'; -- Signal table (if GTID is not enabled) GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_signal TO 'streamkap_user'@'%'; -- Heartbeat table (if heartbeats are enabled) GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO 'streamkap_user'@'%'; ``` Azure Database for MySQL uses standard MySQL `GRANT` statements for user permissions. Binary logging and GTID are configured via Azure server parameters (see [Azure GTID documentation](https://learn.microsoft.com/en-us/azure/mysql/single-server/concepts-read-replicas#global-transaction-identifier-gtid) for details). ## MySQL Setup ### 1. Grant Database Access * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your database. ### 2. Configure Binary Logging (Read Replica) If you are connecting to a read replica, you need to enable binary logging / history mode. * Connect to your read replica with SUPER privileges. * If the `slave_parallel_workers` value is 0, you can skip this step. * If the `slave_parallel_workers` value is not 0, run the following: ```SQL SQL theme={null} SET GLOBAL slave_parallel_type = 'LOGICAL_CLOCK'; SET GLOBAL slave_preserve_commit_order = 1; ``` * Connect to your master instance with SUPER privileges: ```SQL SQL theme={null} SET GLOBAL binlog_order_commits = 1; ``` ### 3. Create Database User It's recommended to create a separate user and role for the Connector to access your MySQL database. Below is an example script that does that. ```SQL SQL theme={null} -- Replace { ... } placeholders as required -- Identify version SHOW VARIABLES LIKE 'VERSION'; -- On MySQL version 5.6 to 8.0 CREATE USER 'streamkap_user'@'%' IDENTIFIED BY '{password}'; -- On MySQL version 8.0+ CREATE USER 'streamkap_user'@'%' IDENTIFIED WITH mysql_native_password BY '{password}'; -- Grant Permissions GRANT REPLICATION CLIENT, RELOAD, SHOW DATABASES, REPLICATION SLAVE ON *.* TO 'streamkap_user'@'%'; -- Grant Select on all schemas needed GRANT SELECT ON {schema}.* TO 'streamkap_user'@'%'; ``` ### 4. Enable Snapshots To backfill your data, the Connector needs to be able to perform snapshots. See [Snapshots & Backfilling](/snapshots) for more information. To enable this feature, there are 2 methods available: #### Method 1: Enable GTID (Recommended) This method is recommended if you cannot create and grant Streamkap read/write privileges on a 'signal' table (method 2) for any reason. It's the equivalent of a 'read only' connection. GTID mode is only available in MySQL version ≥ 5.6.5 Global transaction identifiers (GTIDs) uniquely identify transactions that occur on a server within a cluster. Though not required, using GTIDs simplifies replication and enables you to more easily confirm if primary and replica servers are consistent as well as carry out incremental snapshots. Set up following these instructions. Ensure you follow the guide for your version [Read replicas in Azure Database for MySQL | Global transaction identifiers (GTID)](https://learn.microsoft.com/en-us/azure/mysql/single-server/concepts-read-replicas#global-transaction-identifier-gtid) and that `GTID` mode is `ON`. #### Method 2: Create a table in the source database If you cannot enable GTID mode, you will need to create the table and give permissions to the `streamkap_user`. The Connector will use this collection for managing snapshots. The examples below use `streamkap_signal` as the signal table name, but you can choose any name. During [Streamkap Setup](#3-snapshot-settings), provide the full path to your signal table in `database.table` format (e.g., `streamkap.streamkap_signal`). ```SQL SQL theme={null} -- Create the schema CREATE SCHEMA streamkap; CREATE TABLE streamkap_signal ( id VARCHAR(255) PRIMARY KEY, type VARCHAR(32) NOT NULL, data VARCHAR(2000) NULL ); GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_signal TO 'streamkap_user'@'%'; ``` ### 5. Heartbeats Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. For read-write connections (when **Read only** is **No** during Streamkap Setup), the Connector writes to the heartbeat table directly. ```SQL SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Switch to the streamkap schema USE streamkap; -- Create the heartbeat table with id, text, and last_update fields CREATE TABLE streamkap_heartbeat ( id INT AUTO_INCREMENT PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Grant permission to the Streamkap user GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO 'streamkap_user'@'%'; -- Insert the first row into the heartbeat table INSERT INTO streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` For read-only connections (when **Read only** is **Yes** during Streamkap Setup), the Connector cannot write to the heartbeat table directly. Instead, you must configure a scheduled job on the **primary** database to generate artificial traffic. These changes will replicate to the read replica, which the Connector then consumes. **Run these commands on the primary database, not the read replica.** The heartbeat table and scheduled event must be created on the primary database. The changes will automatically replicate to the read replica. The MySQL Event Scheduler flag must be enabled on your database (`event_scheduler=ON`). See your provider's documentation: Modifying parameters in a DB parameter group in Amazon RDS Aurora MySQL configuration parameters reference Configuring database flags Server parameters Check if the event scheduler is enabled: ```sql SQL theme={null} SHOW VARIABLES WHERE VARIABLE_NAME = 'event_scheduler'; ``` **Making this setting persistent** For self-hosted MySQL, add `event_scheduler = ON` to your MySQL configuration file (usually `my.cnf` or `my.ini`) in the `[mysqld]` section. For managed databases (RDS, Cloud SQL, Azure), configure the `event_scheduler` parameter through your provider's console or CLI. ```sql SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Create the heartbeat table CREATE TABLE streamkap.streamkap_heartbeat ( id INT AUTO_INCREMENT PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Insert the initial row INSERT INTO streamkap.streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` ```sql SQL theme={null} CREATE EVENT streamkap.streamkap_heartbeat_event ON SCHEDULE EVERY 1 MINUTE DO UPDATE streamkap.streamkap_heartbeat SET text = 'updated_heartbeat', last_update = CURRENT_TIMESTAMP WHERE id = 1; ``` Whichever database user is used to create and run the event scheduler (often the MySQL root user or a dedicated event scheduler user) needs appropriate permissions on the heartbeat table. Additionally, the Streamkap user also needs permissions to monitor the heartbeat table. ```sql SQL theme={null} GRANT EVENT ON streamkap.* TO {event scheduler user}; GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO {event scheduler user}; -- Grant permissions to the Streamkap user for monitoring and diagnostics GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO 'streamkap_user'@'%'; ``` ```sql SQL theme={null} -- View all scheduled events in the streamkap schema SHOW EVENTS IN streamkap; -- View event details SELECT * FROM information_schema.EVENTS WHERE EVENT_SCHEMA = 'streamkap'; -- Disable an event temporarily ALTER EVENT streamkap.streamkap_heartbeat_event DISABLE; -- Enable an event ALTER EVENT streamkap.streamkap_heartbeat_event ENABLE; -- Drop an event DROP EVENT IF EXISTS streamkap.streamkap_heartbeat_event; ``` *** ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **MySQL**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Hostname**: Specify the hostname. * **Port**: Default is `3306`. * **Connect via SSH Tunnel**: The Connector will connect to an SSH server in your network which has access to your database. This is necessary if the Connector cannot connect directly to your database. * See [SSH Tunnel](/ssh-tunnel) for setup instructions. * **Username**: Username to access the database. By default, Streamkap scripts use `streamkap_user`. * **Password**: Password to access the database. * **Read only**: Whether or not to use a read-only connection. Requires GTID to be enabled on the source database. See [Enable GTID](#method-1%3A-enable-gtid-recommended) for more information. * **Heartbeats**: Enabled by default. * For **read-write** connections, configure a heartbeat table in the source database and set **Heartbeat Table Database**. See [Heartbeats](#5-heartbeats) for setup instructions. * For **read-only** connections, configure a scheduled heartbeat event on the primary database using the MySQL Event Scheduler, and include the heartbeat table in [Schema and Table Capture](#5-schema-and-table-capture). See [Heartbeats](#5-heartbeats) for setup instructions. * **Connection Timezone**: The timezone of your database. **Timezone conversion** MySQL converts `TIMESTAMP` values from the current time zone to UTC for storage, and back from UTC to the current time zone for retrieval. By default, the current time zone for each connection is the database server's time zone but this option allows you to override that. As long as the time zones remain the same, you get back the same value you store. We recommend using the default `SERVER` option which attempts to detect the session time zone from the values configured on the MySQL server session variables 'time\_zone' or 'system\_time\_zone'. It also reduces the chance of problems with daylight savings adjustment 'fall back' and 'spring forward'. If either time zones change, an [ad-hoc snapshot](/snapshots#triggering-a-snapshot) is recommended so your source and destination timestamps are consistent. ### 3. Snapshot Settings If you set **Read only** to **No**, you will need to create a snapshot signal table and give permissions to the `streamkap_user`. See [Enable Snapshots](#4-enable-snapshots) for setup instructions. * **Signal Table**: Full path to the signal table including database and table name (e.g., `streamkap.streamkap_signal`). This table is used for incremental snapshotting. See [Enable Snapshots](#4-enable-snapshots) for setup instructions. ### 4. Advanced Parameters * **Represent binary data as**: Specifies how the data for binary columns e.g. `blob`, `binary`, `varbinary` should be interpreted. Your destination for this data can impact which option you choose. Default is `bytes`. * **Capture Only Captured Databases DDL**: Specifies whether the connector records schema structures from all databases defined in the server (the default) or only those databases for which you've explicitly configured the connector. Specify `true` to capture schema history only for the specific databases you've configured. This is particularly valuable when databases are large, to reduce the volume of DDL stored in the schema history topic. It also improves startup times when the connector restarts or recovers from failures. Default is `false`. See [Schema History Optimization](/schema-history-optimization) for details. * **Capture Only Captured Tables DDL**: Specifies whether the connector records the schema structure for all tables in the configured databases (the default) or only the tables whose changes the connector captures. Specify `true` to capture schema history only for the specific tables you've configured. This is particularly valuable when tables are large, to reduce the volume of DDL statements stored in the schema history topic. It also improves startup times when the connector restarts or recovers from failures. Default is `false`. See [Schema History Optimization](/schema-history-optimization) for details. Click **Next**. ### 5. Schema and Table Capture * **Add Schemas/Tables**: Specify the schema(s) and table(s) for capture. * You can bulk upload here. The format is a simple list of schemas and tables, with each entry on a new row. Save as a `.csv` file without a header. **CDC only captures base tables, not Views** Change Data Capture reads the MySQL binary log (binlog), which only records changes to physical tables. Database Views are query-time computations with no physical storage—they don't generate binlog entries. **What you cannot capture:** Views, temporary tables, MEMORY tables, BLACKHOLE tables, or system tables (information\_schema, performance\_schema). **Solution:** Specify only the underlying base tables that feed your views. You can recreate the view logic in your destination or transformation layer. **VIRTUAL generated columns are not streamed** MySQL [VIRTUAL generated columns](https://dev.mysql.com/doc/refman/8.0/en/create-table-generated-columns.html) are computed when rows are read and are **not** written to the binary log, so live streaming cannot capture or update their values. (STORED generated columns are written to the binary log and stream normally.) Snapshots read the table directly, so they **do** capture a value for VIRTUAL columns — but because streaming cannot keep it current, that value will become stale. **We recommend excluding VIRTUAL generated columns from capture:** in the Source's **Settings → Advanced** section, set **Column Selection Mode** to **Exclusion** and add the column to the **Column Exclusion List** (fully-qualified, e.g. `database.table.column`). If you need the value at your destination, calculate it there — or with a [transform](/transforms) — from the columns it is derived from. Click **Save**. **Adding tables programmatically** -- You can add tables to an existing source via the API or Terraform using the `table.include.list.user.defined` parameter (Terraform: `table_include_list_user_defined`). See the [REST API](/api) for getting started with the API, or [Terraform Resources](/terraform-resources#sources) for Terraform source configuration. **Have questions?** See the [MySQL Source FAQ](/mysql-source-faq) for answers to common questions about MySQL sources, troubleshooting, and best practices. # Batch vs Streaming Source: https://docs.streamkap.com/batch-vs-streaming-data Traditional ETL is based on the batch loading of data. You might achieve this by either doing a nightly job where you run a single query to extract all the data from your database to refresh your data warehouse, or you poll your database on some periodic cadence (for instance, every 60 minutes) to get the new data and load that into your data warehouse. There are at least three big downsides: * Periodic spikes in load: These large queries impact the latency and ultimately the user experience, which is why a lot of companies tend to schedule jobs during low traffic periods e.g. overnight. * Network provisioning: Sending all that data puts a lot of strain on your network. And because you have big spikes in network costs and bytes that you're sending, you have to provision your network to be able to handle that. * Delayed business decisions: Business decisions based on the data are delayed by your job frequency. So if you update your data every night that means you can't query what happened yesterday until the following day. This is where streaming has a big advantage! Using change data capture to stream data from your database to your data warehouse solves the above problems because: * CDC does not require that you execute high load queries on a periodic basis, so you don't get really spiky behaviors in load. * the data is sent continuously and in much smaller batches, you don't need to provision as much network resource, and you can save money on network costs. * you're continuously streaming data from your database to your data warehouse, the data in your warehouse is up-to-date, allowing you to create real-time insights, and giving you a leg up on your competitors because you're making business decisions on fresher data. # Overview Source: https://docs.streamkap.com/bring-your-own-cloud-byoc Streamkap Kafka/Flink BYOC on AWS, GCP and Azure With the Bring Your Own Cloud option, Streamkap will host the control plane in our environment while the data plane is deployed within your virtual private cloud (VPC) on your infrastructure in a sub-account of your cloud vendor. We support AWS, Google Cloud Platform (GCP), and Azure. ## Cloud Architecture The architecture fundamentally contains a control plane and a data plane. ### Control Plane The control plane is a separate application that manages your deployment, carries out operations and monitors your services. This is hosted within Streamkap. Typical actions include * Adding, editing connectors * Monitoring * Setting up alerts * Team management * SSO ### Data Plane The data plane is where your data moves through. This is a fully managed Kubernetes deployment within a sub account/customer VPC. There are no machines, OS, patching to look after since our agent manages it all. The control plane will communicate with the data plane to set any configuration such as creating connectors as well as collect log and metric data, allowing 24/7 monitoring/support. ## How much does Streamkap manage? Streamkap will manage the entire Kubernetes environment, including deployment, monitoring, patching etc. ## How does the install happen? All that is needed is a single cloud principal that grants initial provisioning access and this setup can done via any of these methods * 1-click install template * Manually using the Cloud console This principal is only used during the initial install, and can be deleted after. It has create but no get access. Please prefer to specific cloud provider subsection for more detailed instructions. Streamkap uses this install role to do the initial install provisioning: * Create base layer sandbox — VPC, Kubernetes cluster, ECR resources * Install the runner/agent From there, the runner takes over the install and no cross account access is ever required again, until deleting the install. The runner is responsible for the following: * Capturing logs when requested + providing basic debugging capabilities * Continuous monitoring of the install * Managing components — provisioning terraform, helm, container components * Syncing and managing OCI artifacts into the account The runner will provision servers and install the data plane portion of the Streamkap application. The runner is also responsible for the delivery of upgrades. ## Setup ### Node & Software Requirements You do not need to provision these or install any software. Our installer will automatically deploy and maintain them, whether that is updating own software and the OS. A general deployment will require: * 4 nodes (2 CPU, 8GB) * 2 nodes (4 CPU, 16GB) ### Network Requirements #### Between Control Plane and Data Plane * VPN will be setup between us to secure the communication **Inbound from Streamkap Control Plane to Customer Data Plane** * Port 443 (HTTPS) * Port 9094,9095,9096 (Kafka) **Outbound from Customer Data Plane to Streamkap Control Plane** * Port 443 (HTTPS) * Port 9701 (Runner/Agent) #### Communication between the sub-account and the sources/destinations The sub-account needs to be able to communicate with all the connectors and resolve any internal hostname. The sub account will initiate the connection. ## FAQ The normal deployment here is to create a sub-account in your cloud vendor for Streamkap to use. There are two deployment options within this: 1. We create a full new install (including cluster + vpc) 2. The customer creates the VPC A Kubernetes cluster will be deployed which will contain a number of VMs.\ The CPU count for the deployment depends on the volume of data being processed but 4 CPU/12GB setup is typical.\ We mostly utilise object storage. No. Our agent/runner will maintain all systems and software in the sub-account.\ We will deploy Kubernetes and our agent will maintain, upgrade and handle entirely. All the images will be synced to the customer account and so you can enable your standard security measures in this account including image scanning. Your confidential data does not leave the data plane. Monitoring logs and some configuration is stored with Streamkap.\ This is all kept encrypted and covered under our SOC2 compliance. We install collector agents to gather metrics, logs that will be sent back to the Streamkap control plane.\ It will detect if any issues happen as well as attempt a remedy.\ Failing this, our support team will be notified. It is only metric and log related to the Kubernetes cluster and our software services. Streamkap provides disaster recovery capabilities for both Streamkap Cloud and BYOC deployments. See [Disaster Recovery](/disaster-recovery) for detailed information on recovery procedures, Recovery Point Objective (RPO) / Recovery Time Objective (RTO) targets, and failover processes. The deployment should be placed within the same region and ideally zone as your current systems. Ingress is typically free regardless of the data source. Egress depends on where you are sending the data.\ Egress is usually free in the same region/zone and only becomes more substantial if you are sending to a different cloud vendor — in which case it could cost up to \$0.10 per GB. By different cloud vendor, this refers to AWS, GCP, Azure or Oracle Cloud for example. # CLI Source: https://docs.streamkap.com/cli Manage Streamkap pipelines, sources, destinations, transforms, and topics from the terminal with the Streamkap command-line interface. The Streamkap CLI lets you manage your Change Data Capture (CDC) infrastructure from the terminal. It covers the full [Streamkap API](/api-reference/authentication/access-token) — pipelines, sources, destinations, transforms, topics, and more. The CLI is designed to work well in both interactive and agentic workflows. Output defaults to human-readable text in a terminal and switches to JSON when piped to another process or AI agent. ## Prerequisites * Node.js 20+ * An API token (Client ID and Client Secret) — see [API Tokens](/api-tokens) for how to create one Check your Node.js version: ```bash theme={null} node -v # should be v20.x or higher ``` If you need to install or update Node.js, visit [nodejs.org](https://nodejs.org) or use [nvm](https://github.com/nvm-sh/nvm). ## Installation ```bash theme={null} npm install -g @streamkap/tools ``` ## Authentication The CLI supports three authentication methods. Pick whichever fits your workflow — when more than one is set, command-line flags win, then environment variables, then a saved profile. Recommended for CI/CD and scripts: ```bash theme={null} export STREAMKAP_CLIENT_ID="your-client-id" export STREAMKAP_CLIENT_SECRET="your-client-secret" ``` Saves credentials to `~/.config/streamkap/config.json`: ```bash theme={null} streamkap auth login --client-id your-client-id --client-secret your-client-secret streamkap auth status # Check what's configured streamkap auth logout # Clear saved tokens ``` Pass credentials inline: ```bash theme={null} streamkap pipelines list --client-id your-client-id --client-secret your-client-secret ``` ### Verify Your Setup After authenticating, validate your install end-to-end: ```bash theme={null} streamkap doctor ``` `doctor` checks your credentials, API connectivity, infrastructure counts, and optionally Kafka and Schema Registry. It exits non-zero if any check fails. ## Usage ```bash theme={null} streamkap --help # List all commands streamkap pipelines list # List pipelines streamkap pipelines get # Get pipeline details streamkap sources list # List sources streamkap sources metrics # Source metrics streamkap destinations list # List destinations streamkap dashboard stats # Organisation overview streamkap doctor # Validate setup & connectivity ``` ### Output Formats By default, output is human-readable text in a terminal and JSON when piped. Override with: ```bash theme={null} streamkap pipelines list --json # Force JSON output streamkap pipelines list --format text # Force text output ``` ### Destructive Commands Commands that modify or delete resources (delete, stop, reset) require confirmation in interactive mode. Each destructive subcommand accepts `--yes` to skip the prompt and `--dry-run` to preview the call without executing it: ```bash theme={null} streamkap sources delete --dry-run # Preview what would happen streamkap sources delete --yes # Skip confirmation ``` When output is piped (non-TTY), destructive commands run **without confirmation** so they work in scripts and agent pipelines. Be careful when piping commands like `streamkap sources delete ` into other tools — they will execute immediately. ### Named Profiles Use `--profile` to switch between multiple accounts without re-exporting env vars: ```bash theme={null} streamkap auth login --profile prod --client-id --client-secret streamkap pipelines list --profile prod ``` ## Commands | Group | Description | | ----------------- | --------------------------------------------------------------------------- | | `pipelines` | Create, update, delete, monitor metrics and logs, bulk operations | | `sources` | Manage CDC connectors, deploy, pause, resume, stop, restart, snapshots | | `destinations` | Manage sinks, deploy, pause, resume, stop, restart | | `transforms` | Manage stream processors, deploy to preview/production, unit tests, clone | | `topics` | List, inspect, create Kafka topics, read sample messages | | `tags` | Organise resources with tags | | `schema-registry` | Browse subjects and schemas | | `consumer-groups` | Inspect lag, reset offsets | | `kafka-access` | Manage direct Kafka cluster users | | `kafka` | Direct Kafka produce, consume, and real-time subscribe | | `dashboard` | Organisation statistics, log search and aggregation | | `alerts` | Manage subscribers and notification preferences | | `usage` | Usage metrics and export | | `cluster-scaling` | Inspect and scale your Kafka cluster | | `admin` | List and switch services | | `auth` | Login, logout, status, token, encode-key (Base64-encode a Project Key file) | | `doctor` | Validate setup, credentials, and API connectivity | | `completions` | Generate shell completion scripts | ## Direct Kafka Access Most users don't need this on day one. The CLI's REST-based commands (like `topics messages`) let you inspect topic data without Kafka credentials. The `kafka` command group connects directly to the Kafka brokers for produce, consume, and real-time subscribe operations. These commands **do not require API credentials** — only Kafka credentials. First, create a Kafka user from the Streamkap dashboard (Kafka Access page) or via `streamkap kafka-access create`. This gives you the bootstrap servers, username, and password. See [Kafka Access](/kafka-access) for details. Then set all three Kafka environment variables before running any `kafka` command. They must be set together — partial config will fail at startup with a clear error: ```bash theme={null} export KAFKA_BOOTSTRAP_SERVERS="host1:9092,host2:9092" export KAFKA_API_KEY="your-kafka-username" export KAFKA_API_SECRET="your-kafka-password" # Optional — enables Avro / JSON Schema / Protobuf encode and decode. # Schema Registry reuses the same Kafka user credentials. export SCHEMA_REGISTRY_URL="https://sr.streamkap.net:8081" export SCHEMA_REGISTRY_USERNAME="$KAFKA_API_KEY" export SCHEMA_REGISTRY_PASSWORD="$KAFKA_API_SECRET" ``` ```bash theme={null} # Produce a single message (optionally encoded via Schema Registry) streamkap kafka produce my-topic \ --value '{"id":"123","event":"create"}' \ --key user-123 \ --schema-registry ``` ```bash theme={null} # Consume a batch of messages with a timeout streamkap kafka consume my-topic \ --max-messages 10 \ --timeout 10000 \ --decode ``` ```bash theme={null} # Subscribe in real time to one or more topics (Ctrl+C to exit) streamkap kafka subscribe topic1,topic2 --timeout 30000 --decode # Subscribe by regex pattern streamkap kafka subscribe --pattern 'source_.*' --decode ``` ## Global Options | Flag | Description | | -------------------------- | ------------------------------------------------------------------------ | | `-j, --json` | Force JSON output | | `-f, --format ` | Output format: `json`, `text`, or `auto` (default: `auto`) | | `-p, --profile ` | Named credential profile | | `--api-url ` | Override API URL | | `--client-id ` | Override client ID | | `--client-secret ` | Override client secret (prefer env vars to avoid shell history exposure) | | `-v, --verbose` | Show request method, path, and timing on stderr | | `-q, --quiet` | Suppress all non-data output | | `-y, --yes` | Skip confirmation for destructive commands | | `--no-color` | Disable ANSI colors | | `-V, --version` | Show CLI version | ## Exit Codes The CLI returns typed exit codes so scripts and agents can branch on the failure mode without parsing error messages. When a command fails, the JSON error output also includes `code` and `exitCode` fields matching the table below. | Code | Name | Meaning | | ---- | ------------------- | ------------------------------------------------------------------ | | `0` | `SUCCESS` | Command completed successfully | | `1` | `GENERAL_ERROR` | Unspecified error | | `2` | `USAGE_ERROR` | Invalid arguments or missing confirmation on a destructive command | | `3` | `AUTH_ERROR` | Missing or invalid credentials (HTTP 401) | | `4` | `PERMISSION_DENIED` | Authenticated but not authorised (HTTP 403) | | `5` | `NOT_FOUND` | Resource not found (HTTP 404) | | `6` | `CONFLICT` | Resource state conflict (HTTP 409) | | `7` | `RATE_LIMITED` | API rate limit exceeded (HTTP 429) | | `8` | `SERVER_ERROR` | Streamkap API returned 5xx | | `9` | `TIMEOUT` | Request timed out | | `10` | `CONFIG_ERROR` | Local configuration problem | | `11` | `NETWORK_ERROR` | DNS/connection failure | ## Shell Completions Generate shell completions for your shell: ```bash theme={null} streamkap completions bash >> ~/.bashrc streamkap completions zsh >> ~/.zshrc streamkap completions fish > ~/.config/fish/completions/streamkap.fish ``` ## Using with AI Agents When output is piped (non-TTY), the CLI automatically switches to JSON and skips confirmation prompts. For a richer agent experience with natural language, use the [MCP Server](/mcp-server) instead. See [Agents](/agents) for all integration paths. ## Related * [Agents](/agents) — using the CLI and other tools in agent workflows * [MCP Server](/mcp-server) — connect AI agents to Streamkap via natural language * [API Reference](/api-reference/authentication/access-token) — full REST API documentation # ClickHouse Cloud Source: https://docs.streamkap.com/clickhouse ClickHouse Cloud destination configuration for Streamkap CDC ## Prerequisites * ClickHouse User * ClickHouse database hostname * For nested arrays/structs: `flatten_nested=0` must be set on the ClickHouse server or user level * Dialect version must match your ClickHouse server version (e.g., ClickHouse 22.x requires dialect version 22) ## Delivery Guarantees Streamkap offers **at-least-once** delivery guarantee for ClickHouse and defaults to `upsert` mode. For `append` (or insert) mode, duplicate records will be inserted into destination tables, but, ClickHouse materialized views can filter them out. With `upsert` mode (default) we carry out deduplication using the records primary key. See [Inserts/Upserts](/insertsupserts) for more information about ingestion modes. ## Data duplication Only applies to `upsert` configured ClickHouse Destinations and destination tables. Although using the ClickHouse [`FINAL`](https://clickhouse.com/docs/en/sql-reference/statements/select/from#final-modifier) query modifier has [drawbacks](https://clickhouse.com/docs/en/sql-reference/statements/select/from#drawbacks) , we have seen situations where users `SELECT` queries on the destination tables return some duplicate records and the `FINAL` query modifier helps avoid that. Behind-the-scenes, `upsert` ingestion mode relies on ClickHouse's `ReplacingMergeTree` [table engine](https://clickhouse.com/docs/en/engines/table-engines) to deduplicate data in the background instead of when the data's inserted or merged (e.g. `MERGE INTO` for other data warehouses like Snowflake or DataBricks) into the destination tables. To save having to specify the `FINAL` query modifier in every query, you can consider altering the querying user's role as shown below: ```SQL SQL theme={null} -- Replace { ... } placeholders as required ALTER ROLE {ROLENAME} SETTINGS final = 1; ``` ## Data Type Support All mappings are done on a 'best effort, nearest equivalent' basis because many data warehouses and data processing systems have unique data types or share data types but interpret them differently. Our Connector supports the following data types and mappings: | Streamkap | ClickHouse | | --------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | | `INT8` | `Int8` | | `INT16` | `Int16` | | `INT32` | `Int32` | | `FLOAT32` | `Float32` | | `FLOAT64` | `Float64` | | `BOOLEAN` | `Bool` | | `BYTES` | `BLOB`(String) | | `org.apache.kafka.connect.data.Decimal` | `DECIMAL(38, 0)` | | `org.apache.kafka.connect.data.Timestamp` `io.debezium.time.ZonedTimestamp` | `DateTime64` | | `org.apache.kafka.connect.data.Date` | `Date` | | `io.debezium.data.Json` | `String`(JSON) or `JSON`(if supported by ClickHouse server & `JSON Support` is enabled) | | `STRUCT`(e.g. maps, key/value pairs) | `Tuple` | | `ARRAY` | `Array` | ## ClickHouse Setup It's recommended to create a separate user and role for Streamkap to access your ClickHouse database. Below is an example script that does that. ```SQL SQL theme={null} -- Replace { ... } placeholders as required CREATE ROLE STREAMKAP_ROLE; GRANT SELECT, CREATE, SHOW, INSERT, ALTER ON {database}.* TO STREAMKAP_ROLE; CREATE USER streamkap_user IDENTIFIED WITH sha256_password BY '{password}' GRANT STREAMKAP_ROLE TO streamkap_user; ``` ## Allowing Streamkap access IP access lists in ClickHouse determine what services can connect to your ClickHouse database. From your ClickHouse Cloud services list: 1. Choose the service and then **Settings** 2. Click **+ Add entry** and enter [Streamkap IP Addresses](/streamkap-ip-addresses) 3. Enter a description e.g. **Streamkap app** 4. Click **Add entry** See [Setting IP Filters](https://clickhouse.com/docs/en/cloud/security/setting-ip-filters) for more information about ClickHouse IP access lists. ## Obtaining connection details You will need the **hostname** of your ClickHouse database when setting up the Destination in Streamkap. From your ClickHouse Cloud services list: 1. Choose the service and then **Connect** 2. At the bottom of the connection window you will see `https://` followed by the hostname like this `abc123.us-west-2.aws.clickhouse.cloud`. The part after `https://` (excluding the port `:port`) is the hostname ## Handling semi-structured data To support complex data structures such as nested arrays of structs and nested structs containing arrays, the Connector's user role needs to be configured as shown below: ```SQL SQL theme={null} ALTER ROLE STREAMKAP_ROLE SETTINGS allow_experimental_object_type = 1; ``` ## Streamkap Setup 1. Go to [Destinations](https://app.streamkap.com/connectors/add?tab=Destinations) and choose **ClickHouse** 2. Input the following information: 1. **Name** - A unique and memorable name for this Connector 2. **Hostname** - The IP or URL of your ClickHouse instance 3. **Ingestion Mode** (default: `upsert`) - See [Inserts/Upserts](/insertsupserts) for information about ingestion modes 4. **Port** (default: `8443`) 5. **Username** (case sensitive) - `STREAMKAP_USER` or the username you chose 6. **Password** - Password for the database user 7. **Database** - The name of the database 8. **SSL** - SSL is enabled by default on ClickHouse instances 9. **Tasks** - If Pipelines for this Destination have lag that's continuously growing over time and not reducing, increase the number of Tasks, otherwise, leave as default 10. **JSON Support** - Allow JSON data type in ClickHouse, make sure the ClickHouse server supports it. 3. Click **Save** ## Metadata Columns For usage patterns, see [Metadata](/metadata). ## Troubleshooting If you are experiencing performance issues or lag with your ClickHouse destination, adjustments to poll records, partition counts, and task parallelism can help. See [Performance Tuning](/performance-tuning) for detailed guidance on optimizing pipeline throughput and latency. ## Related Documentation * [Ingestion Modes](/insertsupserts) - Understand insert vs. upsert ingestion modes * [Delete Handling](/insertsupserts#delete-handling) - Configure how delete events are processed at the destination # CockroachDB Source: https://docs.streamkap.com/cockroachdb Stream data into CockroachDB ## Prerequisites * A database user with sufficient privileges to create users ## CockroachDB Setup ### 1. Grant Database Access In the left hand navigation menu, open **Networking** under **Security**. Add the appropriate IP address from [Streamkap IP Addresses](/streamkap-ip-addresses). ### 2. Create Database User It's recommended to create a separate user for Streamkap to access your CockroachDB database. #### Create user via SQL Shell In the left hand navigation menu, open SQL Shell. Switch to the database you will be sending data to or create a new database. Run the script below replacing items in `{}` with the appropriate values. ```SQL SQL theme={null} -- Create User CREATE USER streamkap_user WITH PASSWORD {'password'}; ``` #### Create user via Cloud In the left hand navigation menu, open SQL users under **Security**. Input `streamkap_user` and generate a password. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Hostname**: Specify the hostname. * **Port**: Default is `26257`. * **Database**: Name of the database to use. * **Username**: Username to access the database. By default, Streamkap scripts use `streamkap_user`. * **Password**: Password to access the database. ### 3. Ingestion Settings * **Schema**: The target schema where tables should be created. * **Schema evolution mode**: Controls how schema evolution is handled by the sink connector. **Self-managed schema evolution** If you have created the destination tables and want to manage their schemas instead of the connector, set **Schema evolution mode** to `none`. See [Ingestion Modes](/insertsupserts) for detailed information about insert modes, primary key modes, and delete handling. * **Insert mode**: Specifies the strategy used to insert events into the database. * **Delete mode**: Specifies whether the connector processes DELETE or tombstone events and removes the corresponding row from the database. * **Primary key mode**: Specifies how the connector resolves the primary key columns from the event. * **Custom primary key**: Optional. Either the name of the primary key column or a comma-separated list of fields to derive the primary key from. * **Tasks**: The maximum number of active tasks. This controls the amount of parallelism in writing events. Click **Save**. # Overview Source: https://docs.streamkap.com/concepts Key concepts about real-time data processing and Streamkap's architecture Whether you're an experienced data engineer or new to real-time data processing, the links below will help you understand some key concepts about real-time data processing and our platform's architecture:" # Overview Source: https://docs.streamkap.com/connection-options Network connectivity options for connecting Streamkap to your sources and destinations We support a range of deployment options and the items below are in reference to connection options when using the Streamkap cloud environment. # Database Connectivity SSL Certificate Management Guide Source: https://docs.streamkap.com/connector-security-ssl This guide can help you identify SSL certificate details and encryption levels for database instances and how to update them to use stronger encryption. This guide provides general information to help identify and update SSL certificates. **Streamkap is not responsible for certificate management, database security configuration, or any issues arising from improper certificate handling.** Always consult your database vendor's official documentation and follow your organization's security policies. Improper certificate management may result in service disruption or security vulnerabilities. It's possible that your database instance is using outdated or weak SSL certificates for encrypted connections. Your Sources and Destinations in Streamkap may be affected if they connect to databases with weak SSL certificates. In such cases, you may see connection errors or warnings in the Streamkap UI and logs indicating SSL/TLS issues or encryption algorithm problems, such as: * "Certificate verify failed" * "SSL handshake failed due to weak encryption algorithm" * "Certificates do not conform to algorithm constraints" *** ## Before You Begin ### Prerequisites 1. **Backup**: Create backups of existing certificates and database configuration 2. **Downtime Planning**: Schedule maintenance window for certificate updates 3. **Certificate Preparation**: Ensure new certificates are ready: * Server certificate (`.crt` or `.pem`) * Private key (`.key` or `.pem`) * CA certificate chain (optional but recommended) 4. **Permission Check**: Verify you have necessary permissions to modify database configuration ### Common Certificate Types and Their Security | Key Type | Key Size | Hash Algorithm | Security Level | Recommendation | | -------- | -------- | -------------- | -------------- | ----------------------------- | | RSA | 2048-bit | SHA-256 | Good | Acceptable for most use cases | | RSA | 4096-bit | SHA-256 | Strong | Recommended for high security | | RSA | 1024-bit | SHA-1 | Weak | **Should be upgraded** | | ECDSA | P-256 | SHA-256 | Good | Modern alternative to RSA | | ECDSA | P-384 | SHA-384 | Strong | High security | *** ## PostgreSQL **Check certificate using `openssl`:** ```bash theme={null} openssl s_client -connect {hostname}:5432 -starttls postgres < /dev/null 2>/dev/null | openssl x509 -text -noout ``` **Check certificate encryption using SQL:** ```sql theme={null} SHOW ssl; SHOW ssl_cert_file; SHOW ssl_key_file; SHOW ssl_ca_file; SELECT ssl, version, cipher, bits FROM pg_stat_ssl WHERE pid = pg_backend_pid(); ``` ### Self-hosted PostgreSQL Self-hosted PostgreSQL certificate documentation: * [PostgreSQL SSL Support](https://www.postgresql.org/docs/current/ssl-tcp.html) ### AWS RDS PostgreSQL AWS RDS manages certificates, so typically no action is needed. PostgreSQL on AWS RDS certificate documentation: * [AWS RDS SSL/TLS Certificates](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.SSL.html) * [Updating RDS SSL/TLS Certificates](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.SSL-certificate-rotation.html) ### Azure Database for PostgreSQL Azure manages certificates, so typically no action is needed. Azure PostgreSQL certificate documentation: * [Azure PostgreSQL SSL/TLS](https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/concepts-networking-ssl-tls) ### Google Cloud SQL PostgreSQL Google Cloud SQL manages certificates, so typically no action is needed. Cloud SQL PostgreSQL certificate documentation: * [Cloud SQL Server Certificates](https://cloud.google.com/sql/docs/postgres/configure-ssl-instance) *** ## MySQL / MariaDB **Check certificate using `openssl`:** ```bash theme={null} openssl s_client -connect {hostname}:3306 -starttls mysql < /dev/null 2>/dev/null | openssl x509 -text -noout ``` **Check certificate encryption using SQL:** ```sql theme={null} SHOW STATUS LIKE 'Ssl_cipher'; ``` ### Self-hosted MySQL/MariaDB Self-hosted MySQL/MariaDB certificate documentation: * [MySQL Configuring SSL/TLS](https://dev.mysql.com/doc/refman/8.0/en/using-encrypted-connections.html) ### AWS RDS MySQL/MariaDB MySQL/MariaDB on AWS RDS manages certificates, so typically no action is needed. MySQL/MariaDB on AWS RDS certificate documentation: * [RDS MySQL SSL/TLS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/mysql-ssl-connections.html) * [RDS Certificate Rotation](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.SSL-certificate-rotation.html) ### Azure Database for MySQL Azure manages certificates, so typically no action is needed. Azure MySQL certificate documentation: * [Azure MySQL SSL Connectivity](https://learn.microsoft.com/en-us/azure/mysql/flexible-server/how-to-connect-tls-ssl) ### Google Cloud SQL MySQL Google Cloud SQL manages certificates, so typically no action is needed. Cloud SQL MySQL certificate documentation: * [Cloud SQL MySQL SSL/TLS](https://cloud.google.com/sql/docs/mysql/configure-ssl-instance) *** ## SQL Server `openssl` does not work with SQL Server. **Check certificate using SQL Server Configuration Manager (Recommended):** 1. Open **SQL Server Configuration Manager** 2. Expand **SQL Server Network Configuration** 3. Right-click **Protocols for \[INSTANCE\_NAME]** → **Properties** 4. Navigate to **Certificate** tab 5. View the selected certificate from dropdown 6. Click **View** to see certificate details (issuer, expiration, key size, signature algorithm) **Check certificate using T-SQL:** ```sql theme={null} -- View certificate details (if using certificate-based authentication) SELECT issuer_name, subject, expiry_date, key_length FROM sys.certificates; ``` **Examine certificate from Windows Certificate Store:** ```powershell theme={null} # View certificates in Local Computer store Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {$_.Subject -like "*your-server*"} | Format-List Subject, Issuer, Thumbprint, NotBefore, NotAfter, @{Name="KeySize";Expression={$_.PublicKey.Key.KeySize}}, SignatureAlgorithm ``` ### Self-hosted SQL Server Microsoft SQL Server certificate documentation: * [Configure SQL Server to Use Certificates](https://learn.microsoft.com/en-us/sql/database-engine/configure-windows/configure-sql-server-encryption) ### AWS RDS SQL Server SQL Server on AWS RDS manages certificates, so typically no action is needed. SQL Server on RDS certificate documentation: * [RDS SQL Server SSL/TLS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/SQLServer.Concepts.General.SSL.Using.html) ### Azure SQL Database Azure manages certificates, so typically no action is needed. Azure SQL certificate documentation: * [Azure SQL TLS/SSL](https://learn.microsoft.com/en-us/azure/azure-sql/database/connect-query-content-reference-guide#encryption-and-certificate-validation) * [Azure SQL Certificate Authentication](https://learn.microsoft.com/en-us/azure/azure-sql/database/authentication-aad-configure) *** ## Oracle Please refer to official documentation for details on managing Oracle SSL certificates. ### Self-hosted Oracle Oracle certificate documentation: * [Oracle Database Security Guide - Configuring SSL/TLS](https://docs.oracle.com/en/database/oracle/oracle-database/19/dbseg/configuring-secure-sockets-layer-authentication.html) ### Oracle Cloud (OCI) Oracle Cloud OCI certificate documentation: * [OCI Database SSL/TLS](https://docs.oracle.com/en/cloud/paas/autonomous-database/adbsa/connect-ssl-tls.html) *** ## MongoDB Please refer to official documentation for details on managing MongoDB SSL certificates. ### Self-hosted MongoDB MongoDB certificate documentation: * [Configure MongoDB Instances for TLS/SSL on Self-Managed Deployments](https://www.mongodb.com/docs/manual/tutorial/configure-ssl/) ### MongoDB Atlas MongoDB Atlas manages certificates, so typically no action is needed. MongoDB Atlas certificate documentation: * [Atlas TLS/SSL Configuration](https://www.mongodb.com/docs/atlas/security-cluster-tls/) *** ## Additional Resources * [OpenSSL Documentation](https://www.openssl.org/docs/) # Consumer Groups Source: https://docs.streamkap.com/consumer-groups Monitor and manage consumer groups, track consumer lag, and manage offset positions Consumer Groups provide visibility into a group of Kafka consumers that are reading from your topics. The Consumer Groups page allows you to monitor their status, track lag, inspect group members, and manage offset positions. ## Overview The Consumer Groups page displays all Kafka consumer groups in your project with their current state and membership information: ### Key Features * **Search Functionality**: Filter consumer groups by group ID using the search bar * **State Filtering**: Filter groups by their current state (STABLE, EMPTY, etc.) * **Group Overview**: View group status, membership count, and assigned partitions * **Offset Management**: Reset consumer offsets for specific topic partitions * **Member Details**: Inspect individual consumer group members and their assignments ## Consumer Groups Table The main table lists all consumer groups with the following columns: * **Group ID**: The unique identifier for the consumer group * Click to navigate to the group detail page * Naming patterns typically include connector IDs or job identifiers * **State**: The current state of the consumer group * `STABLE`: Group is active with members consuming messages * `EMPTY`: Group exists but has no active members * **Members**: Number of active consumers in this group * Shows `0` for `EMPTY` groups * Indicates the parallelism level for `STABLE` groups ### Search and Filtering * **Search Bar**: Filter groups by entering part or all of a group ID * **State Filter**: Use the "Filter by State" dropdown to show only groups in a specific state * **Pagination**: Navigate through groups with configurable page size (10, 20, 50, or 100 per page) ## Consumer Group Detail Page Click any group ID to view detailed information about the consumer group, including members and partition assignments. ### Overview Metrics The detail page displays key metrics for the selected consumer group: **Metric Cards:** * **Members**: Number of active consumers in the group * **Assigned Topics**: Count of topics this group is consuming from * **Assigned Partitions**: Total number of partitions assigned to group members * **Coordinator**: The broker ID acting as the group coordinator * **Total Lag**: Aggregate consumer lag across all assigned partitions The coordinator is the Kafka broker responsible for managing group membership and partition assignments for this consumer group. ### Members Table The Members section lists all active consumers in the group: * **Member ID**: Unique identifier for each consumer instance * Format typically includes client ID and a UUID * **Client ID**: The configured client identifier for the consumer * **Host**: IP address of the machine running the consumer The number of members determines the maximum parallelism for consuming from topics. Each member can be assigned one or more partitions. ### Topic Partitions The Topic Partitions section shows partition-level details and consumption progress: **Table Columns:** * **Topic**: The Kafka topic name * Click to navigate to the topic detail page * **Partition**: The partition number within the topic * **Member**: The consumer member ID currently assigned to this partition * **Current Offset**: The last offset consumed by the consumer * **Log End Offset**: The latest available offset in the partition * **Consumer Lag**: The difference between log end offset and current offset * Higher values indicate the consumer is falling behind **Actions:** * **Search**: Filter partitions by topic name * **Reset Offsets**: Select partitions using checkboxes and click "Reset Offsets" to change offset positions Resetting offsets can cause messages to be re-consumed or skipped. Use this feature carefully, especially in production environments. ## Understanding Consumer States Consumer groups can be in different states depending on their activity: ### `STABLE` State A `STABLE` consumer group has: * One or more active members * Partition assignments distributed among members * Active consumption of messages * Regular heartbeats from all members This is the normal operating state for active consumers. ### `EMPTY` State An `EMPTY` consumer group: * Has no active members * Retains its last committed offsets * May have been created by a connector that stopped * Will transition to `STABLE` when consumers reconnect `EMPTY` groups are common for stopped connectors or jobs. They preserve offset information for when consumption resumes. ## Consumer Lag Monitoring Consumer lag is a critical metric for monitoring data pipeline health: **What is Consumer Lag?** Consumer lag represents how far behind a consumer is from the latest message in a partition. It's calculated as: ``` Consumer Lag = Log End Offset - Current Offset ``` **Common Causes of High Lag:** * Insufficient consumer parallelism (too few members) * Slow processing in the consumer application * Network issues between consumer and Kafka * Destination bottlenecks (for destination connectors) * Under-provisioned consumer resources ## Resetting Consumer Offsets Streamkap retains topic data based on your service's retention policy (typically 7 days by default). You can only replay messages that are still within the retention window. Messages older than the retention period have been deleted and cannot be recovered. Offset management allows you to set consumers to replay messages or skip problematic records. ### When to Reset Offsets Consider resetting offsets in these scenarios: * **Replay Messages**: Re-process historical data after fixing a bug * **Skip Errors**: Move past messages causing processing failures * **Synchronize State**: Reset to a known good position after an incident * **Time-Based Recovery**: Jump to messages from a specific timestamp **For Snowflake Destinations (Append Mode)**: When replaying messages to Snowflake destinations, you must reset **both** offset systems: 1. **Consumer Group offsets** - Set to your desired position (Earliest, Latest, Specific Timestamp, or Specific Offset) 2. **Snowflake channel offsets** - Set to `-1` so Snowflake defers to the Consumer Group position Resetting only one will cause ingestion failures or data misalignment. See [Snowflake Offset Management](/snowflake#offset-management-append-mode) for complete instructions. ### Reset Procedure Ensure **all consumers in the group are stopped** to prevent conflicts during the reset. Typically this means stopping the associated Destination, or for direct access consumers, stopping the consumer application. Open the consumer group detail page for the group you want to reset. In the Topic Partitions table, check the boxes for partitions you want to reset. You can select individual partitions or all partitions for a topic. Click the **Reset Offsets** button that becomes enabled when partitions are selected. A dialog will appear with offset reset options. Select how to reset offsets: * **Earliest**: Reset to the beginning of the partition * **Latest**: Reset to the end (skip all existing messages) * **Specific Timestamp**: Reset to the first offset after a given timestamp * **Specific Offset**: Set a custom offset position Review your selection and click **Apply** to reset the offsets. If you're using Snowflake destinations (append mode), you must also reset the Snowpipe Streaming channel offsets to `-1` in Snowflake. This tells Snowflake to defer to the Consumer Group offset position you just set. ```sql theme={null} SELECT SYSTEM$SNOWPIPE_STREAMING_UPDATE_CHANNEL_OFFSET_TOKEN( '..', '', '-1' ); -- Repeat for each partition ``` See [Snowflake Offset Management](/snowflake#offset-management-append-mode) for complete instructions. **Do not resume the Destination until both offset systems are reset.** Resume your Destinations, or for direct access consumers, resume your consumer applications. The consumers will start processing from the new offset positions. Resetting to "Earliest" for large topics will cause the consumer to re-process all historical messages, which may take considerable time and could duplicate data in destinations. Streamkap retains topic data based on your project's retention policy (typically 7 days by default). You can only replay messages that are still within the retention window. Messages older than the retention period have been deleted and cannot be recovered. ## Consumer Group Best Practices 1. **Monitor Lag Regularly**: Set up alerts for consumer lag thresholds to catch issues early 2. **Right-Size Parallelism**: Match the number of consumer members to partition count for optimal throughput 3. **Use Descriptive Group IDs**: Consumer group IDs should clearly indicate their purpose 4. **Coordinate Offset Resets**: Always stop consumers before resetting offsets 5. **Test Offset Changes**: Test offset resets in non-production environments first 6. **Document Offset Operations**: Record when and why you reset offsets for incident tracking 7. **Clean Up Unused Groups**: Remove consumer groups that are no longer needed ## Performance Tuning For comprehensive tuning guidance, see [Performance Tuning](/performance-tuning). If your consumer group is experiencing high lag, consider these optimizations: ### Increase Consumer Parallelism Add more consumer members (up to the number of partitions) to distribute the load: * For Streamkap destination connectors, increase the **Tasks** setting * More tasks = more parallel consumers = higher throughput The maximum useful number of consumers equals the number of partitions. Additional consumers beyond this will remain idle. ### Increase Topic Partitions More partitions enable greater parallelism. See [Topics](/topics) for the safe partition increase procedure. ### Optimize Consumer Configuration For destination connectors, adjust these settings under Advanced configuration: * **Maximum Poll Records**: Increase to fetch more records per poll (e.g., 25000, 50000) * **Fetch Min Bytes**: Set minimum data to wait for before returning from fetch * **Fetch Max Wait**: Maximum time to wait for fetch min bytes ## Troubleshooting ### Consumer Group Not Appearing If a consumer group is missing from the list: 1. **Check Connector Status**: Verify the associated connector is running 2. **Wait for Creation**: Consumer groups are created on first consumption attempt 3. **Refresh the Page**: Click refresh or reload the browser 4. **Verify Project**: Ensure you're viewing the correct Streamkap project ### High Consumer Lag If a consumer group shows consistently high lag: 1. **Check Consumer Status**: Verify all expected members are active 2. **Review Logs**: Check connector or consumer logs for errors ([Logs](/logs)) 3. **Monitor Resources**: Ensure consumers have adequate CPU and memory 4. **Check Destination**: For destination connectors, verify the destination isn't a bottleneck 5. **Increase Parallelism**: Add more tasks or consumers to the group 6. **Optimize Configuration**: Tune poll records and fetch settings ### Reset Offsets Button Disabled If the Reset Offsets button remains disabled: 1. **Select Partitions**: Ensure you've checked at least one partition in the table 2. **Check Permissions**: Verify you have the required permissions to manage offsets 3. **Verify Group State**: Some operations may not be available for certain group states ### Members Show as N/A If partition members display as "N/A": 1. **Check Group State**: `EMPTY` groups have no active members 2. **Verify Consumers**: Ensure consumers are running and connected 3. **Check Connectivity**: Verify network connectivity between consumers and Kafka 4. **Review Logs**: Check for authentication or authorization errors ## Related Documentation * [Topics](/topics) - Manage Kafka topics and partitions * [Kafka Access](/kafka-access) - Configure Kafka users and ACLs * [Pipelines](/pipelines) - Monitor pipeline consumption progress * [Alerts](/alerts) - Set up alerts for consumer lag thresholds # Creating Final State Tables Source: https://docs.streamkap.com/creating-final-state-tables-from-insertsappend How to create final state tables from append (or insert) only destinations. Let's start with an example table and row of data from a Snowflake destination.
RECORD\_METADATA ID FIRST\_NAME LAST\_NAME \_STREAMKAP\_SOURCE\_TS\_MS \_STREAMKAP\_TS\_MS \_\_DELETED \_STREAMKAP\_OFFSET
```json JSON theme={null} { "CreateTime": 1727770177917, "SnowflakeConnectorPushTime": 1727770177919, "headers": { "__op": "c" }, "key": { "id": 1302 }, "offset": 309707, "partition": 0, "topic": "source_66db3762539d8187df6bd5a7.crm.demo" } ``` 1302 Paul Smith 1727770177000 1727770177618 FALSE 309707
In the above example we have 3 data fields that match the source: * `ID`: also the source table's primary key. * `FIRST_NAME` * `LAST_NAME` Then we have the additional columns that Streamkap adds: * `RECORD_METADATA`: Metadata such as the origin topic, partition, offset, event type and more. It can be useful for debugging purposes. * `_STREAMKAP_SOURCE_TS_MS`: Timestamp in milliseconds (UTC) for when the event occurred in the source database. * `_STREAMKAP_TS_MS`: Timestamp in milliseconds (UTC) for when Streamkap received the event. * `__DELETED`: Indicates whether this event/row (by its primary key) has been deleted in the source database. * `_STREAMKAP_OFFSET`: This is an offset value in relation to the events we process. It can be useful for debugging purposes. **Why `_STREAMKAP_OFFSET` must be in the ORDER BY clause** Including `_STREAMKAP_OFFSET` in the `ORDER BY` is mandatory for correct deduplication. Multiple events for the same primary key can have the same `_STREAMKAP_TS_MS` timestamp (e.g., rapid successive updates within the same millisecond). Without `_STREAMKAP_OFFSET`, records with the same timestamp but different offsets may be deduplicated incorrectly, potentially selecting an older event over the latest one. ## Snowflake ### Task & Merge Statement ```sql SQL theme={null} -- Replace the placeholders in the query -- DBNAME, SCHEMANAME, WAREHOUSE_NAME ------------------------------------------------------- -- Create a `TABLE_REFRESH_HISTORY` table to store the timestamp of the last inserted timestamp. This will ensure that only the latest data is pulled during subsequent runs. Additionally, set up a task to run after the main `_TASK` task, which will update the timestamp in the `_TASK_RUN_HISTORY` table. CREATE OR REPLACE TASK .._TASK_RUN_HISTORY WAREHOUSE = WAREHOUSE_NAME AFTER .._TASK AS INSERT INTO ..TABLE_REFRESH_HISTORY (TABLE_NAME, MAX_TIMESTAMP, REFRESH_TIME) SELECT '..', MAX(_streamkap_ts_ms) AS max_ts, CURRENT_TIMESTAMP AS refresh_time FROM ..; --------------------------------------------------------- -- `MERGE` statement will merge data into the target table. Additionally, a configurable task will be created to run every 15 minutes. CREATE TASK .._TASK WAREHOUSE = SCHEDULE = '15 MINUTE' AS MERGE INTO .. AS target USING ( SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY _streamkap_ts_ms DESC, _streamkap_offset DESC) AS dedupe_id FROM .. WHERE _streamkap_ts_ms >= COALESCE((SELECT MAX(MAX_TIMESTAMP) FROM ..TABLE_REFRESH_HISTORY WHERE TABLE_NAME='..'), (DATE_PART('EPOCH', CURRENT_TIMESTAMP()) * 1000) - (8 * 24 * 60 * 60 * 1000)) ) AS subquery WHERE dedupe_id = 1 AND __deleted = 'false' ) AS source ON target.ID = source.ID WHEN MATCHED THEN UPDATE SET target.RECORD_METADATA = source.RECORD_METADATA, , target.__DELETED = source.__DELETED, target._STREAMKAP_SOURCE_TS_MS = source._STREAMKAP_SOURCE_TS_MS, target._STREAMKAP_TS_MS = source._STREAMKAP_TS_MS, target._STREAMKAP_OFFSET = source._STREAMKAP_OFFSET WHEN NOT MATCHED THEN INSERT ( , __DELETED, _STREAMKAP_SOURCE_TS_MS, _STREAMKAP_TS_MS, _STREAMKAP_OFFSET ) VALUES ( , source.__DELETED, source._STREAMKAP_SOURCE_TS_MS, source._STREAMKAP_TS_MS, source._STREAMKAP_OFFSET ); ``` ### Dynamic Tables Streamkap supports the auto creation and maintenance of Snowflake Dynamic Tables for this final view but the logic underneath is as follows ```sql SQL theme={null} -- Replace the placeholders in the query -- DYNAMIC_TABLE_NAME, TARGET_LAG, WAREHOUSE_NAME, PRIMARY_KEY, SOURCE_TABLE_NAME CREATE OR REPLACE DYNAMIC TABLE ( {{column_1}}, {{column_2}}, {{column_3}}, {{column_4}}, {{column_5}}, . . . . __DELETED, _STREAMKAP_SOURCE_TS_MS, _STREAMKAP_TS_MS, _STREAMKAP_OFFSET ) TARGET_LAG = '' -- Example: '15 minutes' REFRESH_MODE = AUTO INITIALIZE = ON_CREATE WAREHOUSE = AS SELECT * EXCLUDE dedupe_id FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY ORDER BY _streamkap_ts_ms DESC, _streamkap_offset DESC) AS dedupe_id FROM ) WHERE dedupe_id = 1 AND __deleted = 'false'; ``` ## DBT ### SQL Example ```sql SQL theme={null} {{ config( materialized='incremental', unique_key=, post_hook ='DELETE FROM {{ this }} WHERE __deleted = TRUE', ) }} WITH cte AS ( SELECT * FROM source_table {% if is_incremental() %} WHERE _streamkap_ts_ms >= (SELECT MAX(_streamkap_ts_ms) FROM {{ this }}) {% endif %} QUALIFY RANK() OVER (PARTITION BY ORDER BY _streamkap_ts_ms DESC, _streamkap_offset DESC) = 1 ) SELECT * FROM cte {% if is_incremental() %} WHERE _streamkap_ts_ms >= (SELECT MAX(this._streamkap_ts_ms) FROM {{ this }} AS this) {% endif %} ``` This is an example approach whereby we can utilize incremental models (DBT or similar) to only compare data that has recently come into the raw source tables from Streamkap. So during incremental refreshes there is no need to scan over the historical CDC data coming in. The 2nd where clause for incremental logic is in relation to `{{ this }}` declaration for DBT, since it can use post DBT hook to delete the `__deleted` columns that are true. ### Python Example This is an example approach for generating the DBT incremental models. ```python Python theme={null} import os import yaml class IncrementalScriptGenerator: def __init__(self, relative_input_path, relative_output_dir): self.home_directory = os.path.expanduser("~") self.relative_input_path = relative_input_path self.relative_output_dir = relative_output_dir self.prod_tables = [] def load_yaml_file(self): full_path = os.path.join(self.home_directory, self.relative_input_path) with open(full_path, "r") as file: self.data = yaml.safe_load(file) def extract_prod_tables(self): for source in self.data.get("sources", []): if source.get("name") == "prod": tables = source.get("tables", []) for table in tables: table_name = table.get("name") if table_name: self.prod_tables.append(table_name) def generate_incremental_scripts(self): for prod_table in self.prod_tables: incremental_script = """ {{ config( incremental_strategy='merge', unique_key='id' ) }} """ incremental_script += f""" select * from {{{{ source('prod', '{prod_table}') }}}} """ incremental_script += """ {% if is_incremental() %} where _streamkap_ts_ms >= (select max(_streamkap_ts_ms) from {{ this }}) {% endif %} qualify rank() over (partition by id order by _streamkap_ts_ms desc, _streamkap_offset desc) = 1 """ relative_output_path = ( f"{self.relative_output_dir}/sk_{prod_table.lower()}.sql" ) final_output_path = os.path.join(self.home_directory, relative_output_path) with open(final_output_path, "w") as file: file.write(incremental_script) print(f"Written to {final_output_path}") if __name__ == "__main__": relative_input_path = "dae-dbt/models/source.yml" relative_output_dir = "dae-dbt/models/streamkap/incremental" processor = IncrementalScriptGenerator(relative_input_path, relative_output_dir) processor.load_yaml_file() processor.extract_prod_tables() processor.generate_incremental_scripts() ``` # Crunchy Bridge PostgreSQL Source: https://docs.streamkap.com/crunchy-bridge-postgresql Set up PostgreSQL change data capture from Crunchy Bridge to Streamkap, including firewall rules, replication roles, publications, and heartbeat options. ## Prerequisites * PostgreSQL version ≥ 10 * A database user with sufficient privileges to configure the database, including enabling logical replication and creating users ## PostgreSQL Setup ### 1. Grant Database Access * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your database. **Firewall rules** Crunchy Bridge restricts access to your cluster using firewall rules configured on the **Networking** tab of your cluster. If you're connecting directly (not via SSH Tunnel or another private connectivity option), add Streamkap's IPs to your firewall rules—see [Streamkap IP Addresses](/streamkap-ip-addresses). **Logical replication is enabled by default** Crunchy Bridge provisions every cluster with `wal_level = logical`, `max_wal_senders = 10`, and `max_replication_slots = 10`. No parameter changes are required to enable logical replication. You can verify this by running: ```SQL SQL theme={null} SHOW wal_level; -- should return logical SHOW max_wal_senders; -- should return 10 SHOW max_replication_slots; -- should return 10 ``` ### 2. Create Database User It's recommended to create a separate user and role for the Connector to access your PostgreSQL database. Connect using the default `postgres` superuser role, then run the example script below. ```SQL SQL theme={null} -- Replace { ... } placeholders as required CREATE USER streamkap_user PASSWORD '{password}'; -- Create a role for Streamkap CREATE ROLE streamkap_role; GRANT streamkap_role TO streamkap_user; -- Grant Streamkap permissions on the database, schema and all tables to capture GRANT CONNECT ON DATABASE "{database}" TO streamkap_role; GRANT CREATE, USAGE ON SCHEMA "{schema}" TO streamkap_role; GRANT SELECT ON ALL TABLES IN SCHEMA "{schema}" TO streamkap_role; ALTER DEFAULT PRIVILEGES IN SCHEMA "{schema}" GRANT SELECT ON TABLES TO streamkap_role; -- Grant replication privilege to the user ALTER USER streamkap_user WITH REPLICATION; ``` ### 3. Enable Snapshots To backfill your data, the Connector needs to be able to perform snapshots. See [Snapshots & Backfilling](/snapshots) for more information. To enable this feature, there are 2 methods available: #### Method 1: Enable read only connection Requires PostgreSQL version 13 or higher. This method is recommended if you cannot create a table in the source database and grant the Connector read/write privileges to that. * Set **Read only** to **Yes** during Streamkap Setup. No other configuration should be necessary. #### Method 2: Create a table in the source database Not supported on read replicas. Please use method 1 instead. You will need to create the table and give necessary permissions to the `streamkap_user`. The Connector will use this collection for managing snapshots. Below is an example script that does that. The examples below use `streamkap_signal` as the signal table name, but you can choose any name. During [Streamkap Setup](#3-snapshot-settings), provide the full path to your signal table in `schema.table` format (e.g., `streamkap.streamkap_signal`). ```SQL SQL theme={null} -- Create the schema CREATE SCHEMA streamkap; -- Switch to the newly created schema SET search_path TO streamkap; -- Create the table CREATE TABLE streamkap_signal ( id VARCHAR(255) PRIMARY KEY, type VARCHAR(32) NOT NULL, data VARCHAR(2000) NULL ); -- Grant necessary privileges on the table to the role GRANT CREATE, USAGE ON SCHEMA streamkap TO streamkap_role; GRANT SELECT ON ALL TABLES IN SCHEMA streamkap TO streamkap_role; GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE streamkap_signal TO streamkap_role; ``` **Publications and signal tables** When you create the PostgreSQL publication in the next step, if you choose to specify tables for capture instead of all tables, you **must** include your signal table. ### 4. Heartbeats Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. For read-write connections (when **Read only** is **No** during Streamkap Setup), the Connector writes to the heartbeat table directly. ```SQL SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Switch to the streamkap schema SET search_path TO streamkap; -- Create the heartbeat table with id, text, and last_update fields CREATE TABLE streamkap_heartbeat ( id SERIAL PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Grant permission to the Streamkap user GRANT USAGE ON SCHEMA streamkap TO streamkap_user; GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE streamkap_heartbeat TO streamkap_user; -- Insert the first row into the heartbeat table INSERT INTO streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` **Heartbeat tables and PostgreSQL publications** If the `streamkap_pub` publication created during PostgreSQL Setup was for specific tables e.g. `CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, table3, ...;` instead of `FOR ALL TABLES;`, you **must** add the heartbeat table to the publication: `ALTER PUBLICATION streamkap_pub ADD TABLE streamkap.streamkap_heartbeat;`. For read-only connections (when **Read only** is **Yes** during Streamkap Setup), the Connector cannot write to the heartbeat table directly. Instead, you must configure a scheduled job on the **primary** database to generate artificial traffic. These changes will replicate to the read replica, which the Connector then consumes. **Run these commands on the primary database, not the read replica.** The heartbeat table and scheduled job must be created on the primary database. The changes will automatically replicate to the read replica. The `pg_cron` extension must be allowed in your database's configuration. See your provider's documentation for enabling extensions: Scheduling maintenance with pg\_cron Scheduling maintenance with pg\_cron PostgreSQL extensions PostgreSQL extensions pg\_cron extension pg\_cron extension Supported extensions Once allowed, create the extension: ```sql SQL theme={null} CREATE EXTENSION IF NOT EXISTS pg_cron; ``` ```sql SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Create the heartbeat table CREATE TABLE streamkap.streamkap_heartbeat ( id SERIAL PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Insert the initial row INSERT INTO streamkap.streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` ```sql SQL theme={null} -- Schedule a job to update the heartbeat table every minute SELECT cron.schedule( 'streamkap_heartbeat_job', '*/1 * * * *', $$UPDATE streamkap.streamkap_heartbeat SET text = 'updated_heartbeat', last_update = CURRENT_TIMESTAMP WHERE id = 1;$$ ); ``` Whichever database user is used to create and run the cron jobs (often the `postgres` user or a dedicated cron user) needs appropriate permissions on the heartbeat table. Additionally, the Streamkap user also needs permissions to monitor the heartbeat table. ```sql SQL theme={null} GRANT USAGE ON SCHEMA streamkap TO {cron user}; GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO {cron user}; -- Grant permissions to the Streamkap user for monitoring and diagnostics GRANT USAGE ON SCHEMA streamkap TO streamkap_user; GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO streamkap_user; ``` The `pg_cron` extension stores job execution history in the table `cron.job_run_details`. To prevent this table from growing indefinitely: ```sql SQL theme={null} SELECT cron.schedule( 'streamkap_cron_cleanup', '0 0 * * *', $$DELETE FROM cron.job_run_details WHERE end_time < now() - interval '7 days';$$ ); ``` ```sql SQL theme={null} -- View all scheduled jobs SELECT * FROM cron.job; -- View recent job execution history SELECT * FROM cron.job_run_details ORDER BY start_time DESC LIMIT 10; -- Unschedule a job (replace {jobid} with the actual job ID) SELECT cron.unschedule({jobid}); ``` **Heartbeat tables and PostgreSQL publications** If the `streamkap_pub` publication was created for specific tables (e.g., `CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, ...;`) instead of `FOR ALL TABLES`, you **must** add the heartbeat table to the publication: ```sql SQL theme={null} ALTER PUBLICATION streamkap_pub ADD TABLE streamkap.streamkap_heartbeat; ``` **Logical-message heartbeats** are an alternative to a heartbeat table. The Connector itself calls `pg_logical_emit_message()` on each beat, which appends a transactional record to the write-ahead log (WAL). PostgreSQL treats that record like any other committed change, so the replication slot advances and WAL files are released—without any new table, scheduled job, or write privileges on the source database. **When to choose this over a heartbeat table** Use **logical-message heartbeats** when you want zero footprint in the source database itself—no schemas, tables, scheduled jobs, or write grants to maintain. Use a **heartbeat table** when you want the heartbeat to be visible as an ordinary row that operators can `SELECT` to confirm liveness, or when the requirements below rule logical-message out. **Requirements** * **Streamkap must be connected directly to a primary**, not to a read replica or other hot standby. The Connector executes `pg_logical_emit_message()` against the database it's connected to, and that function writes to the WAL—hot standbys are read-only and will reject the call with an error like `cannot execute ... during recovery`. If your Streamkap source is configured against a replica, use the **Read-only connections** tab instead. * PostgreSQL **14 or later** on the primary. Earlier versions are not supported because the built-in `pgoutput` replication plugin only began streaming logical-decoding messages to the Connector in PostgreSQL 14. On PostgreSQL 13 and earlier, `pg_logical_emit_message()` still appends records to the WAL, but the Connector cannot read them and the replication slot will not advance. * The Streamkap user needs `SELECT` privileges only—no `INSERT`, `UPDATE`, or `DELETE` on any heartbeat table. * Logical replication must already be enabled for the Connector itself (this is a prerequisite for any PostgreSQL source, regardless of heartbeat mode). During [Connection Settings](#2-connection-settings): * Set **Heartbeats** to **Yes**. * Leave **Heartbeat Table Schema** blank. * Set **Use Logical-Message Heartbeat** to **Yes**. No further setup is required in the source database. The Connector will issue the heartbeat query once per minute using the standard Streamkap heartbeat interval. After enabling, you can confirm that the replication slot is advancing against an otherwise idle database. Run on the primary: ```sql SQL theme={null} SELECT slot_name, confirmed_flush_lsn, restart_lsn FROM pg_replication_slots WHERE slot_name = 'streamkap_pgoutput_slot'; ``` Re-run the query after one or two minutes. Both `confirmed_flush_lsn` and `restart_lsn` should move forward. **What the Connector sends** When this mode is enabled, the Connector issues the following query on each beat: ```sql SQL theme={null} SELECT pg_logical_emit_message(true, '__debezium-heartbeat', now()::varchar); ``` The first argument (`true`) makes the emit **transactional**. Non-transactional emits advance the replication slot's `confirmed_flush_lsn` but do not recompute global WAL retention across all slots, so WAL files can still accumulate on an idle source database—transactional emits avoid that. The transactional cost is negligible (one extra transaction ID per beat, well below PostgreSQL's wraparound horizon at the default one-minute interval). The heartbeat record is filtered out before reaching Kafka, so it does not appear as a record in any topic in your pipeline. ### 5. Create Publication & Slot Publications contain a set of change events for the tables you want the Connector to capture. * Create a publication for your tables. You can create a publication for all tables or selected tables. ```SQL SQL - All Tables theme={null} -- Create a publication for all tables to capture CREATE PUBLICATION streamkap_pub FOR ALL TABLES; ``` ```SQL SQL - Selected Tables theme={null} -- Create a publication for specific tables to capture CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, table3, ...; -- Verify the tables to capture were added to the publication SELECT * FROM pg_publication_tables where pubname = 'streamkap_pub'; ``` **Altering publications** You cannot alter `FOR ALL TABLES` publications to include/exclude tables. If you set up a `FOR ALL TABLES` publication and later decide to change that, you have to drop the publication and create another to include specific tables e.g. `CREATE PUBLICATION ... TABLE table1, table2, table3, ...`. However, any change events that occur before the new publication's created will not be included in it, so a snapshot's required to ensure they are not missed by your Streamkap pipelines. You should also stop the [Source](https://app.streamkap.com/connectors/sources) *before* changing the publication. A replication slot represents a stream of change events the Connector reads from. * Create a replication slot. ```SQL SQL theme={null} -- Create a logical replication slot SELECT pg_create_logical_replication_slot('streamkap_pgoutput_slot', 'pgoutput'); -- Verify the replication slot is working (this may take a few moments to return the count) SELECT count(*) FROM pg_logical_slot_peek_binary_changes('streamkap_pgoutput_slot', null, null, 'proto_version', '1', 'publication_names', 'streamkap_pub'); ``` **Replication slot auto-creation** If the Connector cannot find the replication slot named in [Replication Settings](#4-replication-settings) during Streamkap Setup, it will attempt to create one automatically as it cannot read the database transaction logs without one. This is assuming the database user has been granted the `REPLICATION` privilege as per [Create Database User](#2-create-database-user). #### Excluding columns from replication By default a publication streams every column of each captured table. On **PostgreSQL 15+** you can attach a *column list* to a table in the publication so that only the listed columns are replicated — any column you leave out is excluded at the source and never enters the replication stream. ```SQL SQL - Selected Columns theme={null} -- Only the listed columns are published; every other column is excluded. -- Each table's list MUST include its replica identity (primary key), -- otherwise UPDATE/DELETE for that table cannot be replicated. CREATE PUBLICATION streamkap_pub FOR TABLE public.users (id, email, created_at, updated_at), public.orders (id, user_id, status, total), public.products (id, sku, name, price); -- Add or change a table's column list on an existing publication ALTER PUBLICATION streamkap_pub SET TABLE public.users (id, email, updated_at); -- Verify the published columns per table SELECT pubname, schemaname, tablename, attnames FROM pg_publication_tables WHERE pubname = 'streamkap_pub'; ``` * Requires **PostgreSQL 15 or later**. On earlier versions a publication always streams all columns. * Each table's column list **must include the table's replica identity** — its primary key, or the columns from `REPLICA IDENTITY USING INDEX` / `FULL`. Otherwise `UPDATE` and `DELETE` events for that table are not replicated. * Column lists are only valid with `FOR TABLE`, not `FOR ALL TABLES`. A table listed without parentheses publishes all of its columns, so you can mix restricted and unrestricted tables in one publication. A publication column list only governs **streamed (CDC) changes**. The initial snapshot reads columns directly from the table, so an excluded column can still appear in snapshot records. If a column must never be captured, exclude it from the snapshot as well, not only from the publication. *** ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **PostgreSQL**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Hostname**: Specify the hostname. Crunchy Bridge cluster hostnames use the format `p..db.postgresbridge.com`—copy it from the **Connection** tab of your cluster in the Crunchy Bridge dashboard. **PgBouncer connections aren't supported** Crunchy Bridge's built-in PgBouncer pooler (port `5431`) explicitly blocks roles with the `REPLICATION` privilege from connecting. Use the direct database port (default `5432`) for the Connector, not the pooled connection. * **Port**: Default is `5432`. * **Connect via SSH Tunnel**: The Connector will connect to an SSH server in your network which has access to your database. This is necessary if the Connector cannot connect directly to your database. * See [SSH Tunnel](/ssh-tunnel) for setup instructions. * **Username**: Username to access the database. By default, Streamkap scripts use `streamkap_user`. * **Password**: Password to access the database. * **Database**: Specify the database to stream data from. * **Read only**: Whether or not to use a read-only connection. Requires PostgreSQL version 13 or higher. * When connecting to a read replica, set this to **Yes** to support Streamkap snapshots. * **Heartbeats**: Enabled by default. Choose one of three modes—see [Heartbeats](#4-heartbeats) for setup instructions: * **Read-write** connections: configure a heartbeat table in the source database and set **Heartbeat Table Schema**. * **Read-only** connections: configure a scheduled heartbeat job on the primary database using `pg_cron`, and include the heartbeat table in [Schema and Table Capture](#6-schema-and-table-capture). * **Logical-message** (PostgreSQL 14+, works under read-only): set **Use Logical-Message Heartbeat** to **Yes** and leave **Heartbeat Table Schema** blank. No table or write privileges required on the source. ### 3. Snapshot Settings If you set Read only to **No**, you will need to create a snapshot signal table and give permissions to the `streamkap_user`. See [Enable Snapshots](#3-enable-snapshots) for setup instructions. * **Signal Table**: Full path to the signal table including schema and table name (e.g., `streamkap.streamkap_signal`). This table is used for incremental snapshotting. See [Enable Snapshots](#3-enable-snapshots) for setup instructions. ### 4. Replication Settings * **Replication Slot Name**: The name of the replication slot for the connector to use. Default is `streamkap_pgoutput_slot`. * **Publication Name**: The name of the publication for the connector to use. Default is `streamkap_pub`. ### 5. Advanced Parameters * **SSL mode**: Whether to use an encrypted connection to the PostgreSQL server. By default, it's required. Crunchy Bridge requires SSL for all connections regardless of this setting. * **Prefix with Database Name?**: Changes the format of topics to `DatabaseName_TopicName` * **Represent binary data as**: Specifies how the data for binary columns e.g. `blob`, `binary`, `varbinary` should be interpreted. Your destination for this data can impact which option you choose. Default is `bytes`. Click **Next**. ### 6. Schema and Table Capture * **Add Schemas/Tables**: Specify the schema(s) and table(s) for capture * You can bulk upload here. The format is a simple list of schemas and tables, with each entry on a new row. Save as a `.csv` file without a header. **CDC only captures base tables, not Views** Change Data Capture reads the PostgreSQL write-ahead log (WAL), which only records changes to physical tables. Database Views are query-time computations with no physical storage—they don't generate WAL entries. **What you cannot capture:** Views, temporary tables, unlogged tables, foreign tables (FDW), or system tables (information\_schema, pg\_catalog). **Solution:** Specify only the underlying base tables that feed your views. You can recreate the view logic in your destination or transformation layer. **Generated columns are not kept up to date** PostgreSQL [generated columns](https://www.postgresql.org/docs/current/ddl-generated-columns.html) (defined with `GENERATED ALWAYS AS (...) STORED`) are calculated by the database from other columns. PostgreSQL does **not** include them in the replication stream Streamkap reads, so live streaming cannot keep their values current. **We recommend excluding generated columns from capture.** In the Source's **Settings → Advanced** section, set **Column Selection Mode** to **Exclusion** and add the column to the **Column Exclusion List** (fully-qualified, e.g. `schema.table.column`). If you need the value at your destination, calculate it there — or with a [transform](/transforms) — from the columns it is derived from. **Snapshots:** An incremental snapshot (the watermarked **Filtered** and **Full** options) of a table that contains a generated column will **fail** unless the column is excluded. **Blocking** and **Parallel** snapshots are not affected, though any value they capture will become stale because streaming cannot update it. See [Snapshots & Backfilling](/snapshots). PostgreSQL 18 and later can optionally stream stored generated columns when the publication is created with `publish_generated_columns = stored`; earlier versions cannot. Click **Save**. ## Troubleshooting When upgrading the PostgreSQL database used by Streamkap, there are specific steps to prevent data loss and ensure continued operation. Streamkap handles network failures and outages well. If a monitored database stops, the connector resumes from the last recorded log sequence number (LSN) once communication is restored. It retrieves this offset and queries PostgreSQL for a matching LSN in the replication slot. A replication slot is required for change capture, but PostgreSQL removes slots during upgrades and doesn't restore them. When the connector restarts, it requests the last known offset, but PostgreSQL cannot return it. Creating a new replication slot isn't enough to prevent data loss. New slots only track changes from their creation point and lack earlier offsets. The connector fetches its last known offset from Kafka but can't retrieve corresponding data from the new slot. It skips older change events and resumes from the latest log position, causing silent data loss with no warnings. ### Procedure Follow these steps to minimize data loss. Note that a few steps may require support from Streamkap. We recommend notifying us about your database upgrade ahead of time to ensure you have the necessary support. * Using Crunchy Bridge's upgrade procedure, ensure writes to it have stopped. * Allow the connector to capture all change events before starting the upgrade procedure. Ask Streamkap to confirm this for you. **Interrupting change data capture** If all the events were not captured before stopping the Source and upgrading the database, you can perform a snapshot (after the procedure is completed) in Streamkap for that Source to ensure no change events were missed. * Assuming all events are captured, stop the Source in the Streamkap app. This flushes the last records and saves the last offset. * Stop the database and upgrade it using Crunchy Bridge's upgrade procedure. Once the database is upgraded, and before allowing writes again: * Recreate the logical replication slot, otherwise, Streamkap will miss changes. See [Create Publication & Slot](/crunchy-bridge-postgresql#5-create-publication--slot). * Verify the publication for Streamkap exists; recreate it if necessary: [Create Publication & Slot](/crunchy-bridge-postgresql#5-create-publication--slot). **Update the Source in the Streamkap app** If you have chosen different names for the logical replication slot or publication, make sure to update them in the Streamkap setup page for the relevant PostgreSQL Source. In this case, contact Streamkap, as they may need to reset your Connector's offsets in this scenario. * Restore write access to the database. * Resume or restart the Source in the Streamkap app. Introduced in PostgreSQL 13, the `REPLICA IDENTITY` table setting controls what data is logged for row updates and deletes. By default, only the primary key and Streamkap metadata column values are retained for deleted records. All other columns will be empty. This leaves you with an incomplete record. If you require - for auditing and historical tracking purposes - all column values for deleted records, or if your deletion strategy for your destination is 'soft deletes' (retain the deleted record with a deletion flag), you should set the `REPLICA IDENTITY` to `FULL` for all capture tables. ```SQL SQL theme={null} ALTER TABLE {table} REPLICA IDENTITY FULL; ``` This ensures complete data retention. For capturing partitioned tables, it's essential to enable `publish_via_partition_root` on the publication. By default, changes to partitions are published from the partition itself. The connector expects changes to come from the root table. To ensure compatibility and consistent replication, enable this setting: `ALTER PUBLICATION streamkap_pub SET (publish_via_partition_root = true);` # Data Encoding & Character Sets Source: https://docs.streamkap.com/data-encoding How Streamkap handles character encoding, binary data, and charset configuration in CDC pipelines. ## Overview Streamkap uses **UTF-8** as its internal encoding for all data transit through the pipeline. Text data flowing through Streamkap is expected to be UTF-8 compatible. Binary data (such as BLOBs and raw byte columns) is handled separately and preserved in its original form or encoded as base64 depending on the source configuration and destination requirements. Key points: * All text fields are serialized as UTF-8 in Kafka * Binary columns (`BLOB`, `BYTEA`, `VARBINARY`, `RAW`) are configurable via the **Represent binary data as** source setting * Non-UTF-8 text data is not supported and may result in data loss or errors ## How Encoding Works in the Pipeline Data flows through the following stages: ```text theme={null} Source Database --> Kafka (UTF-8 serialization) --> Destination ``` | Data type | How it is handled | | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | | **Text fields** (VARCHAR, TEXT, NVARCHAR, CHAR) | Decoded and serialized as UTF-8 strings | | **Binary fields** (BLOB, BYTEA, VARBINARY, RAW) | Preserved as binary; representation controlled by the **Represent binary data as** source connector setting (default: `bytes`) | | **JSON fields** | Serialized as UTF-8 JSON strings | | **Decimal/Numeric fields** | Encoded as bytes with schema metadata or as numeric strings, depending on connector configuration | ### Binary Data Representation All source connectors that handle binary columns expose a **Represent binary data as** setting. This controls how binary column data (e.g., `BLOB`, `BINARY`, `VARBINARY`) is interpreted. Your destination for this data can impact which option you choose. The default is `bytes`. The binary data representation setting is configured per source connector. See your specific source connector setup page for details. ## Database-Specific Encoding Notes * PostgreSQL supports UTF-8 natively when the database is created with `UTF8` encoding * Verify your database encoding: `SHOW server_encoding;` * The `client_encoding` should be set to `UTF8` for the Streamkap connection * Binary data types (`BYTEA`) are handled via the **Represent binary data as** setting * Arrays, JSON/JSONB, and hstore types are serialized as UTF-8 strings * **Unsupported**: Non-UTF-8 database encodings (e.g., `LATIN1`, `SQL_ASCII`) If your PostgreSQL database uses a non-UTF-8 encoding (such as `SQL_ASCII` or `LATIN1`), character data may not be captured correctly. Convert your database to UTF-8 encoding before configuring CDC. * Check your server character set: `SHOW VARIABLES LIKE 'character_set_server';` * Check your database character set: `SHOW VARIABLES LIKE 'character_set_database';` * **Recommended**: Use `utf8mb4` as the character set (supports the full Unicode range, including emoji and CJK characters) * MySQL's legacy `utf8` charset only supports up to 3-byte characters (Basic Multilingual Plane) and cannot store 4-byte characters such as emoji * Binary data types (`BLOB`, `BINARY`, `VARBINARY`) are handled via the **Represent binary data as** setting * `JSON`, `ENUM`, and `SET` types are serialized as UTF-8 strings * **Unsupported**: Non-UTF-8 character sets (e.g., `latin1`, `cp1252`) * Check your database character set: `SELECT value$ FROM sys.props$ WHERE name = 'NLS_CHARACTERSET';` * **Recommended**: `AL32UTF8` for best compatibility with Streamkap * `NLS_NCHAR_CHARACTERSET` should be `AL16UTF16` (default) for NVARCHAR2/NCHAR columns; these are converted to UTF-8 by the connector * Binary types (`RAW`, `LONG RAW`, `BLOB`) are handled via the **Represent binary data as** setting * CLOB and NCLOB data is serialized as UTF-8 strings * XMLTYPE and JSON (12c+) are serialized as UTF-8 strings * `nvarchar`, `nchar`, and `ntext` columns use UTF-16 internally; the Streamkap connector converts these to UTF-8 * `varchar` and `char` columns use the database collation's code page; ensure these use a UTF-8 compatible collation (SQL Server 2019+ supports `_UTF8` collations) * Binary types (`BINARY`, `VARBINARY`, `IMAGE`) are handled via the **Represent binary data as** setting * XML and hierarchyid types are serialized as UTF-8 strings * **Unsupported**: Non-UTF-8/UTF-16 encodings in older collations may cause data loss * BSON (MongoDB's binary JSON format) uses UTF-8 natively for all string data * String fields are serialized as UTF-8 without conversion * Binary data types are configurable (bytes, base64, or hex) * Array and nested document encoding is controlled by the **Array Encoding** and **Nested Document Encoding** source settings * **Unsupported**: Non-UTF-8 string data in BSON documents; oversized BSON documents (strategies: fail/skip/split) ## Common Encoding Issues **Symptoms**: Text appears as question marks, mojibake, or unexpected characters at the destination. **Common causes**: * The source database is not using UTF-8 encoding * A character set mismatch between the database server and client connection * Legacy encoding (e.g., `latin1` in MySQL or `SQL_ASCII` in PostgreSQL) storing non-ASCII data **Resolution**: 1. Verify the source database encoding (see database-specific notes above) 2. Convert the database or affected tables/columns to UTF-8 (`utf8mb4` for MySQL, `UTF8` for PostgreSQL) 3. Ensure the database client connection is set to UTF-8 **Symptoms**: Some characters are silently dropped or replaced during transit. **Common causes**: * Non-UTF-8 characters in the source data that cannot be decoded * MySQL `utf8` charset (3-byte) used instead of `utf8mb4` (4-byte), truncating 4-byte characters * Source data contains invalid byte sequences **Resolution**: 1. For MySQL, switch to `utf8mb4`: `ALTER DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;` 2. Identify and clean invalid byte sequences in the source data 3. Ensure the source database uses a full UTF-8 encoding **Symptoms**: `BLOB`, `BYTEA`, or `VARBINARY` columns appear as long base64-encoded strings at the destination instead of raw binary. **Cause**: This is expected behavior. Binary columns are serialized for transport through Kafka, which uses text-based serialization. The **Represent binary data as** setting on the source connector controls the format. **Resolution**: * If your destination needs raw binary, check whether it supports automatic base64 decoding * Review the **Represent binary data as** setting on your source connector (default: `bytes`) * For text-based destinations (JSON, CSV), base64 encoding is the standard approach for binary data **Symptoms**: Emoji, Chinese/Japanese/Korean characters, or other multi-byte Unicode characters are missing or corrupted. **Common causes**: * MySQL using `utf8` instead of `utf8mb4` (only supports Basic Multilingual Plane, up to 3 bytes) * Destination column defined with insufficient character set support * Intermediate systems stripping 4-byte UTF-8 sequences **Resolution**: 1. For MySQL sources, ensure the character set is `utf8mb4` at the server, database, and table levels 2. Verify the destination table/column supports full UTF-8 (4-byte) 3. Test with a known multi-byte string (e.g., an emoji) before going to production ## Best Practices 1. **Use UTF-8 encoding at the source database level** -- this is the single most important step for preventing encoding issues 2. **For MySQL, always use `utf8mb4`** -- MySQL's `utf8` only supports 3-byte characters and silently truncates 4-byte characters (emoji, some CJK characters, mathematical symbols) 3. **Test with special characters before production** -- insert rows containing emoji, accented characters, and CJK text, then verify they arrive correctly at the destination 4. **For binary data, choose the right representation** -- review the **Represent binary data as** setting on your source connector and ensure your destination can handle the chosen format 5. **Monitor the Dead Letter Queue (DLQ) for encoding errors** -- encoding-related failures appear in the [Dead Letter Queue](/dlq-operations); check error headers for conversion or charset messages ## Destination Encoding Configuration Most destinations receive data as UTF-8 by default and do not require additional encoding configuration. ### Redis The [Redis (Generic)](/redis-destination-generic) destination includes a configurable **Character Encoding** parameter (default: `UTF-8`). This setting controls the character set used for encoding string values written to Redis. Adjust this only if your Redis consumers expect a different encoding. ### Other Destinations All other destinations use UTF-8 encoding by default. No additional encoding configuration is required. ## Known Limitations * **Non-UTF-8 error handling**: The exact behavior when non-UTF-8 data is encountered (silent replacement, error, or routing to the DLQ) depends on the source connector and database. Consult your specific source connector documentation. * **Encoding conversion as a transform**: Encoding conversion is not currently available as a built-in transform option. Data must be UTF-8 compatible at the source. * **Maximum string length**: String length limits vary by destination. Check your destination connector documentation for column size constraints and the [DLQ](/dlq-operations) for size-related errors. # Database Upgrade Guide Source: https://docs.streamkap.com/database-upgrade-guide How to maintain CDC pipelines during source database version upgrades Upgrading your source database version (e.g., PostgreSQL 14 to 16, MySQL 5.7 to 8.0) requires careful planning to maintain CDC pipeline continuity. This guide covers pre-upgrade preparation, upgrade procedures, and post-upgrade verification for each supported database type. We recommend notifying [Streamkap support](mailto:support@streamkap.com) about your database upgrade ahead of time. Some steps may require assistance from the Streamkap team, particularly around offset management and connector restarts. ## General Pre-Upgrade Checklist Before upgrading any source database, complete the following preparation steps: 1. Verify your pipeline is healthy -- no errors, low consumer lag, and the connector shows a **RUNNING** status 2. Note the current pipeline position (snapshot status, consumer lag metrics) 3. Consider pausing the pipeline during the upgrade window to avoid partial captures 4. Ensure a database backup is available and tested 5. Review the database-specific CDC requirements for your target version (see tabs below) 6. Test the upgrade in a non-production environment first, including verifying that CDC resumes correctly Streamkap handles brief network interruptions well. If a monitored database stops, the connector attempts to resume from the last recorded position once communication is restored. However, database version upgrades can invalidate replication positions and require additional steps beyond a simple reconnection. ## Database-Specific Upgrade Guides ### Minor Version Upgrades (e.g., 14.8 to 14.12) Minor version upgrades are generally safe for CDC pipelines: * Replication slots are preserved across minor upgrades * The pipeline reconnects automatically after the database restarts * No snapshot is typically required Verify that `wal_level = logical` remains set after the upgrade, as minor upgrades should not change this. ### Major Version Upgrades (e.g., 14 to 16) Major PostgreSQL upgrades (using `pg_upgrade` or equivalent) **drop logical replication slots**. The pipeline will need to be reconfigured after the upgrade. Failing to follow the correct procedure can result in silent data loss. PostgreSQL removes replication slots during major upgrades and does not restore them. When the connector restarts, it requests the last known offset, but PostgreSQL cannot return it from the new slot. A new replication slot only tracks changes from its creation point, so the connector would skip older change events and resume from the latest log position. **Procedure:** Using your database's upgrade procedure, ensure writes to the database have stopped. Allow the connector to capture all remaining change events before starting the upgrade. Ask Streamkap to confirm all events have been captured. Stop the Source in the [Streamkap app](https://app.streamkap.com/connectors?tab=Sources). This flushes the last records and saves the last offset. Stop the database and upgrade it using your standard upgrade procedure (e.g., `pg_upgrade`). Confirm that `wal_level = logical` is set in the upgraded PostgreSQL configuration. Major upgrades may reset this parameter. ```sql SQL theme={null} SHOW wal_level; ``` The replication slot must be recreated after the upgrade: ```sql SQL theme={null} SELECT pg_create_logical_replication_slot('streamkap_pgoutput_slot', 'pgoutput'); ``` Confirm the publication for Streamkap still exists. Recreate it if necessary with the same tables: ```sql SQL theme={null} -- Check if publication exists SELECT * FROM pg_publication WHERE pubname = 'streamkap_pub'; -- Recreate if needed (all tables) CREATE PUBLICATION streamkap_pub FOR ALL TABLES; -- Or recreate with specific tables CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, table3; ``` Restore write access to the database, then resume or restart the Source in the Streamkap app. If any events were not captured before the upgrade, trigger a [snapshot](/snapshots) for the affected tables to ensure no data was missed. If you choose different names for the replication slot or publication during the upgrade, update them in the Streamkap setup page for the relevant PostgreSQL Source. Contact Streamkap, as they may need to reset your connector's offsets. ### RDS / Aurora PostgreSQL Specifics The same general procedure applies for Amazon RDS and Aurora PostgreSQL managed upgrades. Key considerations: * Use the RDS console or CLI to perform the version upgrade * After the upgrade, verify `rds.logical_replication` is still set to `1` in your parameter group * Recreate the replication slot and verify the publication as described above * For Aurora, ensure you are upgrading the **primary** instance (Aurora read replicas only support physical replication) Exact behavior during RDS managed upgrades may vary depending on the upgrade type (in-place vs. blue/green deployment). Refer to your [PostgreSQL platform setup guide](/postgresql) for platform-specific configuration details. For full PostgreSQL source setup and troubleshooting details, see: * [PostgreSQL (self-hosted)](/postgresql-self-hosted) * [Amazon RDS PostgreSQL](/postgresql-on-amazon-rds) * [Amazon RDS Aurora PostgreSQL](/amazon-rds-aurora-postgresql) * [PostgreSQL Source FAQ](/postgresql-source-faq) ### Minor Version Upgrades (e.g., 8.0.30 to 8.0.35) Minor version upgrades are generally seamless for MySQL CDC pipelines: * If GTID is enabled, the pipeline resumes from the GTID position automatically * Binary log positions are typically preserved * No snapshot is required in most cases ### Major Version Upgrades (e.g., 5.7 to 8.0) Major MySQL upgrades require verifying that all CDC-related configuration remains intact. **Pre-upgrade verification:** Confirm these settings are properly configured in your target version: | Setting | Required Value | Verification Query | | ------------------------- | -------------- | ----------------------------------------- | | `binlog_format` | `ROW` | `SHOW VARIABLES LIKE 'binlog_format';` | | `binlog_row_image` | `FULL` | `SHOW VARIABLES LIKE 'binlog_row_image';` | | `log_bin` | `ON` | `SHOW VARIABLES LIKE 'log_bin';` | | `gtid_mode` (recommended) | `ON` | `SHOW VARIABLES LIKE 'gtid_mode';` | **Upgrade behavior depends on GTID configuration:** If GTID mode is enabled, the upgrade is significantly simpler: 1. Stop writes to the database 2. Allow the connector to capture all remaining events 3. Perform the upgrade using your standard procedure 4. Verify all CDC-related settings (see table above) 5. Resume the Source in Streamkap -- the connector resumes from the GTID position GTID provides a global transaction identifier that survives the upgrade, allowing the connector to resume from exactly where it left off. Without GTID, MySQL uses binlog file and position tracking. Major upgrades can change binlog filenames and positions, which means: 1. Stop writes to the database 2. Allow the connector to capture all remaining events and confirm with Streamkap 3. Stop the Source in Streamkap 4. Perform the upgrade 5. Verify all CDC-related settings (see table above) 6. Resume the Source in Streamkap 7. Trigger a [snapshot](/snapshots) for all affected tables, as the binlog position from before the upgrade may no longer be valid Without GTID, there is a higher risk of data gaps during the upgrade. We strongly recommend enabling GTID before upgrading if possible. See [Enable GTID](/amazon-rds-mysql#method-1-enable-gtid-recommended) for instructions. **MySQL 5.7 to 8.0 specific notes:** * MySQL 8.0 changes the default authentication plugin to `caching_sha2_password`. If your Streamkap user was created with `mysql_native_password`, verify it still authenticates correctly after the upgrade * Review binlog retention settings after upgrade: `CALL mysql.rds_show_configuration;` (RDS) or `SHOW VARIABLES LIKE 'binlog_expire_logs_seconds';` (self-hosted) For full MySQL source setup details, see: * [Amazon RDS MySQL](/amazon-rds-mysql) * [MySQL (Generic)](/mysql-generic) * [MySQL Source FAQ](/mysql-source-faq) ### General Upgrade Considerations Streamkap's Oracle connector uses LogMiner to read redo and archive logs for CDC. After upgrading your Oracle database, verify the following: **Post-upgrade verification checklist:** 1. **Supplemental logging is enabled** -- Oracle upgrades may reset supplemental logging settings ```sql SQL theme={null} -- Verify database-level supplemental logging SELECT NAME, SUPPLEMENTAL_LOG_DATA_MIN FROM V$DATABASE; -- SUPPLEMENTAL_LOG_DATA_MIN should be YES ``` 2. **Table-level supplemental logging is intact** -- Verify for each captured table ```sql SQL theme={null} -- Check table-level supplemental logging SELECT * FROM ALL_LOG_GROUPS WHERE TABLE_NAME = '{TABLE_NAME}'; ``` 3. **ARCHIVELOG mode is still enabled** ```sql SQL theme={null} archive log list; -- Database log mode should be: Archive Mode ``` 4. **Redo log configuration is unchanged** -- Verify redo log sizes and groups ```sql SQL theme={null} SELECT GROUP#, BYTES/1024/1024 SIZE_MB, STATUS FROM V$LOG ORDER BY 1; ``` 5. **Streamkap user privileges are intact** -- Verify the `STREAMKAP_USER` (or `C##STREAMKAP_USER` for CDB) retains all necessary grants, including `LOGMINING`, `FLASHBACK ANY TABLE`, and access to `V$` views **Upgrade procedure:** 1. Stop writes to the database 2. Allow the connector to capture all remaining events 3. Stop the Source in Streamkap 4. Perform the Oracle upgrade using your standard procedure 5. Verify all items in the checklist above 6. Re-enable supplemental logging if needed (`ALTER DATABASE ADD SUPPLEMENTAL LOG DATA;`) 7. Resume the Source in Streamkap 8. Trigger a [snapshot](/snapshots) if you suspect any events were missed For full Oracle source setup details, see: * [Oracle (Generic)](/oracle-generic) * [Oracle on Amazon RDS](/amazon-rds-oracle) * [Oracle Source FAQ](/oracle-source-faq) ### General Upgrade Considerations SQL Server CDC relies on change tracking tables and the SQL Server Agent service. Upgrades can disrupt both of these. **Post-upgrade verification checklist:** 1. **CDC is still enabled on the database** ```sql SQL theme={null} SELECT name, is_cdc_enabled FROM sys.databases WHERE name = '{database}'; -- is_cdc_enabled should be 1 ``` 2. **CDC capture jobs are running** -- SQL Server CDC depends on capture and cleanup jobs ```sql SQL theme={null} EXEC sys.sp_cdc_help_jobs; ``` 3. **SQL Server Agent is running** -- The Agent must be active for CDC capture jobs to execute. If it is not running, start it through SQL Server Management Studio or via service management 4. **CDC is enabled on all captured tables** -- Verify each table still has CDC tracking enabled ```sql SQL theme={null} EXEC sys.sp_cdc_help_change_data_capture; ``` 5. **Streamkap user permissions are intact** -- Verify the `streamkap_user` retains `SELECT` privileges on the `cdc` schema and source tables **Upgrade procedure:** 1. Stop writes to the database 2. Allow the connector to capture all remaining events 3. Stop the Source in Streamkap 4. Perform the SQL Server upgrade 5. Verify all items in the checklist above 6. Re-enable CDC on the database and tables if needed 7. Ensure SQL Server Agent is running 8. Resume the Source in Streamkap 9. Trigger a [snapshot](/snapshots) if you suspect any events were missed If CDC is disabled during the upgrade process (either intentionally or by the upgrade procedure), you must re-enable it on both the database and each individual table. Change events that occur while CDC is disabled will not be captured. For full SQL Server source setup details, see: * [SQL Server (Generic)](/sql-server-generic) * [SQL Server on Amazon RDS](/amazon-rds-sql-server) * [SQL Server on Google Cloud SQL](/sql-server-on-google-cloud-sql) * [Azure SQL Database](/sql-server-on-azure-sql-database) * [SQL Server Source FAQ](/sql-server-source-faq) ### General Upgrade Considerations Streamkap's MongoDB connector uses change streams (backed by the oplog) for CDC. MongoDB upgrade behavior depends on your deployment type and the version jump. **Replica set rolling upgrades** are CDC-friendly: * Rolling upgrades (upgrading secondary nodes first, then stepping down the primary) allow the connector to continue reading change streams with minimal interruption * The connector automatically reconnects to the new primary after a stepdown **Key risks during major version upgrades:** * Change stream resume tokens may not survive major version upgrades if the oplog format changes * If the resume token is invalidated, the connector cannot resume from its last position **Post-upgrade verification checklist:** 1. **Oplog size is sufficient** -- Verify the oplog window is large enough to cover the upgrade duration plus a buffer 2. **Replica set is healthy** -- All members should be in a healthy state ```javascript theme={null} rs.status() ``` 3. **Streamkap user privileges are intact** -- Verify the `streamkap_user` retains `readAnyDatabase` and `read` on the `local` database 4. **Change streams are functional** -- Verify the connector can open a change stream on the target collections **Upgrade procedure:** 1. Verify your oplog window is large enough to cover the expected upgrade duration 2. If performing a rolling upgrade, allow the connector to continue running during secondary node upgrades 3. Before stepping down the primary, allow the connector to capture all pending events 4. Stop the Source in Streamkap if the resume token may be invalidated (major version upgrade) 5. Complete the upgrade 6. Verify all items in the checklist above 7. Resume the Source in Streamkap 8. Trigger a [snapshot](/snapshots) if the resume token was invalidated or you suspect data gaps For full MongoDB source setup details, see: * [MongoDB Atlas](/mongodb-atlas) * [MongoDB (Generic)](/mongodb-1) * [MongoDB Source FAQ](/mongodb-source-faq) ## Post-Upgrade Verification Checklist After completing the upgrade for any database type, verify the following: 1. The pipeline reconnects and shows a **RUNNING** status in the [Streamkap app](https://app.streamkap.com/connectors?tab=Sources) 2. New data changes (inserts, updates, deletes) are being captured -- insert a test row and verify it appears at the destination 3. Consumer group lag is decreasing steadily 4. Check the [DLQ (Dead Letter Queue)](/dlq-operations) for any error messages related to the upgrade 5. Compare row counts between source and destination for key tables to identify any gaps 6. Monitor the pipeline for 24-48 hours for any delayed issues ## When to Snapshot A full snapshot is required in specific situations. Trigger a [snapshot](/snapshots) if any of the following apply: | Scenario | Database | Reason | | ------------------------------- | -------------------- | ----------------------------------------------------------------------------------------- | | Replication slot was dropped | PostgreSQL | Major upgrades drop logical replication slots; new slots only track changes from creation | | Binlog position was lost | MySQL (without GTID) | Binlog file names and positions can change during major upgrades | | Resume token was invalidated | MongoDB | Major version upgrades may change oplog format, invalidating existing resume tokens | | CDC was disabled during upgrade | SQL Server | Change events are not captured while CDC is disabled | | Supplemental logging was reset | Oracle | Events during the period without supplemental logging are not captured | | Data gaps are suspected | Any | If you cannot confirm all events were captured before the upgrade | If all change events were not captured before stopping the Source and upgrading the database, you can perform a snapshot after the upgrade is completed to ensure no change events were missed. See [Snapshots & Backfilling](/snapshots) for detailed snapshot procedures. ## Cloud-Managed Database Considerations Cloud-managed database upgrades (Amazon RDS, Aurora, Google Cloud SQL, Azure) vary depending on the provider's upgrade mechanism (in-place, blue/green deployment, read replica promotion, etc.). The general procedures in this guide apply, but consult your cloud provider's documentation for specific upgrade behavior. Key points for managed database upgrades: * **Amazon RDS / Aurora**: Managed upgrades may handle some infrastructure steps automatically, but replication slots (PostgreSQL) and CDC configuration (SQL Server) still need manual verification * **Google Cloud SQL**: Similar to RDS -- verify CDC-related parameters in your database flags after the upgrade * **Azure Database**: Check that server parameters and CDC settings are preserved through the upgrade process * **MongoDB Atlas**: Atlas manages rolling upgrades automatically for replica sets, but verify your connector resumes correctly after the upgrade completes # Databricks Delta Lake Source: https://docs.streamkap.com/databricks-delta-lake # Streamkap Setup To set up the Connector, you will need to gather connection details and configure your DataBricks cluster. Log in to your [Databricks Cloud Account](https://accounts.cloud.databricks.com) and then follow the steps below. ## Get connection details Streamkap connects to Databricks via a JDBC URL. You can use either an [**All-Purpose Compute**](https://docs.databricks.com/aws/en/compute#classic-compute) or a [**SQL Warehouse**](https://docs.databricks.com/en/compute/sql-warehouse/index.html) as the compute resource. ### Option A: All-Purpose Compute 1. Open the **Compute** page from the sidebar and choose your cluster 2. Click on **Advanced Options** 3. Open the **JDBC/ODBC** tab 4. Copy the JDBC Connection URL ### Option B: SQL Warehouse A [SQL Warehouse](https://docs.databricks.com/en/compute/sql-warehouse/index.html) can automatically scale across multiple Spark clusters to handle concurrent workloads, but is generally more expensive than an All-Purpose Cluster. To get the JDBC URL for a SQL Warehouse: 1. Open the **SQL Warehouses** page from the sidebar 2. Select your warehouse 3. Open the **Connection Details** tab 4. Copy the **JDBC URL** For both options, you can append `ConnCatalog=` to the JDBC URL to select a catalog other than the default. ## Generate an access token For setting the Streamkap DataBricks' Token: 1. Open **Settings** page from the sidebar and then **User Settings** 2. Open the **Personal Access Tokens** tab 3. Click **+ Generate New Token** 4. (Optional) Enter a comment and change the token lifetime 5. Click **Generate** 6. Copy the access token ## Create a temporary directory 1. Create `tmp` directory on the Databricks File System (DBFS) # How it works As data's streamed from the source in to topics (think of them as partitioned tables), the Databricks Sink connector will: * Check whether tables for the topics exist in Databricks, if not, it creates them * Automatically handle [schema evolution](/schema-evolution-support) when the source schema changes (e.g. new columns, data type changes) * Stream change data into Parquet files and upload them to the `tmp` directory on the Databricks File System (DBFS) and: * Load data to the target table using SQL bulk import `COPY` * Clean up the Parquet files ## Ingestion Modes Streamkap supports two ingestion modes for writing data to Databricks Delta Lake: **Upsert** and **Append**. ### Upsert Upsert mode uses a `MERGE INTO` statement to insert new records and update existing ones based on the primary key columns from the source table. * **New records** (no matching primary key in the target) are inserted * **Existing records** (matching primary key) are updated with the latest values * **Deleted records** (when hard delete is enabled) are physically removed from the target table * **Out-of-order protection:** Streamkap tracks record timestamps and offsets to ensure older records never overwrite newer data Upsert is the recommended mode for most use cases, as it keeps your target table in sync with the source and handles updates and deletes automatically. ### Append Append mode uses a simple `INSERT INTO` statement to add all incoming records as new rows. * Every record is inserted regardless of whether a row with the same key already exists * No deduplication or update logic is applied * Deletes from the source are not reflected in the target Append is useful for event logs, audit trails, or any scenario where you want to preserve every change as a separate row rather than maintaining a current-state replica. # Db2 (Generic) Source: https://docs.streamkap.com/db2-generic # Prerequisites **IBM InfoSphere Data Replication License required** The connector requires the use of the abstract syntax notation (ASN) libraries, which are available as a standard part of Db2 for Linux. To use the ASN libraries, you must have a license for IBM InfoSphere Data Replication (IIDR). You do not have to install IIDR to use the ASN libraries. * Db2 version ≥ 11.5 * `db2inst1` user credentials # Db2 Setup The Connector is based on the ASN Capture/Apply agents that enable SQL Replication in Db2. A capture agent: * Generates change-data tables for tables that are in capture mode. * Monitors tables in capture mode and stores change events for updates to those tables in their corresponding change-data tables. The Connector uses a SQL interface to query change-data tables for change events. ## Granting Privileges It's recommended to create a separate user and role for Streamkap to access your Db2 database. Below is an example script that does that. ```SQL SQL theme={null} -- Replace { ... } placeholders as required CREATE USER STREAMKAP_USER IDENTIFIED BY '{password}'; CREATE ROLE STREAMKAP_ROLE; -- Grant Streamkap permissions on the database, schema and all tables to capture GRANT CONNECT ON DATABASE TO STREAMKAP_ROLE; -- Grant SELECT on all tables Streamkap should capture GRANT SELECT ON {schemaName}.{tableName} TO STREAMKAP_ROLE; GRANT ROLE STREAMKAP_ROLE TO USER STREAMKAP_USER; ``` ## Enable Snapshots You will need to create the table in the source database and give permissions to the `STREAMKAP_USER`. Streamkap will use this table for managing snapshots. The examples below use `STREAMKAP_SIGNAL` as the signal table name, but you can choose any name. During [Streamkap Setup](#3-snapshot-settings), provide the full path to your signal table in `schema.table` format (e.g., `STREAMKAP.STREAMKAP_SIGNAL`). ```SQL SQL theme={null} -- Create the schema CREATE SCHEMA STREAMKAP; -- Create the table within the schema CREATE TABLE STREAMKAP.STREAMKAP_SIGNAL ( id VARCHAR(255) PRIMARY KEY, type VARCHAR(32) NOT NULL, data VARCHAR(2000) NULL ); -- Grant necessary privileges on the table to the role GRANT SELECT, UPDATE, INSERT ON STREAMKAP.STREAMKAP_SIGNAL TO STREAMKAP_ROLE; ``` ## Enable Change Data Capture To put tables into capture mode, Debezium provides a set of user-defined functions (UDFs) for your convenience. The procedure here shows how to install and run these management UDFs. Alternatively, you can run Db2 control commands to put tables into capture mode. The administrator must then enable CDC for each table that you want Streamkap to capture. ### Prerequisites * You are logged in to Db2 as the `db2inst1` user * On the Db2 host, the Debezium management UDFs are available in the `$HOME/asncdctools/src` directory. UDFs are available from the Debezium [examples](https://github.com/debezium/debezium-examples/tree/main/tutorial/debezium-db2-init/db2server) repository * The Db2 command `bldrtn` is on `PATH`, e.g. by running `export PATH=$PATH:/opt/ibm/db2/V11.5.0.0/samples/c/` with Db2 11.5 Compile the Debezium management UDFs on the Db2 server host by using the `bldrtn` command provided with Db2: ```bash Shell theme={null} cd $HOME/asncdctools/src bldrtn asncdc ``` Ensure that JDBC can read the Db2 metadata catalog: ```bash Shell theme={null} # Replace the {...} placeholders as required cd $HOME/sqllib/bnd db2 connect to {databaseName} db2 bind db2schema.bnd blocking all grant public sqlerror continue ``` Ensure that the database was recently backed-up. The ASN agents must have a recent starting point to read from. If you need to perform a backup, run the following commands, which prune the data so that only the most recent version is available. If you do not need to retain the older versions of the data, specify `dev/null` for the backup location. ```bash Shell theme={null} # Replace the {...} placeholders as required db2 backup db {databaseName} to {backupLocation} db2 restart db {databaseName} ``` Connect to the database to install the Debezium management UDFs. It is assumed that you are logged in as the `db2inst1` user so the UDFs should be installed on the `db2inst1` user. Copy the Debezium management UDFs and set permissions for them: ```bash Shell theme={null} # Replace the {...} placeholders as required db2 connect to {databaseName} cp $HOME/asncdctools/src/asncdc $HOME/sqllib/function chmod 777 $HOME/sqllib/function ``` Enable the Debezium UDF that starts and stops the ASN capture agent: ```bash Shell theme={null} db2 -tvmf $HOME/asncdctools/src/asncdc_UDF.sql ``` Create the ASN control tables: ```bash Shell theme={null} $ db2 -tvmf $HOME/asncdctools/src/asncdctables.sql ``` Enable the Debezium UDF that adds tables to capture mode and removes tables from capture mode: ```bash Shell theme={null} $ db2 -tvmf $HOME/asncdctools/src/asncdcaddremove.sql ``` After you set up the Db2 server, use the UDFs to control Db2 replication (ASN) with SQL commands. Some of the UDFs expect a return value in which case you use the SQL `VALUE` statement to invoke them. For other UDFs, use the SQL `CALL` statement. Start the ASN agent: ```SQL SQL theme={null} VALUES ASNCDC.ASNCDCSERVICES('start','asncdc'); ``` Put tables into capture mode. Invoke the following statement for each table that you want to put into capture: ```SQL SQL theme={null} -- Replace {...} placeholders as required CALL ASNCDC.ADDTABLE('{schemaName}', '{tableName}'); ``` Reinitialize the ASN service: ```SQL SQL theme={null} VALUES ASNCDC.ASNCDCSERVICES('reinit','asncdc'); ``` Grant the `STREAMKAP_USER` privileges on the ASN CDC tables: ```SQL SQL theme={null} GRANT SELECT ON TABLE ASNCDC.IBMQREP_COLVERSION TO ROLE STREAMKAP_ROLE; GRANT SELECT ON TABLE ASNCDC.IBMQREP_TABVERSION TO ROLE STREAMKAP_ROLE; GRANT SELECT ON TABLE ASNCDC.IBMSNAP_APPLEVEL TO ROLE STREAMKAP_ROLE; GRANT SELECT ON TABLE ASNCDC.IBMSNAP_CAPMON TO ROLE STREAMKAP_ROLE; GRANT SELECT ON TABLE ASNCDC.IBMSNAP_CAPPARMS TO ROLE STREAMKAP_ROLE; GRANT SELECT ON TABLE ASNCDC.IBMSNAP_CAPSCHEMAS TO ROLE STREAMKAP_ROLE; GRANT SELECT ON TABLE ASNCDC.IBMSNAP_CAPTRACE TO ROLE STREAMKAP_ROLE; GRANT SELECT ON TABLE ASNCDC.IBMSNAP_PRUNCNTL TO ROLE STREAMKAP_ROLE; GRANT SELECT ON TABLE ASNCDC.IBMSNAP_PRUNE_LOCK TO ROLE STREAMKAP_ROLE; GRANT SELECT ON TABLE ASNCDC.IBMSNAP_PRUNE_SET TO ROLE STREAMKAP_ROLE; GRANT SELECT ON TABLE ASNCDC.IBMSNAP_REGISTER TO ROLE STREAMKAP_ROLE; GRANT SELECT ON TABLE ASNCDC.IBMSNAP_RESTART TO ROLE STREAMKAP_ROLE; GRANT SELECT ON TABLE ASNCDC.IBMSNAP_SIGNAL TO ROLE STREAMKAP_ROLE; GRANT SELECT ON TABLE ASNCDC.IBMSNAP_SUBS_COLS TO ROLE STREAMKAP_ROLE; GRANT SELECT ON TABLE ASNCDC.IBMSNAP_SUBS_MEMBR TO ROLE STREAMKAP_ROLE; GRANT SELECT ON TABLE ASNCDC.IBMSNAP_SUBS_SET TO ROLE STREAMKAP_ROLE; GRANT SELECT ON TABLE ASNCDC.IBMSNAP_SUBS_STMTS TO ROLE STREAMKAP_ROLE; GRANT SELECT ON TABLE ASNCDC.IBMSNAP_UOW TO ROLE STREAMKAP_ROLE; GRANT SELECT ON TABLE ASNCDC.IBMSNAP_CAPENQ TO ROLE STREAMKAP_ROLE; ``` # Streamkap Setup * Go to [Sources](https://app.streamkap.com/connectors/sources), click Add and choose **Db2** * Input the following information: * Name for your Connector * Hostname - IP address or hostname of the Db2 database server * Port (default: `50000`) - Port number of the Db2 database server * Username - Name of the Db2 database user for connecting to the Db2 database server * Password - Password to use when connecting to the Db2 database server * Database Name - The name of the Db2 database from which to stream the changes * Signal Table - Full path to the signal table as `schema.table` (e.g., `STREAMKAP.STREAMKAP_SIGNAL`). The database name will be added automatically. This table is used for incremental snapshotting. See [Enable Snapshots](#enable-snapshots) for setup instructions. * Connect via SSH Tunnel. See [SSH Tunnel](/ssh-tunnel) * Advanced Parameters * Capture Only Captured Databases DDL (Default `false`) - Specifies whether the connector records schema structures from all logical databases in the database instance or only captured databases. Enabling this when you have many databases in your instance can improve performance and avoid timeouts. See [Schema History Optimization](/schema-history-optimization) for details. * Capture Only Captured Tables DDL (Default `false`) - Specifies whether the connector records schema structures from all logical tables in the captured schemas or databases, or only captured tables. Enabling this when you have many tables can improve performance and avoid timeouts. See [Schema History Optimization](/schema-history-optimization) for details. * Add Schemas/Tables. Can also bulk upload here. The format is a simple list of each schema or table per row saved in csv format without a header. **CDC only captures base tables, not Views** Change Data Capture reads Db2 change-data tables, which only track changes to physical base tables. Database Views are query-time computations with no physical storage—they don't have change-data tables. **What you cannot capture:** Views, materialized query tables (MQTs), declared global temporary tables, created global temporary tables, external tables, or system catalog tables. **Solution:** Specify only the underlying base tables that feed your views. You can recreate the view logic in your destination or transformation layer. * Click Save # Db2 Schema Evolution While the Connector can capture schema changes, to update a schema, you must collaborate with a database administrator to ensure that the connector continues to produce change events. This is required because of how Db2 implements replication. For each table in capture mode, the replication feature in Db2 creates a change-data table that contains all changes to that source table. However, change-data table schemas are static. If you update the schema for a table in capture mode then you must also update the schema of its corresponding change-data table. The Connector cannot do this. A database administrator with elevated privileges must update schemas for tables that are in capture mode. ## Refresh change table structure (Online) **Online refresh limitation** In the interval between the source table structure changing in the source database, and \_before \_the change table structure is refreshed, change events continue to be captured with the **outdated** table structure. For example, if you added a new column to a source table, change events that are captured before the change table is refreshed will not contain the new column. If this cannot be tolerated, an **Offline** refresh has to be performed. However, that means downtime for whatever system made structural changes to your tables **and** your Streamkap pipelines. Please contact us for assistance if an Offline refresh is required. An online schema update does not require application and data processing downtime. That is, you do not stop the Connector before you perform an online schema update. Also, an online schema update procedure is simpler than the procedure for an offline schema update. However, when a table is in capture mode, after a change to a column name, the Db2 replication feature continues to use the old column name. The new column name does not appear in the change events. You must restart the Connector to see the new column name in change events. ### Procedure when adding a column to the end of a table 1. Lock the source tables whose schema you want to change 2. In the ASN register table, mark the locked tables as `INACTIVE` 3. Reinitialize the ASN capture service 4. Apply all changes to the schemas for the source tables 5. Apply all changes to the schemas for the corresponding change-data tables 6. In the ASN register table, mark the source tables as `ACTIVE` 7. Reinitialize the ASN capture service 8. Restart the connector to see updated column names in change events ### Procedure when adding a column to the middle of a table 1. Lock the source table(s) to be changed 2. In the ASN register table, mark the locked tables as `INACTIVE` 3. Reinitialize the ASN capture service 4. For each source table to be changed: 1. Export the data in the source table 2. Truncate the source table 3. Alter the source table and add the column 4. Load the exported data into the altered source table 5. Export the data in the source table’s corresponding change-data table 6. Truncate the change-data table 7. Alter the change-data table and add the column 8. Load the exported data into the altered change-data table 5. In the ASN register table, mark the tables as `INACTIVE`. This marks the old change-data tables as inactive, which allows the data in them to remain but they are no longer updated 6. Reinitialize the ASN capture service 7. Restart the connector to see updated column names in change events # Reference ## Debezium Management UDFs and Db2 ASN replication If you followed our [Db2 (Generic) - Enable Change Data Capture](/db2-generic#enable-change-data-capture) guide you can use these Debezium management UDFs to control Db2 replication (ASN) with SQL commands. | Description | Command and notes | | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Start the ASN agent | `VALUES ASNCDC.ASNCDCSERVICES('start','asncdc');` | | Stop the ASN agent | `VALUES ASNCDC.ASNCDCSERVICES('stop','asncdc');` | | Check the status of the ASN agent | `VALUES ASNCDC.ASNCDCSERVICES('status','asncdc');` | | Enable change data capture mode on a table | `CALL ASNCDC.ADDTABLE('{SCHEMA}', '{TABLE}');`

Replace `{SCHEMA}` with the name of the schema that contains the table you want to put into capture mode. Likewise, replace `{TABLE}` with the name of the table to put into capture mode. | | Disable change data capture mode on a table | `CALL ASNCDC.REMOVETABLE('{SCHEMA}', '{TABLE}');` | | Reinitialize the ASN capture service | `VALUES ASNCDC.ASNCDCSERVICES('reinit','asncdc');`

Do this after you put a table into capture mode or after you remove a table from capture mode. | # Deployment Options Source: https://docs.streamkap.com/deployment-options Choose between Streamkap Cloud and Bring Your Own Cloud (BYOC) deployment models Streamkap supports the following deployment options: | Option | Description | | :-------------------------------------------------------- | :--------------------------------------------------------------------------- | | [Streamkap Cloud](/streamkap-cloud) | The fastest and easiest way to get started. | | [Bring Your Own Cloud (BYOC)](/bring-your-own-cloud-byoc) | Best suited for companies who can't have data leave their cloud environment. | **Security Compliance** Streamkap has SOC2 Type 2 Compliance and also provides a number of Enterprise-grade security features. To find out more, see the [Security Posture](/security-posture) page. ## Related Documentation * [Disaster Recovery](/disaster-recovery) - Understand Streamkap's disaster recovery capabilities and procedures * [Connection Options](/connection-options) - Network connectivity options for connecting to your sources and destinations # Destinations Source: https://docs.streamkap.com/destinations Destinations refer to data connectors we will send data to. On this page you can create new connectors or view the status of existing ones. Functionality * Create * View existing Destinations & Quick Actions * Create & Edit Authentication * Settings * Status * Config History ## View Existing Destinations * Shows types, quantity of topics latency * Able to search and filter the list ### Row Actions Menu Click the actions menu (⋮) on any destination row to access: * **View**: Open the destination detail page * **Copy ID**: Copy the destination UUID to clipboard (useful for API calls and support tickets) * **Clone**: Create a copy of this destination with the same configuration * **Logs**: View logs for this destination connector * **Restart**: Stop and resume the connector without clearing offsets (useful for recovering from a stuck or errored state) * **Reset**: Clear all offsets and replay up to 7 days of data **Resetting** Resetting a Destination will clear all offsets and replay up to 7 days of data. This may cause duplicate events in downstream systems. It may also trigger consumer lag alerts until the data backlog is cleared. This is a destructive action and should be used with caution. You will be prompted to confirm this action. * **Delete**: Remove this destination connector (requires confirmation) Row actions menu showing View, Copy ID, Clone, Logs, Restart, Reset, and Delete options ## Bulk Actions Select multiple destinations using the checkboxes on the left side of the table to perform actions on multiple destinations at once. When destinations are selected, the Bulk Actions menu becomes available. Available bulk actions: * **Stop**: Stop all selected destinations from consuming data * **Resume**: Resume data consumption for all selected stopped destinations * **Reset**: Clear offsets and replay data for all selected destinations (requires confirmation) * **Delete**: Remove all selected destinations (requires confirmation) You can select individual destinations or use "Select all X matching destination" to select all destinations in the current filtered view. Bulk actions are applied to all selected destinations simultaneously. Use caution with destructive actions like Reset and Delete, as they can cause data replay and potential duplicates in downstream systems. ## Create & Edit Authentication ## Settings Varies per connector but you will find values to configure such as metadata to create ## Status * View headline statistics such as Latency * View volume, events retries and snapshot status per table/topic * Trigger a new snapshot/backfill per connecor or table/topics ## Config History The Config History tab provides a complete timeline of all configuration changes made to your connector. This feature helps you track changes over time, understand their impact, and troubleshoot issues. **What's Tracked:** * Kafka Connect configuration changes that affect data flow * Changes made during connector creation and edits * Both successful and failed configuration updates * Note: Metadata updates (name, tags, description) are not included **Key Features:** * **Timeline View** - Chronological list of all configuration changes with timestamps * **Status Indicators** - Visual indicators showing successful (green) and failed (red) updates * **AI-Generated Summaries** - Streamkap AI analyzes each change and provides: * A clear explanation of what was modified * Potential impact on your data pipeline * Relevant warnings about performance or data flow changes * **Sync Delay Notice** - Recent changes may take a few minutes to appear as the system syncs **Example Use Cases:** * Investigate when a configuration change caused an issue * Review the history of schema or table inclusion changes * Understand how settings like snapshot parallelism or table lists have evolved * Document configuration changes for compliance or audit purposes # Disaster Recovery & Failover Source: https://docs.streamkap.com/disaster-recovery Handling outages and recovery procedures for Streamkap CDC pipelines across sources, destinations, and platform components. Change Data Capture (CDC) pipelines involve multiple components: a source database, Kafka, the Streamkap platform, and a destination. Each of these can experience outages independently. This page covers what happens during each type of failure and how to recover. ## Streamkap's Built-in Resilience Streamkap includes several mechanisms that protect against data loss and enable recovery without manual intervention in many scenarios: | Mechanism | Description | | :-------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Kafka buffering** | Data captured from sources is persisted in Kafka topics. If a destination goes offline, messages accumulate in Kafka and are delivered once the destination recovers. | | **Automatic retry** | Destination connectors automatically retry failed writes with backoff, handling transient errors without operator intervention. | | **Dead letter queue (DLQ)** | Messages that cannot be processed after retries are routed to a [dead letter queue](/dlq-operations) rather than blocking the pipeline. Healthy data continues to flow. | | **Offset tracking** | Each pipeline tracks its position (offset) in the Kafka topic. When a pipeline restarts, it resumes from the last successfully committed offset, preventing data loss. | | **Incremental snapshots** | If a pipeline falls too far behind or a source is rebuilt, [snapshots](/snapshots) can re-backfill historical data without interrupting real-time streaming. | Streamkap provides **at-least-once** delivery semantics. Every change event is delivered at least once, but in failure or recovery scenarios, some events may be delivered more than once. **Upsert mode** handles duplicates automatically via primary key deduplication. **Insert/append mode** may result in duplicate rows — use [metadata columns](/metadata) to deduplicate at query time. ## Failure Scenarios **What happens:** * The source connector detects the connection loss and enters an error or retry state. * The pipeline status changes to **Broken** in the Streamkap UI. * No new change events are captured while the source is unavailable. * Data already in Kafka continues to flow to the destination. **Recovery:** 1. Restore the source database and verify it is accepting connections. 2. The source connector automatically attempts to reconnect. Once the source is reachable, the connector resumes reading from its last known position in the change log (WAL, binlog, oplog, or change stream). 3. If the source was rebuilt from a backup or replica and the change log position is no longer available, you may need to trigger a [snapshot](/snapshots) to backfill affected tables. **PostgreSQL:** Ensure the replication slot is preserved during any source database failover or maintenance. If the replication slot is dropped, the connector cannot resume from its previous position and a full snapshot is required. See the [PostgreSQL source documentation](/postgresql) for replication slot configuration. **What happens:** * Data continues to be captured from the source and buffered in Kafka topics. * The destination connector retries writes automatically. * Messages that fail after all retries are routed to the [DLQ](/dlq-operations). * No data loss is expected as long as the outage duration is within the Kafka retention period. **Recovery:** 1. Restore the destination and verify it is accepting connections and writes. 2. The destination connector automatically resumes writing buffered data from Kafka. 3. Monitor [consumer group lag](/consumer-groups) after recovery to confirm the pipeline is catching up. 4. Check the [DLQ](/dlq-operations) for any messages that failed during the outage window and resolve them. During recovery, you may observe temporarily elevated lag as the pipeline processes the backlog of buffered messages. This is expected and will decrease as the pipeline catches up. **What happens:** * Pipeline processing pauses entirely. No data is captured from sources or written to destinations. * Source databases continue to accumulate changes in their change logs (WAL, binlog, oplog). **Recovery:** * For [Streamkap Cloud](/streamkap-cloud) deployments, recovery is managed by the Streamkap team with 24/7 monitoring. * Once the platform recovers, pipelines automatically resume from their last committed offsets. * Source connectors reconnect and read accumulated change log entries. When a source database undergoes a planned or unplanned failover (e.g., primary to replica promotion), the behavior depends on the database type: **PostgreSQL:** * Replication slots are tied to the primary instance. After failover to a standby, the slot typically needs to be recreated on the new primary. * If the slot is lost, the connector cannot resume from its previous position. A snapshot may be required. * Consider using logical replication slot failover capabilities if your PostgreSQL version and HA solution support them. **MySQL:** * If GTID-based replication is enabled and GTIDs are consistent across the primary and replica, the connector can resume seamlessly after failover. * Without GTIDs, the connector relies on binlog file and position, which may not transfer across instances. A snapshot may be required. **MongoDB:** * Change stream resume tokens allow seamless failover within a replica set. When the primary changes, the connector resumes from its last resume token automatically. * For sharded clusters, each shard's change stream resumes independently. **What happens:** * The pipeline detects connectivity loss and enters a retry loop. * Transient interruptions (seconds to minutes) are typically handled automatically. **Recovery:** * **Transient interruptions:** The pipeline reconnects and resumes automatically once connectivity is restored. No action needed. * **Extended interruptions:** If connectivity is not restored within the retry window, the pipeline may transition to a **Broken** state. 1. Verify network connectivity between Streamkap and your source/destination. 2. Check VPN or [PrivateLink](/connection-options) status if applicable. 3. Once connectivity is restored, restart the pipeline from the Streamkap UI if it does not resume automatically. For [Bring Your Own Cloud (BYOC) deployments](/bring-your-own-cloud-byoc), verify that the VPN tunnel between the control plane and data plane is active, and that the data plane can reach your source and destination endpoints. ## Post-Outage Verification Checklist After recovering from any outage, work through this checklist to confirm your pipelines are healthy: Navigate to the [Pipelines](/pipelines) page and confirm all affected pipelines show an **Active** (green) status. Open the [Consumer Groups](/consumer-groups) page and verify that lag for affected consumer groups is **decreasing**. Sustained or increasing lag after recovery indicates a problem. Query your destination to confirm new data is arriving. Check the latest timestamps on recently updated tables to verify freshness. Check the [DLQ](/dlq-operations) for messages that failed during the outage or recovery window. Resolve or replay any failed messages. For critical tables, compare row counts between the source database and the destination to confirm they are in sync. If discrepancies exist, consider triggering a [snapshot](/snapshots) for the affected tables. ## Configuration Backup & Recovery Maintaining recoverable pipeline configurations reduces the time and effort needed to rebuild after a major incident. ### API Export Pipeline and connector configurations can be retrieved programmatically via the [Streamkap API](/api). Use the API to export configuration state for backup purposes or to recreate resources in a new environment. ### Terraform (Recommended) The [Streamkap Terraform Provider](/streamkap-provider-for-terraform) enables you to define all sources, destinations, pipelines, and transforms as infrastructure-as-code. This provides: * **Version-controlled configuration** tracked in Git alongside your application code * **Reproducible deployments** that can recreate your entire pipeline topology from code * **Disaster recovery** by re-applying Terraform configurations to a new environment * **Change auditing** through standard Git history and pull request reviews Maintain your Streamkap Terraform configurations in version control and treat them as the source of truth for your pipeline infrastructure. This enables rapid recovery by re-applying configurations if resources need to be recreated. For setup instructions, see: * [Terraform Getting Started](/streamkap-provider-for-terraform) * [Terraform Configuration](/terraform-configuration) * [Terraform Resources](/terraform-resources) ## Related Documentation * [Pipelines](/pipelines) - Monitor pipeline status and manage data flow * [Snapshots & Backfilling](/snapshots) - Re-backfill data after outages or schema changes * [Deployment Options](/deployment-options) - Streamkap Cloud and BYOC deployment models # Dead Letter Queue (DLQ) Source: https://docs.streamkap.com/dlq-operations Monitor, inspect, and resolve messages that failed processing in your Streamkap pipelines. When a message cannot be processed by downstream consumers or fails during a transform, Streamkap routes it to a **dead letter queue (DLQ) topic** rather than blocking the entire pipeline. This ensures that healthy data continues to flow while problematic messages are captured for inspection and resolution. For a brief overview of DLQ topics and how they appear in the Topics page, see [Topics](/topics). Messages in the DLQ represent data that did NOT reach the destination. Until the root cause is resolved and data is backfilled, DLQ records represent data loss. Investigate DLQ messages promptly. ## How the DLQ Works When Streamkap encounters a message that cannot be processed, the following happens: 1. The message is diverted from the normal data flow into a dedicated DLQ topic 2. Error metadata is attached to the message as Kafka headers 3. The pipeline continues processing remaining messages without interruption 4. The DLQ message is retained for inspection and root-cause analysis DLQ routing is automatic. You do not need to configure it. Every destination connector and transform has a DLQ topic created when errors occur. ## Finding DLQ Topics ### Naming Convention DLQ topics follow a predictable naming pattern based on the connector type: | Connector Type | DLQ Topic Pattern | Example | | --------------- | --------------------------------------------------- | ---------------------------------------------- | | Destination | `destination_{entity_id}.streamkap.deadletterqueue` | `destination_abc123.streamkap.deadletterqueue` | | Transform | `{transform_output_topic_prefix}deadletterqueue` | `my_transform.streamkap.deadletterqueue` | | Streaming Agent | `dlq.{output_topic}` | `dlq.orders_enriched` | Streaming agents use the `dlq.` prefix rather than the `deadletterqueue` suffix. To find an agent's DLQ, search the Topics page for `dlq.`. See [Manage Agents — Dead-letter topic](/streaming-agents-manage#dead-letter-topic). ### Filtering in the Topics Page 1. Navigate to the **Topics** page 2. Check the **Include DLQ** checkbox in the top toolbar to show DLQ topics 3. Use the **Search Box** to filter by `deadletterqueue` 4. Optionally, use the **By Destination** or **By Transform** collection in the left sidebar to narrow results 5. Check the **Errors** column for non-zero values to identify active DLQ topics Use the **Error Status** filter in the left sidebar to quickly surface topics that have DLQ activity without manually searching. ## Inspecting DLQ Messages Once you locate a DLQ topic, click on it to open the Topic Details panel, then click **Browse Messages** to access the Messages tab. ### Reading Error Information Each DLQ message contains the original payload along with error headers that describe why the message failed. Streamkap inspects the following headers in order of priority: 1. `__connect.errors.exception.message` -- the primary exception message from the Kafka Connect framework 2. `__connect.errors.exception.class.name` -- the Java exception class that caused the failure 3. `exception.message` -- a general exception message 4. `error.message` -- an error description from the processing component 5. `_streamkap_error` -- a Streamkap-specific error message 6. `connect.errors.exception.message` -- fallback exception message (without leading underscores) 7. `connect.errors.exception.class.name` -- fallback exception class name (without leading underscores) Streamkap parses error information by first checking message headers, then attempting to extract structured JSON values, then falling back to raw values, and finally adding contextual metadata. The first non-empty value found in the header priority list above is used as the primary error description. ### Inspecting a DLQ Message 1. Open the DLQ topic in the **Messages** tab 2. Click on a message row to expand it 3. Review the **Value** field for the original message payload 4. Review the **Headers** for error details -- look for the headers listed above 5. Use the **Regex Filter** to search for specific error patterns across multiple messages (e.g., `.*schema.*` or `.*permission.*`) If the DLQ topic contains many messages, use the **Timestamp Seek** filter to jump to a specific time window. Combine it with a Regex filter to narrow results further. See [Topics - Filtering Messages](/topics#filtering-messages) for details. ## Common Error Patterns and Fixes **Symptoms:** * Error headers reference schema, type, or conversion errors * Messages mention mismatched column types or unsupported data types **Common Causes:** * A source column type was altered (e.g., `INTEGER` changed to `TEXT`) * The destination does not support a data type present in the source * A new column was added to the source but does not exist in the destination **Resolution:** See [Error Reference — Schema Errors](/error-reference#schema-errors) for detailed resolution steps. **Symptoms:** * Error headers reference access denied, authorization, or permission failures * Messages mention insufficient privileges or forbidden operations **Common Causes:** * The destination database user lacks `INSERT`, `UPDATE`, or `DELETE` privileges on the target table * Table-level or schema-level permissions were revoked or not granted * Role-based access changes were applied without updating the connector credentials **Resolution:** See [Error Reference — Permission Errors](/error-reference#permission-errors) for detailed resolution steps. **Symptoms:** * Error headers reference message size, row size, or value length limits * Messages mention payload too large or column value exceeds maximum **Common Causes:** * A row exceeds the maximum message size supported by the destination * A column value (e.g., a large `TEXT` or `BLOB` field) exceeds the destination column length limit * Batch size settings cause aggregated payloads to exceed limits **Resolution:** See [Error Reference — Resource Errors](/error-reference#resource-errors) for detailed resolution steps on size and limit issues. **Symptoms:** * Records appearing in the DLQ for Snowflake destinations * Error headers reference maximum record size exceeded **Common Causes:** * Individual records exceed Snowflake's 16 MB maximum record size limit * Tables with large `TEXT`, `BLOB`, or `BYTEA` columns produce records that exceed the limit **Resolution:** 1. Identify the oversized columns in the DLQ message payload 2. Exclude large columns from replication if they are not needed at the destination 3. Add a transform to truncate large fields before they reach the destination 4. If the data is required, consider splitting the source table or extracting large columns into a separate table **Symptoms:** * Records diverted to DLQ with column value length errors * Error headers reference value exceeding maximum column length **Common Causes:** * A source column value exceeds the VARCHAR size defined at the destination * Schema evolution created a column with a default size that is too small for the actual data **Resolution:** 1. Identify the column and value from the DLQ message 2. Increase the column size at the destination (e.g., `ALTER TABLE ... ALTER COLUMN ... TYPE VARCHAR(n)`) 3. Configure a transform (SMT) to truncate values before delivery if increasing the column size is not feasible **Symptoms:** * Records diverted to DLQ with timestamp-related errors * Error headers reference invalid date/time values **Common Causes:** * Source data contains invalid timestamps such as year `0000` or dates before year 1 * The destination rejects timestamps outside its supported range **Resolution:** 1. Identify the invalid timestamp values from the DLQ message payload 2. Clean the source data to use valid timestamp values 3. Configure a timestamp normalization transform (SMT) to convert out-of-range timestamps to valid values before delivery **Symptoms:** * Error headers reference null constraint, NOT NULL, or required field violations * Messages contain `null` values for columns that have NOT NULL constraints **Common Causes:** * The source contains `NULL` values in a column that the destination defines as NOT NULL * A transform removed or nullified a required field * Schema evolution added a new NOT NULL column at the destination without a default value **Resolution:** See [Error Reference — Null constraint violation](/error-reference#schema-errors) for detailed resolution steps. ## Recovery DLQ messages represent records that did not reach the destination. The recovery process focuses on identifying the error, fixing the root cause, and backfilling the missing data. Inspect the DLQ topic messages and error headers to understand the failure reason. Use the [common error patterns](#common-error-patterns-and-fixes) above or the [Error Reference](/error-reference) as a guide. Apply the appropriate fix based on the error type: correct schema mismatches, grant missing permissions, adjust size limits, or resolve constraint violations. After fixing the root cause, backfill the data that was lost: * **Trigger a snapshot** (recommended): Snapshot the affected tables to backfill all missing records. This is the safest approach as it captures the current state of the source data. See [Snapshots & Backfilling](/snapshots). * **Reset consumer group offsets** (for very recent events): If the DLQ messages are recent and you want to replay a specific time window, reset the consumer group offsets to replay the affected messages. See [Consumer Groups](/consumer-groups). Monitor the pipeline to confirm that new messages are delivered successfully to the destination. Check that the DLQ topic stops receiving new messages. Review historical DLQ messages to ensure no additional issues remain. DLQ messages are retained according to the topic's retention policy. By default, DLQ topics retain messages for **7 days** (168 hours). After the retention period expires, messages are automatically deleted by Kafka. You can view the exact retention settings for a DLQ topic by opening the topic and checking the **Metadata** tab, which displays the configured `retention.ms` (time-based) and `retention.bytes` (size-based) values. DLQ retention settings are managed by Streamkap and cannot be changed by the user. If you need messages retained for a longer period, contact Streamkap support. **Replay and skip/acknowledge operations are not self-service.** Re-injecting DLQ messages back into the pipeline, skipping individual DLQ records, or acknowledging (clearing) DLQ messages all require assistance from Streamkap support. If you need to reprocess, skip, or clear failed messages, contact [Streamkap support](mailto:support@streamkap.com) with the DLQ topic name, affected connector, and a description of the issue. The recommended self-service approach for recovering missing data is to trigger a snapshot of the affected tables (see [Recovery](#recovery) above). ## Preventing DLQ Records While the DLQ ensures that pipeline failures do not block healthy data, the best approach is to minimize DLQ records in the first place. Follow these practices to reduce the likelihood of messages being routed to the DLQ. ### Pre-deployment Schema Validation Test all schema changes in a staging or development environment before applying them to production. Common DLQ-causing schema issues include: * Adding a NOT NULL column without a default value at the destination * Changing a column type at the source (e.g., `INTEGER` to `TEXT`) without updating the destination * Dropping columns that are referenced by transforms Maintain a staging pipeline that mirrors your production setup. Apply schema changes to staging first and verify that data flows without DLQ errors before promoting to production. ### Test Transforms Before Deploying Transforms that produce unexpected output schemas, null values, or oversized fields are a common source of DLQ records. Before deploying a new or modified transform: 1. Validate the transform logic with representative sample data 2. Verify that the transform output schema is compatible with the destination table 3. Check edge cases such as null input values, empty strings, and unusually large fields ### Set Up Alert Thresholds Configure monitoring alerts to detect DLQ activity early, before a small number of failed records becomes a large backlog: * Set up alerts on DLQ topic record counts (see [Alerts](/alerts)) * Monitor for sudden spikes in DLQ activity, which often indicate a systemic issue such as a schema change or permission revocation * Review DLQ topics regularly, even when alerts are not firing, to catch low-volume issues ### Enable Schema Evolution If your source schemas may change over time (new columns, type changes), ensure that **schema evolution** is enabled on your destination connector. Schema evolution allows the destination to automatically adapt to non-breaking schema changes, reducing the chance of schema mismatch errors in the DLQ. See [Schema Evolution Support](/schema-evolution-support) for details on supported evolution types and configuration. ## Best Practices 1. **Monitor DLQ topics proactively**: Set up alerts for DLQ activity to catch issues early (see [Alerts](/alerts)) 2. **Investigate promptly**: DLQ messages often indicate systemic issues that affect multiple records 3. **Check error headers first**: The error headers provide the fastest path to understanding the failure 4. **Fix at the source**: Whenever possible, fix the root cause rather than working around it at the destination 5. **Use the Logs page**: Cross-reference DLQ errors with connector logs for additional context (see [Logs](/logs)) 6. **Review after schema changes**: Any time you alter source or destination schemas, check DLQ topics for new errors ## Related Documentation * [Topics](/topics) - Browse topics, inspect messages, and manage DLQ topic visibility * [Logs](/logs) - View connector and pipeline logs for additional troubleshooting context * [Alerts](/alerts) - Configure notifications for DLQ activity and pipeline errors * [Pipelines](/pipelines) - Monitor pipeline health and data flow status # DocumentDB Source: https://docs.streamkap.com/documentdb DocumentDB Change Data Capture Setup with Streamkap ## Prerequisites * DocumentDB version ≥ 4.x * A database user with sufficient privileges to create database users and collections ## DocumentDB Setup ### 1. Grant Database Access * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your database. ### 2. Enable Change Streams Change streams allow applications to access real-time data changes. The Connector relies on DocumentDB's implementation of this. * [Enable/Configure Change Streams](https://docs.aws.amazon.com/documentdb/latest/developerguide/change_streams.html#change_streams-enabling) #### Set Change Stream Log Retention Policy Change stream logs should be retained for a minimum of 48 hours. We recommend 7 days. * [How to modify change stream log retention policy](https://docs.aws.amazon.com/documentdb/latest/developerguide/change_streams.html#change_streams-modifying_log_retention) ### 3. Create Database User It's recommended to create a separate user for the Connector to access your DocumentDB database. * Using MongoDB Shell, connect to your primary node or replica set. * Create a user for Streamkap using the script below. Replace password with your choice. ```bash Shell theme={null} use admin db.createUser({ user: "streamkap_user", pwd: "{password}", roles: [ "readAnyDatabase", {role: "read", db: "local"} ] }) ``` ### 4. Enable Snapshots To backfill your data, the Connector needs to be able to perform snapshots. See [Snapshots & Backfilling](/snapshots) for more information. You will need to create a signal collection and give permissions to the `streamkap_user`. The Connector will use this collection for managing snapshots. This collection can exist in a different database (on the same DocumentDB instance) to the database Streamkap captures data from. The examples below use `streamkap_signal` as the signal collection name, but you can choose any name. During [Streamkap Setup](#3-snapshot-settings), provide the full path to your signal collection in `database.collection` format (e.g., `streamkap.streamkap_signal`). ```bash Shell theme={null} db.createCollection("streamkap_signal") db.grantRolesToUser("streamkap_user", [ { role: "read", db: "{database}" }, { role: "readWrite", db: "{database}", collection: "streamkap_signal" } ]) ``` *** ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **DocumentDB**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Connection String**: The DocumentDB connection string. * **Connection Mode** (optional): Default is `replica_set`. * **Array Encoding**: Specify how Streamkap should encode DocumentDB array types. `Array` is the optimal method but requires all elements in the array to be of the same type. `Document` or `String` should be used if the DocumentDB arrays have mixed types. * **Include Schema?** (optional): If you plan on streaming data from this DocumentDB Source to Rockset, set this option to **No**. ### 3. Snapshot Settings * **Signal Collection**: Full path to the signal collection including database and collection name (e.g., `streamkap.streamkap_signal`). This collection is used for incremental snapshotting. See [Enable Snapshots](#4-enable-snapshots) for setup instructions. ### 4. Advanced Parameters * **Represent binary data as**: Specifies how the data for binary columns should be interpreted. Your destination for this data can impact which option you choose. Default is `bytes`. Click **Next**. ### 5. Database and Collection Capture * **Add Database/Collections**: Specify the database(s) and collection(s) for capture. * You can bulk upload here. The format is a simple list of databases and collections, with each entry on a new row. Save as a `.csv` file without a header. **CDC only captures base collections, not Views** Change Data Capture reads DocumentDB's change streams, which only record changes to physical collections. Database Views are query-time aggregations with no physical storage—they don't generate change stream events. **What you cannot capture:** Views (aggregation pipeline results), system collections (system.*, admin.*, config.\*). **Solution:** Specify only the underlying base collections that feed your views. You can recreate the view aggregation pipeline in your destination or transformation layer. Click **Save**. **Have questions?** See the [DocumentDB Source FAQ](/documentdb-source-faq) for answers to common questions about DocumentDB sources, troubleshooting, and best practices. # DocumentDB Source FAQ Source: https://docs.streamkap.com/documentdb-source-faq ## Amazon DocumentDB Sources FAQ for streamkap This FAQ focuses on using Amazon DocumentDB as a source in Streamkap, including general AWS-hosted setups (compatible with MongoDB). Streamkap's DocumentDB connector provides real-time CDC with managed features like automatic scaling, UI setup, and ETL transformations. An Amazon DocumentDB source in streamkap enables real-time Change Data Capture (CDC) from DocumentDB databases, capturing document-level inserts, updates, and deletes with sub-second latency. It uses change streams (MongoDB-compatible) to stream changes to destinations, supporting snapshots for initial loads, schema evolution, and handling for nested JSON data. Streamkap offers a serverless setup via UI or API. * DocumentDB 4.0+ for basic CDC; 5.0+ for advanced features like enhanced change streams and array encoding options. * Compatible with MongoDB 3.6/4.0 compatibility modes. * AWS-hosted DocumentDB clusters (single instance or replica sets). * Streamkap handles sharded setups and multi-region replicas with automatic shard/membership tracking. * **CDC**: Change streams for inserts/updates/deletes; oplog-based resume tracking. * **Snapshots**: Ad-hoc/initial backfills using incremental or blocking methods; phased chunking for minimal impact. * **Schema Evolution**: Automatic handling of document structure changes; field renaming/exclusion. * **Data Types**: Supports integers, floats, strings, dates, arrays, objects, binary (configurable as bytes/base64/hex), JSON; extended JSON for identifiers. * **Ingestion Modes**: Inserts (append) or upserts. * **Security**: SSL, IAM authentication, access control. * **Monitoring**: Latency, lag, queue sizes in-app; heartbeat messages. * Streamkap adds transaction metadata, filtering by collections, and aggregation pipelines. Streamkap uses DocumentDB change streams to capture and decode oplog data, emitting changes as events. It starts from the last recorded transaction, performs a snapshot if needed, then streams from the oplog position. Supports full document updates with pre/post-images (DocumentDB 5.0+). * Trigger ad-hoc at source/collection level.\ Methods: Incremental (phased, chunked by `_id`, default 1024 documents) or blocking (pauses streaming).\ Uses watermarking for progress; supports partial snapshots via conditions. * Modes: `initial` (default), `always`, `initial_only`, `no_data`, `when_needed`, `configuration_based`, `custom`. * **Basics**: Integers (INT32/64), floats (FLOAT32/64), strings, dates/timestamps. * **Advanced**: Arrays (configurable encoding: array, document, string), objects (STRUCT/Tuple), binary (BYTES/base64/hex), decimals, JSON (`STRING/io.debezium.data.Json`). * **Identifiers**: `_id` (ObjectId, string, etc.), binary (extended JSON strict mode). * **Unsupported**: Inconsistent nested structures without preprocessing; non-UTF8; oversized BSON (strategies: fail/skip/split). 1. Ensure DocumentDB cluster is in active state with change streams enabled (default in 4.0+) 2. Create IAM user with read permissions on cluster and `streamkap_signal` collection 3. Create `streamkap_signal` collection for snapshots (can be in a different DB on same instance) 4. In Streamkap UI: Add source, enter connection string (e.g., `mongodb://:@:27017/?ssl=true&replicaSet=rs0`), databases/collections, snapshot mode, array encoding 5. Allow Streamkap IPs in VPC security group. Use AWS CloudWatch for oplog size/lag; Streamkap app for queue metrics. **Best Practices**: Retain oplog 7 days (min 48 hours); alert on growth. * Standalone instances unsupported (requires replica set) * Oplog purging during downtime may lose events * BSON size limits (fail/skip/split) * No transactions pre-4.0 * Sharded clusters need config server access * Incremental snapshots require stable `_id` (non-strings preferred) * UTF-8 only Captures deletes as events with before images; supports full records with pre-images (5.0+). Encrypted connections (SSL), IAM authentication, role-based access; VPC security groups. * **Oplog Buildup**: Monitor retention (AWS Console); resume from last position * **Connection Failures**: Verify VPC, SSL, IAM roles * **Missing Events**: Check include/exclude lists; ensure change streams enabled * **Streamkap-Specific**: Check logs for resume token issues; validate signal collection **No, CDC cannot capture Views or most virtual database objects.** **Why Views cannot be captured:**\ CDC captures changes by reading the database transaction log (binlog, WAL, oplog, redo log, etc.). Views are query-time computations over base tables—they don't store data or generate transaction log entries. When you query a view, the database engine executes the underlying SELECT statement against the base tables. Since views don't store data, they don't generate transaction log entries. **What cannot be captured:** * **Views**: Virtual collections defined by aggregation pipelines, no physical storage or oplog entries * **System Collections** (system.\*, admin.\*, config.\*): Metadata and internal state, not user data * **Time Series Collections**: Amazon DocumentDB does not natively support MongoDB 5.0+ time series collections. However, if using DocumentDB 5.0-compatible mode or custom implementations of time-stamped data with specialized storage, change streams may be limited or unavailable due to storage optimizations that don't maintain document-level change granularity in the oplog. **Solution**: Use regular collections with appropriate time-based indexes for CDC on time-stamped data. * **On-Demand Materialized Views** (`$merge`, `$out` results): Generated data, not original sources **Solution:**\ Configure CDC on the underlying base tables that power your views. The view logic can be recreated in your destination or transformation layer. **DocumentDB-specific notes:** * **Aggregation pipelines on views**: Capture the source collections and apply the pipeline logic downstream * **Standalone instances**: Not supported for CDC—must use a replica set configuration **Example:**\ If you have a view `order_summary` created from the `orders` collection with filters and projections, capture the `orders` collection instead, then apply the same aggregation logic in your destination. * Use replica sets (min 3 nodes for production) * Enable pre/post-images for full updates (5.0+) * Limit collections to reduce load * Test snapshots in staging * Monitor via CloudWatch; set 7-day oplog retention # DynamoDB Source: https://docs.streamkap.com/dynamodb-source DynamoDB Change Data Capture Setup with Streamkap ## Prerequisites * DynamoDB tables created with streams enabled using **"New and old images"** view type (other view types are not supported) * For capturing historic (older than 24 hours) data: * An S3 bucket (for the data exports) * Point-in-time recovery (PITR) enabled on DynamoDB tables ## DynamoDB Setup A DynamoDB stream is an ordered flow of information about changes to items in a DynamoDB table. When you enable a stream on a table, DynamoDB captures information about every modification to data items in the table and stores this information in a log for up to 24 hours. ### 1. Enable DynamoDB Streams The easiest way to manage DynamoDB Streams is by using the AWS Management Console. * Sign in to the AWS Management Console and open the [DynamoDB console](https://console.aws.amazon.com/dynamodb/). * On the DynamoDB console dashboard, choose **Tables** and select an existing table. * Choose the **Exports and streams** tab. * In the DynamoDB stream details section, choose **Turn on**. * Choose the information that will be written to the stream whenever the data in the table is modified. From the list, select **New and old images** (required). * Choose **Turn on stream**. * Repeat the above steps for every table to be captured by Streamkap. **"New and old images" is required.** Streamkap needs both the previous and current state of each item to properly process all change events: * **INSERT events** require the new image to capture the inserted data * **MODIFY events** require the new image to capture the updated data * **DELETE events** require the old image to capture what was deleted Other stream view types (`Keys only`, `New image only`, `Old image only`) will cause the connector to fail with a configuration error. If you see an error mentioning `STREAMKAP-MISCONFIGURATION`, verify this setting is correct. See [Change data capture for DynamoDB Streams](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.html) for more information. ### 2. Enable Point-in-Time Recovery (for historic data) Amazon DynamoDB point-in-time recovery (PITR) provides automatic backups of your DynamoDB table data. This is required by Streamkap for capturing data older than 24 hours. * Navigate to the [DynamoDB console](https://console.aws.amazon.com/dynamodb/). * Choose **Tables** from the left navigation and select an existing table. * From the **Backups** tab, for the Point in Time Recovery option, choose **Edit**. * Choose **Turn on point-in-time recovery** and then **Save** changes. * Repeat the above steps for every table where historic data is to be captured by Streamkap. See [Point-in-time backups: how it works](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/PointInTimeRecovery_Howitworks.html) for more information. ### 3. Configure S3 Exports An S3 bucket is used in conjunction with point-in-time recovery to export data older than 24 hours. You can export to an existing Amazon S3 bucket you have permission to write to. The destination bucket doesn't need to be in the same AWS Region or have the same owner as the source table owner. However, it's recommended to create a new bucket for Streamkap to use when snapshotting historic data. Your AWS Identity and Access Management (IAM) policy needs to allow you to be able to perform S3 actions (`s3:AbortMultipartUpload`, `s3:PutObject`, and `s3:PutObjectAcl`) and the DynamoDB export action (`dynamodb:ExportTableToPointInTime`). Here's an example of a sample policy that will grant your user permissions to perform exports to an S3 bucket: ```JSON JSON theme={null} { "Version": "2012-10-17", "Statement": [ { "Sid": "AllowDynamoDBExportAction", "Effect": "Allow", "Action": "dynamodb:ExportTableToPointInTime", "Resource": "arn:aws:dynamodb:us-east-1:111122223333:table/my-table" }, { "Sid": "AllowWriteToDestinationBucket", "Effect": "Allow", "Action": [ "s3:AbortMultipartUpload", "s3:PutObject", "s3:PutObjectAcl" ], "Resource": "arn:aws:s3:::your-bucket/*" } ] } ``` If you need to write to an S3 bucket that is in another account or you don't have permissions to write to, the S3 bucket owner must add a bucket policy to allow you to export from DynamoDB to that bucket. Here's an example policy on the target S3 bucket: ```JSON JSON theme={null} { "Version": "2012-10-17", "Statement": [ { "Sid": "ExampleStatement", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::123456789012:user/{name}" }, "Action": [ "s3:AbortMultipartUpload", "s3:PutObject", "s3:PutObjectAcl" ], "Resource": "arn:aws:s3:::awsexamplebucket1/*" } ] } ``` See [DynamoDB data export to Amazon S3: how it works](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/S3DataExport.HowItWorks.html) for more information. ### 4. Configure IAM Policy Below is the recommended IAM policy for the Connector: ```JSON JSON theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "dynamodb:DescribeExport", "dynamodb:ExportTableToPointInTime", "dynamodb:ListExports", "dynamodb:DescribeStream", "dynamodb:GetRecords", "dynamodb:GetShardIterator", "dynamodb:DescribeTable", "dynamodb:Scan", "dynamodb:Query", "dynamodb:GetItem" ], "Resource": "arn:aws:dynamodb:us-west-2:424842667740:table/" }, { "Sid": "AccessGameScoresStreamOnly", "Effect": "Allow", "Action": [ "dynamodb:DescribeStream", "dynamodb:GetRecords", "dynamodb:GetShardIterator", "dynamodb:ListStreams" ], "Resource": "arn:aws:dynamodb:us-west-2:424842667740:table//stream/*" }, { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:ListBucket", "s3:PutObject" ], "Resource": [ "arn:aws:s3:::", "arn:aws:s3:::/*" ] } ] } ``` Some permissions are needed only for manual troubleshooting purposes and Auto QA tools, not used by the connector, e.g. `dynamodb:Scan`, `dynamodb:Query`, `dynamodb:GetItem`. *** ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **DynamoDB**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **AWS Region**: The region of the S3 bucket used for historic data exports. * **AWS Access Key ID**: The key for the S3 bucket. * **AWS Secret Key**: The secret for the S3 bucket key. * **S3 export bucket name**: The name of the bucket used for historic data exports. Click **Next**. ### 3. Table Capture * **Add Schemas/Tables**: Specify the table(s) for capture. * You can bulk upload here. The format is a simple list of tables, with each entry on a new row. Save as a `.csv` file without a header. Click **Save**. ## Why "New and old images" is Required DynamoDB Streams supports four stream view types: `KEYS_ONLY`, `NEW_IMAGE`, `OLD_IMAGE`, and `NEW_AND_OLD_IMAGES`. Streamkap requires **"New and old images"** (`NEW_AND_OLD_IMAGES`) because: * **Complete change events**: Both the before and after states of each item are needed to generate accurate CDC events for inserts, updates, and deletes. * **Delete handling**: Without old images, delete events would only contain the key attributes, making it impossible to propagate the full row state to the destination. * **Accurate updates**: Old images enable the connector to detect which attributes changed, supporting correct upsert behavior at the destination. ## Troubleshooting DynamoDB Streams has built-in throughput limits. When the connector reads from the stream faster than the service allows, a `ThrottlingException` occurs. **Resolution:** * No action needed — the connector auto-recovers within 10-30 minutes via exponential backoff * If the error persists beyond 30 minutes, verify that no other consumers are competing for the same DynamoDB Stream * Contact [Streamkap support](mailto:support@streamkap.com) if throttling is persistent and impacting data freshness # Elasticsearch Source: https://docs.streamkap.com/elasticsearch Elasticsearch Change Data Capture Setup with Streamkap ## Prerequisites * Elasticsearch cluster connection details * A database user with sufficient privileges to read from indices ## Elasticsearch Setup ### 1. Grant Database Access * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your Elasticsearch cluster. ### 2. Create Database User * Create a user and password for Streamkap with read access to the indices you want to capture. *** ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **Elasticsearch**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Hostname**: Specify the Elasticsearch cluster hostname. * **Port**: Default is `443` for HTTPS. * **Protocol**: Select HTTPS or HTTP. * **Authentication Type**: Select the authentication method. * For **Basic** authentication: * **Username**: The Elasticsearch user. * **Password**: The user's password. ### 3. Index Capture * **Index names**: Specify the index(es) to capture. * **Datetime Field name**: The field used to track changes (e.g. `@timestamp`). Click **Save**. # Error Reference Source: https://docs.streamkap.com/error-reference Consolidated lookup for common Streamkap errors, their causes, and resolution steps — organized by category for quick troubleshooting. This page consolidates known errors from across Streamkap sources, destinations, transforms, and pipelines into a single searchable reference. Each entry includes the error message, its cause, and the steps to resolve it. For errors specific to a particular source or destination, cross-links point to the relevant connector documentation for additional context. Use your browser's search (Ctrl+F / Cmd+F) to find a specific error message quickly. ## Schema Schema errors occur when there is a mismatch between the source data schema and what the destination expects — including type conflicts, evolution failures, and unsupported data types. **Error message:** ```text theme={null} Column is of type double precision but expression is of type character varying ``` **Cause:** The source column type was altered (e.g., `INTEGER` changed to `TEXT`), or the destination column type does not match the incoming data type. This commonly happens after a schema change at the source that was not reflected at the destination. **Resolution:** 1. Compare the source schema with the destination table schema 2. Alter the destination column to match the expected type, or enable [schema evolution](/schema-evolution-support) to handle new columns automatically 3. If using transforms, verify the transform output schema aligns with the destination 4. Check the [DLQ topic](/dlq-operations) for affected messages and confirm new data flows correctly after the fix **Related:** [Schema Evolution](/schema-evolution-support) | [DLQ Operations](/dlq-operations) **Error message:** ```text theme={null} Cannot encode decimal with precision 44 as max precision 38 ``` **Cause:** The source database contains a decimal value whose precision exceeds the maximum precision (38) supported by the destination or the Kafka Connect Decimal logical type. **Resolution:** 1. Contact [Streamkap support](mailto:support@streamkap.com) to adjust how decimal values are handled on your source connector — Streamkap can configure it to convert decimals to floating-point numbers or represent them as strings, avoiding precision overflow 2. Review the source table's column definitions for excessively large precision values **Error message:** ```text theme={null} Insufficient privileges ``` **Cause:** Schema evolution in Snowflake requires the `OWNERSHIP` privilege on the target tables, not just `ALTER`. When Snowpipe Streaming detects new columns in incoming data, it must alter the destination table, which Snowflake restricts to the table owner. **Resolution:** 1. Grant `OWNERSHIP` on existing and future tables to the Streamkap role: ```sql theme={null} GRANT OWNERSHIP ON ALL TABLES IN SCHEMA . TO ROLE STREAMKAP_ROLE COPY CURRENT GRANTS; GRANT OWNERSHIP ON FUTURE TABLES IN SCHEMA . TO ROLE STREAMKAP_ROLE COPY CURRENT GRANTS; ``` 2. Alternatively, pre-create destination tables with the expected columns and manage schema changes manually 3. Restart the destination connector after applying permission changes **Related:** [Snowflake Schema Evolution Permissions](/snowflake#schema-evolution-permissions) | [Schema Evolution](/schema-evolution-support) **Error message (varies by destination):** ```text theme={null} null value in column "X" violates not-null constraint ``` **Cause:** The source contains `NULL` values in a column that the destination defines as `NOT NULL`, a transform removed or nullified a required field, or schema evolution added a new `NOT NULL` column at the destination without a default value. **Resolution:** 1. Identify the column with the null violation from the error message 2. Either relax the `NOT NULL` constraint at the destination or add a default value 3. If the source legitimately contains nulls, add a transform to provide a default value before delivery 4. Verify that schema evolution settings are compatible between source and destination **Related:** [DLQ Operations — Null Constraint Violations](/dlq-operations#common-error-patterns-and-fixes) **Error message:** ```text theme={null} Two capture instances already exist for source table ``` **Cause:** SQL Server limits each table to a maximum of 2 CDC capture instances. This error occurs when trying to refresh the change table structure (required after a schema change) while 2 capture instances already exist. **Resolution:** 1. Identify the oldest capture instance by running: ```sql theme={null} USE {database}; GO EXEC sys.sp_cdc_help_change_data_capture @source_schema = N'{schema}', @source_name = N'{table}' GO ``` 2. Disable the oldest capture instance using the `create_date` column to identify it: ```sql theme={null} USE {database}; GO EXEC sys.sp_cdc_disable_table @source_schema = N'{source_schema}', @source_name = N'{source_table}', @capture_instance = N'{capture_instance}' GO ``` 3. Retry the change table refresh script **Related:** [SQL Server Source FAQ](/sql-server-source-faq) **Error message (varies):** ```text theme={null} Renaming table(s) will report an error ``` **Cause:** Renaming a source table is not automatically tracked by schema evolution. The renamed table appears as a new table and requires explicit reconfiguration. **Resolution:** 1. Add the renamed table to your Source Connector and Pipeline configuration 2. Trigger a snapshot for the newly added table 3. The old table's destination data remains intact but stops receiving new change events **Related:** [Schema Evolution](/schema-evolution-support) | [Snapshots](/snapshots#snapshotting-after-schema-changes) **Error message:** ```text theme={null} Schema isn't known to this connector ``` **Cause:** Affects databases with 1000+ tables when schema history optimization is enabled. The schema history may become incomplete, causing the connector to encounter tables or schemas it cannot resolve. **Resolution:** 1. Contact [Streamkap support](mailto:support@streamkap.com) for schema history recovery 2. Consider enabling **Capture Only Captured Tables DDL** in the source's Advanced settings to limit schema history scope 3. After recovery, a snapshot of affected tables may be required **Related:** [MySQL Source FAQ](/mysql-source-faq) **Error message:** ```text theme={null} Ambiguous method overloading for BigDecimal ``` **Cause:** A transform function receives a decimal value that causes ambiguous method resolution in the JavaScript transform runtime. **Resolution:** 1. Use explicit type casting in the transform code to convert the decimal to a specific type (e.g., `Number()` or `.toString()`) 2. Contact [Streamkap support](mailto:support@streamkap.com) to check how decimals are handled on the source connector — adjusting the decimal handling can prevent this conflict 3. Test the transform with sample data in the Implementation tab before deploying **Related:** [Streaming Transforms](/transforms-1) | [Transform Filter Records](/transform-filter-records) **Error message:** ```text theme={null} Column '' not found in result set ... This might be caused by DBZ-4350 ``` **Cause:** The table has a generated column (`GENERATED ALWAYS AS (...) STORED`). PostgreSQL does not include generated columns in the replication stream, so an incremental (Filtered or Full) snapshot cannot reconcile them and fails. Blocking and Parallel snapshots are not affected. **Resolution:** 1. Exclude the generated column from capture: in the Source's **Settings → Advanced** section, set **Column Selection Mode** to **Exclusion** and add the column to the **Column Exclusion List** (fully-qualified, e.g. `schema.table.column`). 2. Re-trigger the snapshot. Recompute the value at your destination or in a [transform](/transforms) if you need it. **Related:** [PostgreSQL Source FAQ](/postgresql-source-faq) | [Snapshots & Backfilling](/snapshots) ## Permission Permission errors occur when the connector's database user or cloud IAM role lacks the required privileges to perform an operation. **Error message:** ```text theme={null} Insufficient privileges ``` **Cause:** The Snowflake user or role used by the connector does not have the required privileges on the target object (warehouse, database, schema, or table). This is common during initial setup or when schema evolution requires `OWNERSHIP` on tables. **Resolution:** 1. Verify the Snowflake role has the required privileges by running Script #2 from the [Snowflake troubleshooting section](/snowflake#snowflake-setup-scripts-failing) 2. For schema evolution, grant `OWNERSHIP` on tables (see [Schema Evolution Permissions](/snowflake#schema-evolution-permissions)) 3. Ensure the role is granted to the user and set as the default role: ```sql theme={null} GRANT ROLE IDENTIFIER($role_name) TO USER IDENTIFIER($user_name); ALTER USER IDENTIFIER($user_name) SET DEFAULT_ROLE = $role_name; ``` **Related:** [Snowflake Setup](/snowflake#snowflake-setup) | [Snowflake Setup Scripts Failing](/snowflake#snowflake-setup-scripts-failing) **Error message:** ```text theme={null} AccessDenied when calling AssumeRole ``` **Cause:** The IAM role trust policy does not include Streamkap's role ARN, or the IAM role does not have the required permissions for the target AWS service (S3, DynamoDB, etc.). **Resolution:** 1. Update the IAM role's trust policy to include Streamkap's external ID and role ARN 2. Verify the IAM role has the required service permissions (e.g., `s3:PutObject`, `dynamodb:DescribeStream`) 3. Confirm the role ARN in the Streamkap connector configuration matches the actual IAM role **Error message (varies by database):** ```text theme={null} Access denied for user 'X'@'Y' to database 'Z' ``` ``` permission denied for table X ``` **Cause:** The database user configured for the Streamkap source connector lacks the required privileges (e.g., `REPLICATION`, `SELECT`, or CDC-specific permissions). **Resolution:** * **PostgreSQL:** Verify the user has `REPLICATION` privilege and `SELECT` on captured tables. Check `pg_hba.conf` allows the connection. See [PostgreSQL Source FAQ — Troubleshooting](/postgresql-source-faq) * **MySQL:** Ensure the user has `REPLICATION SLAVE`, `REPLICATION CLIENT`, and `SELECT` privileges. See [MySQL Source FAQ — Troubleshooting](/mysql-source-faq) * **Oracle:** Verify LogMiner privileges and `SELECT` on captured tables. See [Oracle Source FAQ — Troubleshooting](/oracle-source-faq) * **SQL Server:** Ensure the user has `db_owner` role or equivalent CDC privileges. See [SQL Server Source FAQ — Troubleshooting](/sql-server-source-faq) **Error message:** ```text theme={null} Topic authorization failed ``` ``` TOPIC_AUTHORIZATION_FAILED ``` **Cause:** The Kafka user is missing `TOPIC READ` or `TOPIC WRITE` ACL permissions for the target topic. **Resolution:** 1. Add the appropriate ACL permission: * **Resource Type:** `TOPIC` * **Operation:** `READ` (for consumers) or `WRITE` (for producers) * **Pattern Type:** `LITERAL` or `PREFIXED` * **Name:** Your topic name or prefix **Related:** [Kafka Access](/kafka-access) **Error message:** ```text theme={null} GROUP_AUTHORIZATION_FAILED ``` ``` Group authorization failed ``` **Cause:** The Kafka user is missing `GROUP READ` ACL permissions for the consumer group. **Resolution:** 1. Add the following ACL permission: * **Resource Type:** `GROUP` * **Operation:** `READ` * **Pattern Type:** `LITERAL` or `PREFIXED` * **Name:** Your consumer group ID (e.g., `my-consumer-group`) This only affects consumers using consumer groups (Python consumers, CLI tools). **Related:** [Kafka Access](/kafka-access) **Error message (varies):** ```text theme={null} Access denied ``` ``` Insufficient privileges to perform snapshot ``` **Cause:** The database user lacks `SELECT` or `READ` privileges on one or more tables being snapshotted, or the signal table permissions are missing. **Resolution:** 1. Verify the connector's database user has `SELECT` privileges on all tables being snapshotted 2. For MySQL/SQL Server: confirm `SELECT`, `INSERT`, `UPDATE`, and `DELETE` privileges on the `streamkap.streamkap_signal` table 3. For PostgreSQL: confirm the user has access to the publication and the signal table 4. Re-trigger the snapshot after granting the necessary permissions **Related:** [Snapshots — Insufficient Permissions](/snapshots#failed-snapshot-recovery) ## Connection Connection errors arise from network issues, timeouts, SSL/TLS configuration problems, or firewall rules blocking access. **Error message:** ```text theme={null} MySQL server has gone away ``` **Cause:** The MySQL connection timed out, typically due to a long-running snapshot, idle connection exceeding `wait_timeout`, or network instability. **Resolution:** 1. Increase `wait_timeout` on the MySQL server (e.g., `SET GLOBAL wait_timeout = 28800;`) 2. Check network stability between Streamkap and the MySQL server 3. If the error occurs during snapshots, consider using filtered (partial) snapshots to reduce operation time 4. Verify firewall rules and security groups allow persistent connections **Related:** [MySQL Source FAQ — Troubleshooting](/mysql-source-faq) **Error message (varies):** ```text theme={null} Connection refused ``` ``` FATAL: no pg_hba.conf entry for host ``` **Cause:** The PostgreSQL server is rejecting the connection due to `pg_hba.conf` rules, firewall restrictions, or SSL configuration. **Resolution:** 1. Verify `pg_hba.conf` includes an entry allowing the Streamkap IP addresses with the correct authentication method 2. Check firewall rules and security groups allow traffic on the PostgreSQL port (default 5432) 3. Ensure SSL is correctly configured if required 4. Confirm [Streamkap IP addresses](/streamkap-ip-addresses) are allowlisted **Related:** [PostgreSQL Source FAQ — Troubleshooting](/postgresql-source-faq) **Error message (varies):** ```text theme={null} ORA-12541: TNS:no listener ``` ``` ORA-12514: TNS:listener does not currently know of service ``` **Cause:** The Oracle listener is not running, the TNS configuration is incorrect, or firewall rules are blocking access on the configured port. **Resolution:** 1. Check the Oracle listener status on the database server (`lsnrctl status`) 2. Verify the TNS configuration (hostname, port, service name) in the Streamkap connector settings 3. Ensure firewall rules allow traffic on the Oracle listener port (default 1521) 4. For AWS RDS Oracle, verify the endpoint and port from the RDS console **Related:** [Oracle Source FAQ — Troubleshooting](/oracle-source-faq) **Error message (varies):** ```text theme={null} Certificate verify failed ``` ``` SSL handshake failed due to weak encryption algorithm ``` ``` Certificates do not conform to algorithm constraints ``` **Cause:** The database instance is using an outdated, weak, or expired SSL certificate. The SSL certificate's encryption algorithm or key size does not meet the minimum requirements. **Resolution:** 1. Check the database SSL certificate details using the `openssl` commands in the [SSL Certificate Management Guide](/connector-security-ssl) 2. If the certificate uses RSA 1024-bit or SHA-1, upgrade to RSA 2048-bit+ with SHA-256 3. For cloud-managed databases (AWS RDS, Azure, GCP), check if a certificate rotation is needed 4. Update the Streamkap connector if the SSL mode or certificate path has changed **Related:** [SSL Certificate Management Guide](/connector-security-ssl) **Error message:** ```text theme={null} SSL connection closed by peer ``` **Cause:** SSL certificate verification is failing when connecting to the Streamkap Kafka cluster, or the client's certificate configuration is incorrect. **Resolution:** 1. Ensure certificates are properly configured for your client: * **Python:** `pip install --upgrade certifi` and set `ssl.ca.location` to `certifi.where()` * **CLI tools:** Try different certificate paths (e.g., `/etc/ssl/cert.pem` or `/etc/ssl/certs/ca-certificates.crt`) 2. Test the SSL handshake: ```bash theme={null} openssl s_client -connect :32400 -servername ``` 3. Verify your network does not have a VPN or proxy interfering with SSL **Related:** [Kafka Access](/kafka-access) **Error message:** ```text theme={null} SASL authentication failed ``` **Cause:** Incorrect Kafka username or password, or the SASL mechanism is not configured correctly. **Resolution:** 1. Verify the username and password are correct 2. Ensure `sasl.mechanism` is set to `PLAIN` and `security.protocol` is set to `SASL_SSL` 3. Confirm the Kafka user account is active and not disabled **Related:** [Kafka Access](/kafka-access) **Error message (varies):** ```text theme={null} Connection timed out ``` ``` Read timed out ``` **Cause:** Network interruption between Streamkap and your source database during a snapshot — firewall change, VPN drop, or transient cloud networking issue. **Resolution:** 1. Verify network connectivity to your source database 2. Check firewall rules and security group configurations 3. Ensure any VPN or SSH tunnel is active and stable 4. Once connectivity is restored, re-trigger the snapshot **Related:** [Snapshots — Failed Snapshot Recovery](/snapshots#failed-snapshot-recovery) **Error message (varies):** ```text theme={null} Connection refused ``` ``` Authentication failed ``` **Cause:** Firewall, SSL configuration, or authentication issues between Streamkap and the MongoDB cluster. **Resolution:** 1. Verify firewall rules allow Streamkap IPs to connect 2. Confirm SSL is enabled and the connection string includes `?ssl=true` 3. Check the authentication credentials and database name 4. For MongoDB Atlas, ensure Streamkap IP addresses are in the IP access list **Related:** [MongoDB Source FAQ — Troubleshooting](/mongodb-source-faq) **Error message:** ```text theme={null} ThrottlingException ``` **Cause:** DynamoDB Streams throughput limit exceeded. This is a transient error that occurs when the connector reads from DynamoDB Streams faster than the service allows. **Resolution:** 1. No action needed — the connector auto-recovers within 10-30 minutes via exponential backoff 2. If the error persists beyond 30 minutes, verify that no other consumers are competing for the same DynamoDB Stream 3. Contact [Streamkap support](mailto:support@streamkap.com) if throttling is persistent and impacting data freshness **Related:** [DynamoDB Source](/dynamodb-source) **Error message (varies):** ```text theme={null} Connection refused ``` ``` VPC security group denied ``` **Cause:** VPC security group rules, SSL configuration, or IAM authentication issues prevent Streamkap from connecting to the DocumentDB cluster. **Resolution:** 1. Verify the VPC security group allows inbound traffic from Streamkap IPs on port 27017 2. Confirm SSL is enabled and the connection string includes `?ssl=true&replicaSet=rs0` 3. Check IAM roles and database user credentials 4. Ensure the DocumentDB cluster is in an active state **Related:** [DocumentDB Source FAQ — Troubleshooting](/documentdb-source-faq) ## Replication Replication errors relate to WAL/binlog/redo log/oplog issues, replication slot problems, and CDC configuration failures. **Symptoms:** Disk usage on the PostgreSQL server is growing rapidly; `pg_replication_slots` shows large lag values. **Cause:** Inactive or slow replication slots prevent WAL files from being recycled. Low-traffic databases without heartbeats can also cause WAL accumulation because the replication slot position is not advancing. **Resolution:** 1. Enable heartbeats to keep replication slot positions advancing on low-traffic databases 2. Monitor replication slots and drop inactive ones: ```sql theme={null} SELECT slot_name, active, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS lag FROM pg_replication_slots; -- Drop an inactive slot SELECT pg_drop_replication_slot('{slot_name}'); ``` 3. Set WAL retention to 3-5 days 4. Ensure `VACUUM` and `ANALYZE` run regularly **Related:** [Monitoring the PostgreSQL WAL Log](/monitoring-the-postgresql-wal-log) | [PostgreSQL Source FAQ](/postgresql-source-faq) **Symptoms:** Binlog files are accumulating and consuming disk space; expected change events are not appearing in the destination. **Cause:** Binlog retention is too short (events expire before being consumed), heartbeats are not enabled for low-traffic databases, or the binlog format is not set to `ROW`. **Resolution:** 1. Ensure `binlog_format=ROW` and `binlog_row_image=FULL` 2. Enable heartbeats for low-traffic databases 3. Set binlog retention to 3-5 days 4. Verify the connector user has `REPLICATION SLAVE` and `REPLICATION CLIENT` privileges 5. Check that the target tables are included in the connector configuration **Related:** [MySQL Source FAQ — Troubleshooting](/mysql-source-faq) **Symptoms:** Archive log destination disk space is growing; LogMiner sessions are consuming resources. **Cause:** Heartbeats are not enabled for low-traffic databases, archive log retention is too long, or supplemental logging overhead is high. **Resolution:** 1. Enable heartbeats for low-traffic databases 2. Monitor archive log destination space and adjust retention (3-5 days minimum) 3. Verify supplemental logging is enabled at database and table levels: ```sql theme={null} ALTER DATABASE ADD SUPPLEMENTAL LOG DATA; ALTER TABLE schema.table ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS; ``` 4. Check LogMiner session resource usage and limit captured tables if needed **Related:** [Oracle Source FAQ — Troubleshooting](/oracle-source-faq) **Symptoms:** No change events are being captured; change tables are not being populated. **Cause:** The SQL Server Agent service is stopped. CDC relies on the SQL Server Agent to populate change tables and run cleanup jobs. **Resolution:** 1. Verify the SQL Server Agent is running: ```sql theme={null} EXEC master.dbo.xp_servicecontrol N'QUERYSTATE', N'SQLSERVERAGENT' ``` 2. Start the SQL Server Agent if stopped 3. Verify CDC is enabled on the database and tables: ```sql theme={null} SELECT name, is_cdc_enabled FROM sys.databases WHERE name = '{database}'; EXEC sys.sp_cdc_help_change_data_capture; ``` 4. Check that the connector user has the required role membership **Related:** [SQL Server Source FAQ — Troubleshooting](/sql-server-source-faq) **Symptoms:** CDC stops working after a database restore; no change events captured. **Cause:** A database restore operation disables CDC on the database and all tables. CDC must be re-enabled manually after a restore. **Resolution:** 1. Re-enable CDC on the database: ```sql theme={null} USE {database}; EXEC sys.sp_cdc_enable_db; ``` 2. Re-enable CDC on each table that was previously captured 3. Trigger a new snapshot to backfill any data lost during the restore **Related:** [SQL Server Source FAQ — Troubleshooting](/sql-server-source-faq) **Symptoms:** The connector fails to resume from its last position; logs mention resume token issues. **Cause:** The oplog has been rotated past the connector's last resume token position, typically because the connector was offline for too long or oplog retention is too short. **Resolution:** 1. Set oplog retention to at least 3-5 days (7 days recommended for DocumentDB) 2. Enable heartbeats to keep resume tokens fresh 3. If the resume token is invalid, trigger a new snapshot to re-establish position 4. For sharded clusters, verify oplog retention on all shards **Related:** [MongoDB Source FAQ — Troubleshooting](/mongodb-source-faq) | [DocumentDB Source FAQ — Troubleshooting](/documentdb-source-faq) **Symptoms:** Some change events (especially updates and deletes) are not appearing in the destination. **Cause:** The PostgreSQL publication does not include the affected tables, or `REPLICA IDENTITY` is set to `DEFAULT` (only logs primary key columns for updates/deletes). **Resolution:** 1. Verify the publication includes the target tables: ```sql theme={null} SELECT * FROM pg_publication_tables WHERE pubname = 'streamkap_pub'; ``` 2. Set `REPLICA IDENTITY FULL` for complete before/after images: ```sql theme={null} ALTER TABLE schema.table REPLICA IDENTITY FULL; ``` 3. Check that the connector's table inclusion list matches the publication **Related:** [PostgreSQL Source FAQ](/postgresql-source-faq) **Symptoms:** Change events for updates or deletes are incomplete (only primary key values captured); some tables produce no events. **Cause:** Supplemental logging is not enabled at the database level or with `ALL COLUMNS` at the table level. **Resolution:** 1. Enable supplemental logging at database level: ```sql theme={null} ALTER DATABASE ADD SUPPLEMENTAL LOG DATA; ``` 2. Enable supplemental logging with ALL COLUMNS at table level: ```sql theme={null} ALTER TABLE schema.table ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS; ``` 3. Verify LogMiner privileges are granted to the Streamkap user 4. Confirm archive log mode is enabled **Related:** [Oracle Source FAQ](/oracle-source-faq) **Error message:** ```text theme={null} ChangeStreamFatalError (Error 280) ``` **Cause:** The MongoDB oplog has been recycled before the connector could process all events. The connector's resume token points to a position that no longer exists in the oplog. **Resolution:** 1. Contact [Streamkap support](mailto:support@streamkap.com) for an offset reset 2. Increase oplog retention to 48 hours or more to prevent recurrence: ```javascript theme={null} db.adminCommand({ replSetResizeOplog: 1, minRetentionHours: 48 }) ``` 3. After the offset reset, a snapshot may be required to backfill any missed data **Related:** [MongoDB Source FAQ](/mongodb-source-faq) | [Snapshots](/snapshots) **Error message:** ```text theme={null} Failed to re-select row ``` **Cause:** TOAST data reselect connection timeout on tables with large TOAST columns. When PostgreSQL stores large column values in TOAST tables, the connector must re-select them during processing, which can time out on tables with very large values. **Resolution:** 1. Contact [Streamkap support](mailto:support@streamkap.com) to configure retriable exception handling for your connector 2. Consider setting `REPLICA IDENTITY FULL` on affected tables to avoid TOAST reselection: ```sql theme={null} ALTER TABLE schema.table REPLICA IDENTITY FULL; ``` **Related:** [PostgreSQL Source FAQ](/postgresql-source-faq) **Symptoms:** Snapshot fails or produces inconsistent data when DDL changes are applied to a table during an active snapshot. **Cause:** Schema changes during an active snapshot are not supported. The snapshot process reads data using the schema at the time it started, and a DDL change mid-snapshot causes conflicts. **Resolution:** 1. Avoid making schema changes to tables that are actively being snapshotted 2. If a schema change was applied during a snapshot, cancel the snapshot and re-trigger it after the schema change is complete 3. Wait for the DDL change to propagate before triggering a new snapshot **Related:** [Snapshots — Snapshotting After Schema Changes](/snapshots#snapshotting-after-schema-changes) ## Transform Transform errors occur during data transformation in Apache Flink, including JavaScript runtime failures, DLQ routing, and job management issues. **Symptoms:** The transform job continuously restarts and never reaches a stable `RUNNING` state. **Cause:** The transform code contains a runtime error, the input topic pattern matches no topics, or the project lacks sufficient resources for the configured parallelism. **Resolution:** 1. Check transform logs on the [Logs](/logs) page for specific error messages 2. Verify the input pattern matches existing topics 3. Test the transform logic in the Implementation tab with sample data 4. Reduce parallelism to check if it is a resource issue 5. Revert recent settings or code changes that may have caused the issue **Related:** [Streaming Transforms — Troubleshooting](/transforms-1#troubleshooting) **Symptoms:** The transform shows `RUNNING` status but the written records count is zero. **Cause:** The input pattern regex does not match any actual topic names, the transform logic filters out all records, or the input topics are empty. **Resolution:** 1. Verify the input pattern regex matches actual topic names 2. Confirm the transform logic does not filter out all records 3. Review input topics to ensure they contain data 4. Check the Errors metric and review logs for silent failures 5. Test the implementation with sample data in the Implementation tab **Related:** [Streaming Transforms — Troubleshooting](/transforms-1#troubleshooting) **Symptoms:** Transform latency is continuously increasing; downstream destinations are falling behind. **Cause:** The transform parallelism is too low for the input volume, the JavaScript logic is computationally expensive, or the input topics have excessive consumer lag. **Resolution:** 1. Increase parallelism in the Settings tab (set to at least the number of input topic partitions) 2. Optimize the JavaScript transform logic — reduce unnecessary operations 3. Check input topics for excessive consumer lag 4. Increase partitions on input topics for better parallelism **Related:** [Streaming Transforms — Troubleshooting](/transforms-1#troubleshooting) **Symptoms:** The transform's DLQ topic is receiving messages; errors visible in the DLQ message headers. **Cause:** Individual records fail during the transform — type mismatches, null values in required fields, or unexpected data formats. **Resolution:** 1. Inspect the DLQ topic messages to identify the failing records and error details 2. Check headers for `_streamkap_error` or `__connect.errors.exception.message` 3. Fix the transform logic to handle edge cases (null values, unexpected types) 4. Add error handling in the transform code to prevent job-level failures **Related:** [DLQ Operations](/dlq-operations) | [Streaming Transforms](/transforms-1) ## Resource Resource errors occur when system resources (memory, disk, compute) are exhausted or when internal timeouts are exceeded. **Error message:** ```text theme={null} Could not acquire minimum required resources ``` **Cause:** The Kafka Connect cluster has reached its capacity limit and cannot allocate resources for the connector or task. **Resolution:** 1. Reduce connector parallelism (lower the number of tasks) 2. Contact Streamkap support to discuss scaling options 3. Review whether other connectors on the same project can be optimized to free resources **Error message:** ```text theme={null} Kafka Connect API call timed out ``` **Cause:** An internal API call to the Kafka Connect cluster timed out due to high load, network latency, or resource contention. **Resolution:** 1. Retry the operation — transient timeouts often resolve on subsequent attempts 2. If persistent, check the connector's resource allocation and reduce parallelism 3. Contact Streamkap support if the issue persists after multiple retries **Error message:** ```text theme={null} 503 Service Unavailable ``` ``` NullPointerException ``` **Cause:** Transient Snowflake Streaming API overload. The Snowflake service is temporarily unable to handle the request volume. **Resolution:** 1. **Do NOT restart the connector.** The connector's built-in retry mechanism with exponential backoff handles recovery automatically 2. Recovery typically occurs within 5-30 minutes 3. Monitor the pipeline — if the error persists beyond 30 minutes, contact [Streamkap support](mailto:support@streamkap.com) Restarting the connector during a transient 503 error can make the situation worse by triggering additional reconnection overhead. Allow the automatic retry to handle recovery. **Related:** [Snowflake](/snowflake) **Symptoms:** New data is not appearing in destination tables; lag shows as negative (e.g., -1) or unusually high; Snowflake channels show offset positions that do not match Consumer Group offsets. **Cause:** The Consumer Group offsets and Snowflake Channel offsets have become misaligned, typically after topic deletion and recreation. **Resolution:** 1. **Stop** the destination connector in Streamkap UI 2. Reset Consumer Group offsets via [Consumer Groups Reset Procedure](/consumer-groups#reset-procedure) 3. Reset Snowflake Channel offsets to `-1`: ```sql theme={null} SELECT SYSTEM$SNOWPIPE_STREAMING_UPDATE_CHANNEL_OFFSET_TOKEN( '..', '', '-1' ); ``` 4. **Resume** the destination connector 5. Verify data appears in the destination and lag decreases **Related:** [Snowflake — Offset Management](/snowflake#offset-management-append-mode) **Error message (varies):** ```text theme={null} Message size exceeds maximum ``` ``` Column value exceeds maximum length ``` **Cause:** A row or column value exceeds the maximum message size supported by the destination, or batch size settings cause aggregated payloads to exceed limits. **Resolution:** 1. Identify the oversized field in the DLQ message payload 2. Increase the column size limit at the destination if possible 3. Consider adding a transform to truncate or filter oversized values before they reach the destination 4. Review the destination connector's batch size settings **Related:** [DLQ Operations — Size and Limit Errors](/dlq-operations#common-error-patterns-and-fixes) **Symptoms:** Snapshot fails partway through; source database logs show slow queries or connection pool exhaustion. **Cause:** The snapshot's read queries compete with the production workload, causing timeouts or resource exhaustion on the source database. **Resolution:** 1. Schedule the snapshot during off-peak hours to reduce contention 2. Use filtered (partial) snapshots to process smaller data ranges 3. Review the source database's connection limits and increase if needed 4. Check for long-running queries or locks that may block snapshot reads 5. Re-trigger the snapshot after the source database has recovered **Related:** [Snapshots — Failed Snapshot Recovery](/snapshots#failed-snapshot-recovery) **Symptoms:** Snapshot or connector fails with errors related to disk space, memory, or storage limits. **Cause:** The source database, destination system, or intermediate storage has run out of available disk space or memory during operations. **Resolution:** 1. Check available disk space on the source database server 2. For cloud-managed databases, verify storage auto-scaling is enabled or increase provisioned storage 3. Review destination storage capacity 4. Clean up unnecessary data, logs, or temporary files 5. Re-trigger the operation after freeing sufficient resources **Related:** [Snapshots — Failed Snapshot Recovery](/snapshots#failed-snapshot-recovery) **Symptoms:** ClickHouse destination lag is continuously growing; records are being written slowly. **Cause:** The connector configuration is not optimized for the workload — batch size, parallelism, or topic partitions may be insufficient. **Resolution:** 1. Increase **Maximum poll records** in the connector's Advanced settings (e.g., `25000`, `50000`, or `80000`) 2. Increase topic partitions to at least `5` on the [Topics](/topics) page 3. Increase the **Tasks** setting to allow more parallel processing 4. Adjust settings incrementally and monitor the impact **Error message:** ```text theme={null} Database 'X' not found ``` **Cause:** The database referenced in the incremental snapshot target does not exist, has been renamed, or the connector's configuration references a stale database name. **Resolution:** 1. Verify the database name in the source connector configuration matches an existing database 2. If the database was renamed, update the connector configuration with the new name 3. Re-trigger the snapshot after correcting the configuration **Symptoms:** Source connector startup is slow or times out; schema history topic is very large. **Cause:** The source connector records schema structures from all databases and tables in the instance, even those not being captured. For large instances, this causes slow startup and excessive topic growth. **Resolution:** 1. Enable **Capture Only Captured Databases DDL** in the source's Advanced settings 2. Enable **Capture Only Captured Tables DDL** to limit schema history to only the tables you capture 3. Consider restricting the database user's access to only the captured databases and tables ## Related Resources * [DLQ Operations](/dlq-operations) - Monitor, inspect, and resolve failed messages in your pipelines * [Pipeline Recovery](/pipeline-recovery) - Troubleshoot and recover broken pipelines * [Schema Evolution](/schema-evolution-support) - How Streamkap handles schema changes between source and destination * [Logs](/logs) - View detailed connector and pipeline logs # Streamkap Docs Source: https://docs.streamkap.com/getting-started Serverless, real-time data integration platform for streaming data sub-second from sources to destinations Streamkap is a serverless, real-time data integration platform. You can set up pipelines within *minutes*, streaming data *sub-second* from a variety of sources to destinations for building real-time data apps and services. Here are some useful links to get you started: # Overview Source: https://docs.streamkap.com/google-bigquery Configure Google BigQuery as a Streamkap CDC destination: prerequisites, IAM permissions, service account JSON key, Storage Write API, and schema evolution. Stream change data into Google BigQuery. Streamkap writes using the [BigQuery Storage Write API](https://cloud.google.com/bigquery/docs/write-api) — the lowest-cost, high-throughput ingestion path. ## Prerequisites * A Google Cloud project. * A BigQuery **dataset** that already exists — Streamkap writes tables into it but does **not** create the dataset for you. * A Google **service account** with a **JSON** key (P12 keys are not supported). * The service account granted the role below. ## Required permissions Grant the service account access **on the target dataset** (least privilege), not the whole project: * **BigQuery Data Editor** (`roles/bigquery.dataEditor`) on the dataset — covers everything Streamkap needs: writing rows, creating tables, and evolving table schemas. Prefer a custom role? The minimum permissions are `bigquery.datasets.get`, `bigquery.tables.get`, `bigquery.tables.list`, `bigquery.tables.updateData`, plus `bigquery.tables.create` (auto-create tables) and `bigquery.tables.update` (schema evolution). ## Delivery guarantees Streamkap provides **at-least-once** delivery and appends rows via the Storage Write API, so retries can write the same change more than once — a table can contain **duplicate rows for the same key**. Query the latest version per key (or schedule a cleanup) as shown in [Best Practices](/google-bigquery-best-practices). ## Google Cloud setup Replace the `{ ... }` placeholders. You can use the Cloud Console or the `gcloud`/`bq` CLI: ```bash gcloud theme={null} # 1. Create the dataset (if it doesn't exist yet) bq --location={REGION} mk --dataset {PROJECT_ID}:{DATASET} # 2. Create a service account gcloud iam service-accounts create streamkap-bigquery \ --display-name="Streamkap BigQuery" --project={PROJECT_ID} # 3. Grant BigQuery Data Editor on the dataset (least privilege). # Easiest via Console: BigQuery > {DATASET} > Sharing > Permissions > # Add principal = the service account email, role = BigQuery Data Editor. # 4. Create a JSON key gcloud iam service-accounts keys create key.json \ --iam-account=streamkap-bigquery@{PROJECT_ID}.iam.gserviceaccount.com ``` The dataset must exist before you create the destination. Streamkap creates and evolves **tables** inside it, but never creates the dataset. ## Streamkap setup 1. Go to **Destinations** and choose **BigQuery**. 2. Enter the configuration: * **Name** — a unique, memorable name for this destination. * **JSON key file** — upload the service-account JSON key you created. * **Dataset Name** — the existing dataset to write into. The GCP project is read automatically from the key file. * **Time Partitioning** — partition granularity for auto-created tables (`DAY`, `HOUR`, `MONTH`, `YEAR`). Default `DAY`. Choose `NONE` to create non-partitioned tables. * **Partition Field** *(optional)* — a record field to partition by. Leave blank for ingestion-time partitioning. * **Clustering Fields** *(optional)* — comma-separated fields to cluster by (max 4). * **Partition Expiration in Days** *(optional)* — automatically delete partitions older than this many days from auto-created tables. Leave blank to keep all partitions. Ignored when Time Partitioning is `NONE`. * **Auto-create Tables** — create BigQuery tables for new topics automatically (on by default). * **Allow New Fields** / **Allow Required Field Relaxation** — let the table schema evolve as the source schema changes (both on by default). * **Tasks** — number of parallel tasks. 3. Click **Save**. ## Schema evolution With **Allow New Fields** and **Allow Required Field Relaxation** enabled (the defaults), Streamkap keeps BigQuery tables in step with the source as it changes: | Source change | BigQuery result | | ---------------------------------------------------- | ------------------------------------------------------- | | Add a column | Added as a `NULLABLE` column; existing rows are `NULL`. | | Make a `NOT NULL` column nullable | Column relaxed `REQUIRED` → `NULLABLE`. | | Widen a type (e.g. `INT`→`BIGINT`, longer `VARCHAR`) | No change — both map to the same BigQuery type. | BigQuery cannot **drop** or **rename** columns, and cannot add a `REQUIRED` column to a populated table. A dropped source column is retained in BigQuery and filled with `NULL`s going forward; a renamed column appears as a **new** column alongside the old one. Avoid **incompatible type changes** (e.g. `VARCHAR`→`INT`), which the connector cannot apply. Source column names that aren't valid BigQuery column names (for example names with leading digits or unsupported characters) are adjusted automatically so the data still loads — the BigQuery column may therefore differ slightly from the source column name. ## Data type mapping Mappings are best-effort to the nearest BigQuery equivalent. | Streamkap | BigQuery | | ---------------------------------------------------------------------------- | ------------------------ | | `INT8`, `INT16`, `INT32`, `INT64` | `INTEGER` | | `FLOAT32`, `FLOAT64` | `FLOAT` | | `BOOLEAN` | `BOOLEAN` | | `STRING` | `STRING` | | `BYTES` | `BYTES` | | `org.apache.kafka.connect.data.Decimal` | `NUMERIC` / `BIGNUMERIC` | | `org.apache.kafka.connect.data.Date` | `DATE` | | `org.apache.kafka.connect.data.Time` | `TIME` | | `org.apache.kafka.connect.data.Timestamp`, `io.debezium.time.ZonedTimestamp` | `TIMESTAMP` | | `io.debezium.data.Json` | `JSON` | | `STRUCT` | `RECORD` | | `ARRAY` | repeated field | ## Network access BigQuery is reached over Google Cloud APIs, so there is no IP allowlist to configure — access is controlled entirely by the service account's IAM permissions. ## Related documentation * [BigQuery Best Practices](/google-bigquery-best-practices) — partitioning, clustering, and deduplicating appended data * [Error Reference](/error-reference) — common error codes and resolution steps # Best Practices Source: https://docs.streamkap.com/google-bigquery-best-practices Reduce BigQuery costs and speed up queries with partitioning, clustering, and deduplication patterns for Streamkap's at-least-once CDC streams. BigQuery cost and query speed depend mostly on **how much data each query scans**. Reference fewer columns (avoid `SELECT *`), partition and cluster your tables, and reference the partition/cluster keys in your queries. ## Deduplicating your data Streamkap writes to BigQuery via the Storage Write API and guarantees **at-least-once** delivery. Rows are **appended** — retries can write the same change more than once — so a table can hold **duplicate rows for the same key**. This is expected; you get the current state by deduplicating at query time with a view, and optionally trim stored duplicates with a scheduled cleanup. Streamkap stamps every row with metadata columns you can order by to find the latest version of each record: * `_streamkap_source_ts_ms` — when the change occurred at the source * `_streamkap_offset` — always increases, so it breaks ties when two changes share the same source timestamp ### Latest-record view Expose the deduplicated, current state as a view. Replace the `{ ... }` placeholders: ```SQL SQL theme={null} CREATE VIEW {dataset}.{view} OPTIONS(description="Latest version per key, excluding deleted records") AS SELECT * EXCEPT(_dedupe_rn) FROM ( SELECT *, ROW_NUMBER() OVER ( PARTITION BY {primary_key_column, ...} ORDER BY _streamkap_source_ts_ms DESC, _streamkap_offset DESC ) AS _dedupe_rn FROM {dataset}.{table} ) WHERE _dedupe_rn = 1 AND __deleted = 'false'; -- drop keys whose latest change was a delete ``` The view scans the whole table each time. For large, high-churn tables, also run the scheduled cleanup below so queries (and the view) scan fewer rows. ### Scheduled cleanup (optional) For high-volume tables, [schedule a query](https://cloud.google.com/bigquery/docs/scheduling-queries) to delete superseded rows, keeping only the latest per key: ```SQL SQL theme={null} DELETE FROM {dataset}.{table} t WHERE STRUCT(t.{primary_key_column, ...}, t._streamkap_source_ts_ms) NOT IN ( SELECT AS STRUCT {primary_key_column, ...}, MAX(_streamkap_source_ts_ms) FROM {dataset}.{table} GROUP BY {primary_key_column, ...} ); ``` BigQuery restricts `UPDATE`/`DELETE` on rows still in the streaming write buffer. Scope cleanup to older rows (e.g. add `AND TIMESTAMP_DIFF(CURRENT_TIMESTAMP(), t._streamkap_source_ts_ms, MINUTE) > 90`) to avoid errors on very recently streamed data. ## Datasets ### Use a separate dataset Create a dedicated dataset for Streamkap to avoid conflicts with existing data. For the dataset location, multi-region offers better redundancy at some cost to latency/query performance; single-region is faster. With single-region datasets you can add table snapshots and scheduled exports to improve redundancy. ## Tables ### Set a partition key Partition by a **time unit** (hour, day, month, year) rather than a number — BigQuery is built for analyzing data over time, and time-unit partitioning lets you set partition expiration to drop old data automatically. If there's no natural date/timestamp in your data, partition on the Streamkap change-event timestamp (`_streamkap_source_ts_ms`). See [Choose Daily, Hourly, Monthly or Yearly Partitioning](https://cloud.google.com/bigquery/docs/partitioned-tables#choose_daily_hourly_monthly_or_yearly_partitioning). For database sources, the change-event timestamp is the **snapshot time** for the initial backfill, and the **actual change time** thereafter. Keep this in mind when partitioning on it. ### Expire old partitions automatically For high-volume tables, set **Partition Expiration in Days** on the destination to drop partitions older than a given age — BigQuery deletes the expired partitions for you, so storage and query scans stay bounded. It applies to any time-unit partitioning (`DAY`, `HOUR`, `MONTH`, `YEAR`); fractional days are allowed for sub-day expiration with `HOUR` partitioning. Leave it blank to keep all partitions, and note it has no effect when Time Partitioning is `NONE` (a non-partitioned table has nothing to expire). ### Set a cluster key Cluster by one or more columns you commonly **filter or aggregate on** that have high cardinality. If unsure, your table's primary key column(s) are a reasonable default. See [Partitioning versus Clustering](https://cloud.google.com/bigquery/docs/partitioned-tables#partitioning_versus_clustering). Both the partition field and clustering fields can be set directly on the destination — see [BigQuery setup](/google-bigquery). The partition field and clustering fields are **destination-wide** — one setting applied to **every** table the destination creates. They can't be set per table. Each field you specify must exist in **every** table the destination writes. If a field is missing from a table (or the partition field isn't a date/time column), BigQuery rejects that table's creation and the destination goes into an error state — it does **not** skip the setting or fall back to a default. * **Partition field:** for mixed-schema pipelines, the Streamkap metadata column `_streamkap_source_ts_ms` is a safe universal choice — it's a timestamp present on every table. * **Clustering fields:** cluster on the columns you actually filter or join on (usually the **primary key**). There's rarely a single good clustering key across different tables, so for mixed-schema pipelines either leave clustering unset or give each table (or group of same-shaped tables) its own destination clustered on its own key. Don't reach for `_streamkap_offset` just because it's present everywhere — it's never a query filter, so clustering on it gives no benefit. Partitioning and partition expiration are fixed when a table is first created — changing either later only applies to newly created tables, not existing ones. # Google Cloud MySQL Source: https://docs.streamkap.com/google-cloud-mysql Stream MySQL change data from Google Cloud SQL into Streamkap, covering binary logging, replication user permissions, and heartbeat table setup. ## Prerequisites * MySQL version ≥ 5.7 * MySQL [binlog](https://dev.mysql.com/doc/refman/8.0/en/binary-log.html) enabled * A database user with sufficient privileges to configure the database and create users The following table summarizes all permissions the Streamkap user needs. The setup steps on each source connector page walk through granting each one. **Core permissions (always required):** | Permission | Scope | Purpose | | -------------------- | ------------ | -------------------------------------------------------- | | `REPLICATION CLIENT` | `*.*` | Read binlog metadata and positions | | `REPLICATION SLAVE` | `*.*` | Read binlog events for CDC streaming | | `RELOAD` | `*.*` | Flush operations required for consistent snapshots | | `SHOW DATABASES` | `*.*` | Discover available databases and schemas | | `SELECT` | `{schema}.*` | Read table data during initial and incremental snapshots | **Snapshot signal table (required if GTID is not enabled):** | Permission | Scope | Purpose | | ---------- | ---------------------------- | -------------------------- | | `SELECT` | `streamkap.streamkap_signal` | Read signal table state | | `INSERT` | `streamkap.streamkap_signal` | Trigger snapshot signals | | `UPDATE` | `streamkap.streamkap_signal` | Update signal table state | | `DELETE` | `streamkap.streamkap_signal` | Clean up processed signals | **Heartbeat table (required if heartbeats are enabled):** | Permission | Scope | Purpose | | ---------- | ------------------------------- | ------------------------------ | | `SELECT` | `streamkap.streamkap_heartbeat` | Read heartbeat state | | `INSERT` | `streamkap.streamkap_heartbeat` | Write heartbeat records | | `UPDATE` | `streamkap.streamkap_heartbeat` | Update heartbeat timestamps | | `DELETE` | `streamkap.streamkap_heartbeat` | Clean up old heartbeat records | If GTID is enabled (default on Google Cloud SQL), the signal table is not required. If using **read-only** mode with GTID enabled, the signal table permissions are not needed. The heartbeat table still requires permissions for monitoring and diagnostics. **Combined GRANT statements:** ```sql SQL theme={null} -- Core permissions (always required) GRANT REPLICATION CLIENT, RELOAD, SHOW DATABASES, REPLICATION SLAVE ON *.* TO 'streamkap_user'@'%'; GRANT SELECT ON {schema}.* TO 'streamkap_user'@'%'; -- Signal table (if GTID is not enabled) GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_signal TO 'streamkap_user'@'%'; -- Heartbeat table (if heartbeats are enabled) GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO 'streamkap_user'@'%'; ``` Google Cloud SQL for MySQL has GTID-based replication enabled by default, so the snapshot signal table and its permissions are **not required**. Binary logging is configured via the Cloud SQL console (see step 2 below). ## MySQL Setup ### 1. Grant Database Access * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your database. ### 2. Configure Binary Logging Binary logging records all changes to your database tables. The Connector relies on MySQL's implementation of this. If you are using a read replica, you must enable binary logging on the read replica. * In the Google Cloud console, go to the Cloud SQL Instances page. * Go to Cloud SQL Instances. * Open the more actions menu for the instance you want to enable point-in-time recovery on and click **Edit**. * Under **Customize your instance**, expand the **Data Protection** section. * Select the **Enable point-in-time recovery** checkbox. * Expand **Advanced options**. * Enter the number of days to retain logs, from 3-7. We recommend 7 days. * Click **Save**. ### 3. Create Database User It's recommended to create a separate user and role for the Connector to access your MySQL database. Below is an example script that does that. ```SQL SQL theme={null} -- Replace { ... } placeholders as required -- Identify version SHOW VARIABLES LIKE 'VERSION'; -- On MySQL version 5.6 to 8.0 CREATE USER 'streamkap_user'@'%' IDENTIFIED BY '{password}'; -- On MySQL version 8.0+ CREATE USER 'streamkap_user'@'%' IDENTIFIED WITH mysql_native_password BY '{password}'; -- Grant Permissions GRANT REPLICATION CLIENT, RELOAD, SHOW DATABASES, REPLICATION SLAVE ON *.* TO 'streamkap_user'@'%'; -- Grant Select on all schemas needed GRANT SELECT ON {schema}.* TO 'streamkap_user'@'%'; ``` ### 4. Enable Snapshots To backfill your data, the Connector needs to be able to perform snapshots. See [Snapshots & Backfilling](/snapshots) for more information. This feature is available without any additional configuration because 'GTID-based replication' is enabled by default - and cannot be disabled - for MySQL Cloud SQL instances. See [About replication in Cloud SQL](https://cloud.google.com/sql/docs/mysql/replication#introduction) for more information. ### 5. Heartbeats Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. For read-write connections (when **Read only** is **No** during Streamkap Setup), the Connector writes to the heartbeat table directly. ```SQL SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Switch to the streamkap schema USE streamkap; -- Create the heartbeat table with id, text, and last_update fields CREATE TABLE streamkap_heartbeat ( id INT AUTO_INCREMENT PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Grant permission to the Streamkap user GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO 'streamkap_user'@'%'; -- Insert the first row into the heartbeat table INSERT INTO streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` For read-only connections (when **Read only** is **Yes** during Streamkap Setup), the Connector cannot write to the heartbeat table directly. Instead, you must configure a scheduled job on the **primary** database to generate artificial traffic. These changes will replicate to the read replica, which the Connector then consumes. **Run these commands on the primary database, not the read replica.** The heartbeat table and scheduled event must be created on the primary database. The changes will automatically replicate to the read replica. The MySQL Event Scheduler flag must be enabled on your database (`event_scheduler=ON`). See your provider's documentation: Modifying parameters in a DB parameter group in Amazon RDS Aurora MySQL configuration parameters reference Configuring database flags Server parameters Check if the event scheduler is enabled: ```sql SQL theme={null} SHOW VARIABLES WHERE VARIABLE_NAME = 'event_scheduler'; ``` **Making this setting persistent** For self-hosted MySQL, add `event_scheduler = ON` to your MySQL configuration file (usually `my.cnf` or `my.ini`) in the `[mysqld]` section. For managed databases (RDS, Cloud SQL, Azure), configure the `event_scheduler` parameter through your provider's console or CLI. ```sql SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Create the heartbeat table CREATE TABLE streamkap.streamkap_heartbeat ( id INT AUTO_INCREMENT PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Insert the initial row INSERT INTO streamkap.streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` ```sql SQL theme={null} CREATE EVENT streamkap.streamkap_heartbeat_event ON SCHEDULE EVERY 1 MINUTE DO UPDATE streamkap.streamkap_heartbeat SET text = 'updated_heartbeat', last_update = CURRENT_TIMESTAMP WHERE id = 1; ``` Whichever database user is used to create and run the event scheduler (often the MySQL root user or a dedicated event scheduler user) needs appropriate permissions on the heartbeat table. Additionally, the Streamkap user also needs permissions to monitor the heartbeat table. ```sql SQL theme={null} GRANT EVENT ON streamkap.* TO {event scheduler user}; GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO {event scheduler user}; -- Grant permissions to the Streamkap user for monitoring and diagnostics GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO 'streamkap_user'@'%'; ``` ```sql SQL theme={null} -- View all scheduled events in the streamkap schema SHOW EVENTS IN streamkap; -- View event details SELECT * FROM information_schema.EVENTS WHERE EVENT_SCHEMA = 'streamkap'; -- Disable an event temporarily ALTER EVENT streamkap.streamkap_heartbeat_event DISABLE; -- Enable an event ALTER EVENT streamkap.streamkap_heartbeat_event ENABLE; -- Drop an event DROP EVENT IF EXISTS streamkap.streamkap_heartbeat_event; ``` *** ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **MySQL**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Hostname**: Specify the hostname. * **Port**: Default is `3306`. * **Connect via SSH Tunnel**: The Connector will connect to an SSH server in your network which has access to your database. This is necessary if the Connector cannot connect directly to your database. * See [SSH Tunnel](/ssh-tunnel) for setup instructions. * **Username**: Username to access the database. By default, Streamkap scripts use `streamkap_user`. * **Password**: Password to access the database. * **Read only**: Whether or not to use a read-only connection. GTID is enabled by default on Cloud SQL instances. * **Heartbeats**: Enabled by default. * For **read-write** connections, configure a heartbeat table in the source database and set **Heartbeat Table Database**. See [Heartbeats](#5-heartbeats) for setup instructions. * For **read-only** connections, configure a scheduled heartbeat event on the primary database using the MySQL Event Scheduler, and include the heartbeat table in [Schema and Table Capture](#4-schema-and-table-capture). See [Heartbeats](#5-heartbeats) for setup instructions. * **Connection Timezone**: The timezone of your database. **Timezone conversion** MySQL converts `TIMESTAMP` values from the current time zone to UTC for storage, and back from UTC to the current time zone for retrieval. By default, the current time zone for each connection is the database server's time zone but this option allows you to override that. As long as the time zones remain the same, you get back the same value you store. We recommend using the default `SERVER` option which attempts to detect the session time zone from the values configured on the MySQL server session variables 'time\_zone' or 'system\_time\_zone'. It also reduces the chance of problems with daylight savings adjustment 'fall back' and 'spring forward'. If either time zones change, an [ad-hoc snapshot](/snapshots#triggering-a-snapshot) is recommended so your source and destination timestamps are consistent. ### 3. Advanced Parameters * **Represent binary data as**: Specifies how the data for binary columns e.g. `blob`, `binary`, `varbinary` should be interpreted. Your destination for this data can impact which option you choose. Default is `bytes`. * **Capture Only Captured Databases DDL**: Specifies whether the connector records schema structures from all databases defined in the server (the default) or only those databases for which you've explicitly configured the connector. Specify `true` to capture schema history only for the specific databases you've configured. This is particularly valuable when databases are large, to reduce the volume of DDL stored in the schema history topic. It also improves startup times when the connector restarts or recovers from failures. Default is `false`. See [Schema History Optimization](/schema-history-optimization) for details. * **Capture Only Captured Tables DDL**: Specifies whether the connector records the schema structure for all tables in the configured databases (the default) or only the tables whose changes the connector captures. Specify `true` to capture schema history only for the specific tables you've configured. This is particularly valuable when tables are large, to reduce the volume of DDL statements stored in the schema history topic. It also improves startup times when the connector restarts or recovers from failures. Default is `false`. See [Schema History Optimization](/schema-history-optimization) for details. Click **Next**. ### 4. Schema and Table Capture * **Add Schemas/Tables**: Specify the schema(s) and table(s) for capture. * You can bulk upload here. The format is a simple list of schemas and tables, with each entry on a new row. Save as a `.csv` file without a header. **CDC only captures base tables, not Views** Change Data Capture reads the MySQL binary log (binlog), which only records changes to physical tables. Database Views are query-time computations with no physical storage—they don't generate binlog entries. **What you cannot capture:** Views, temporary tables, MEMORY tables, BLACKHOLE tables, or system tables (information\_schema, performance\_schema). **Solution:** Specify only the underlying base tables that feed your views. You can recreate the view logic in your destination or transformation layer. **VIRTUAL generated columns are not streamed** MySQL [VIRTUAL generated columns](https://dev.mysql.com/doc/refman/8.0/en/create-table-generated-columns.html) are computed when rows are read and are **not** written to the binary log, so live streaming cannot capture or update their values. (STORED generated columns are written to the binary log and stream normally.) Snapshots read the table directly, so they **do** capture a value for VIRTUAL columns — but because streaming cannot keep it current, that value will become stale. **We recommend excluding VIRTUAL generated columns from capture:** in the Source's **Settings → Advanced** section, set **Column Selection Mode** to **Exclusion** and add the column to the **Column Exclusion List** (fully-qualified, e.g. `database.table.column`). If you need the value at your destination, calculate it there — or with a [transform](/transforms) — from the columns it is derived from. Click **Save**. **Adding tables programmatically** -- You can add tables to an existing source via the API or Terraform using the `table.include.list.user.defined` parameter (Terraform: `table_include_list_user_defined`). See the [REST API](/api) for getting started with the API, or [Terraform Resources](/terraform-resources#sources) for Terraform source configuration. **Have questions?** See the [MySQL Source FAQ](/mysql-source-faq) for answers to common questions about MySQL sources, troubleshooting, and best practices. # Google Cloud SQL for PostgreSQL Source: https://docs.streamkap.com/google-cloud-sql-postgresql Stream PostgreSQL change data from Google Cloud SQL to Streamkap with logical decoding, replication user permissions, and heartbeat configuration. ## Prerequisites * PostgreSQL version ≥ 10 * A database user with sufficient privileges to configure the database, including enabling logical replication and creating users ## PostgreSQL Setup ### 1. Grant Database Access * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your database. ### 2. Enable Logical Replication Logical replication is a method of replicating data objects and their changes, based upon their replication identity (usually a primary key). The Connector relies on PostgreSQLs implementation of this. * In the Google Cloud console, go to the Cloud SQL Instances page. * Go to Cloud SQL Instances * Open the more actions menu for the instance you want to enable point-in-time recovery on and click Edit. * Under Customize your instance, expand the Data Protection section. * Select the Enable point-in-time recovery checkbox. * Expand Advanced options. * Enter the number of days to retain logs, from 3-7. We recommend 7 days, * Click Save. **WAL and database storage** The write-ahead logs take up disk space on your database. If you run out of disk space, you may face an interruption to your database. To mitigate this: * Ensure that your database server has ample free space. * Enable sufficient space with auto growth on the database. * Monitor the WAL growth and configure alerts. * Configure the publication (See [Create Publication & Slot](/google-cloud-sql-postgresql#5-create-publication--slot)) to contain only tables you need. ### 3. Create Database User It's recommended to create a separate user and role for the Connector to access your PostgreSQL database. Below is an example script that does that. ```SQL SQL theme={null} -- Replace { ... } placeholders as required CREATE USER streamkap_user PASSWORD '{password}'; ALTER USER streamkap_user WITH REPLICATION; -- Create a role for Streamkap CREATE ROLE streamkap_role; GRANT streamkap_role TO streamkap_user; -- Grant Streamkap permissions on the database, schema and all tables to capture GRANT CONNECT ON DATABASE "{database}" TO streamkap_role; GRANT CREATE, USAGE ON SCHEMA "{schema}" TO streamkap_role; GRANT SELECT ON ALL TABLES IN SCHEMA "{schema}" TO streamkap_role; ALTER DEFAULT PRIVILEGES IN SCHEMA "{schema}" GRANT SELECT ON TABLES TO streamkap_role; ``` ### 4. Enable Snapshots To backfill your data, the Connector needs to be able to perform snapshots. See [Snapshots & Backfilling](/snapshots) for more information. To enable this feature, there are 2 methods available: #### Method 1: Enable read only connection Requires PostgreSQL version 13 or higher. This method is recommended if you cannot create a table in the source database and grant the Connector read/write privileges to that. * Set **Read only** to **Yes** during Streamkap Setup. No other configuration should be necessary. #### Method 2: Create a table in the source database You will need to create the table and give necessary permissions to the `streamkap_user`. The Connector will use this collection for managing snapshots. Below is an example script that does that. The examples below use `streamkap_signal` as the signal table name, but you can choose any name. During [Streamkap Setup](#3-snapshot-settings), provide the full path to your signal table in `schema.table` format (e.g., `streamkap.streamkap_signal`). ```SQL SQL theme={null} -- Create the schema CREATE SCHEMA streamkap; -- Switch to the newly created schema SET search_path TO streamkap; -- Create the table CREATE TABLE streamkap_signal ( id VARCHAR(255) PRIMARY KEY, type VARCHAR(32) NOT NULL, data VARCHAR(2000) NULL ); -- Grant necessary privileges on the table to the role GRANT CREATE, USAGE ON SCHEMA streamkap TO streamkap_role; GRANT SELECT ON ALL TABLES IN SCHEMA streamkap TO streamkap_role; GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE streamkap_signal TO streamkap_role; ``` **Publications and signal tables** When you create the PostgreSQL publication in the next step, if you choose to specify tables for capture instead of all tables, you **must** include your signal table. ### 5. Heartbeats Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. For read-write connections (when **Read only** is **No** during Streamkap Setup), the Connector writes to the heartbeat table directly. ```SQL SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Switch to the streamkap schema SET search_path TO streamkap; -- Create the heartbeat table with id, text, and last_update fields CREATE TABLE streamkap_heartbeat ( id SERIAL PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Grant permission to the Streamkap user GRANT USAGE ON SCHEMA streamkap TO streamkap_user; GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE streamkap_heartbeat TO streamkap_user; -- Insert the first row into the heartbeat table INSERT INTO streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` **Heartbeat tables and PostgreSQL publications** If the `streamkap_pub` publication created during PostgreSQL Setup was for specific tables e.g. `CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, table3, ...;` instead of `FOR ALL TABLES;`, you **must** add the heartbeat table to the publication: `ALTER PUBLICATION streamkap_pub ADD TABLE streamkap.streamkap_heartbeat;`. For read-only connections (when **Read only** is **Yes** during Streamkap Setup), the Connector cannot write to the heartbeat table directly. Instead, you must configure a scheduled job on the **primary** database to generate artificial traffic. These changes will replicate to the read replica, which the Connector then consumes. **Run these commands on the primary database, not the read replica.** The heartbeat table and scheduled job must be created on the primary database. The changes will automatically replicate to the read replica. The `pg_cron` extension must be allowed in your database's configuration. See your provider's documentation for enabling extensions: Scheduling maintenance with pg\_cron Scheduling maintenance with pg\_cron PostgreSQL extensions PostgreSQL extensions pg\_cron extension pg\_cron extension Supported extensions Once allowed, create the extension: ```sql SQL theme={null} CREATE EXTENSION IF NOT EXISTS pg_cron; ``` ```sql SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Create the heartbeat table CREATE TABLE streamkap.streamkap_heartbeat ( id SERIAL PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Insert the initial row INSERT INTO streamkap.streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` ```sql SQL theme={null} -- Schedule a job to update the heartbeat table every minute SELECT cron.schedule( 'streamkap_heartbeat_job', '*/1 * * * *', $$UPDATE streamkap.streamkap_heartbeat SET text = 'updated_heartbeat', last_update = CURRENT_TIMESTAMP WHERE id = 1;$$ ); ``` Whichever database user is used to create and run the cron jobs (often the `postgres` user or a dedicated cron user) needs appropriate permissions on the heartbeat table. Additionally, the Streamkap user also needs permissions to monitor the heartbeat table. ```sql SQL theme={null} GRANT USAGE ON SCHEMA streamkap TO {cron user}; GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO {cron user}; -- Grant permissions to the Streamkap user for monitoring and diagnostics GRANT USAGE ON SCHEMA streamkap TO streamkap_user; GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO streamkap_user; ``` The `pg_cron` extension stores job execution history in the table `cron.job_run_details`. To prevent this table from growing indefinitely: ```sql SQL theme={null} SELECT cron.schedule( 'streamkap_cron_cleanup', '0 0 * * *', $$DELETE FROM cron.job_run_details WHERE end_time < now() - interval '7 days';$$ ); ``` ```sql SQL theme={null} -- View all scheduled jobs SELECT * FROM cron.job; -- View recent job execution history SELECT * FROM cron.job_run_details ORDER BY start_time DESC LIMIT 10; -- Unschedule a job (replace {jobid} with the actual job ID) SELECT cron.unschedule({jobid}); ``` **Heartbeat tables and PostgreSQL publications** If the `streamkap_pub` publication was created for specific tables (e.g., `CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, ...;`) instead of `FOR ALL TABLES`, you **must** add the heartbeat table to the publication: ```sql SQL theme={null} ALTER PUBLICATION streamkap_pub ADD TABLE streamkap.streamkap_heartbeat; ``` **Logical-message heartbeats** are an alternative to a heartbeat table. The Connector itself calls `pg_logical_emit_message()` on each beat, which appends a transactional record to the write-ahead log (WAL). PostgreSQL treats that record like any other committed change, so the replication slot advances and WAL files are released—without any new table, scheduled job, or write privileges on the source database. **When to choose this over a heartbeat table** Use **logical-message heartbeats** when you want zero footprint in the source database itself—no schemas, tables, scheduled jobs, or write grants to maintain. Use a **heartbeat table** when you want the heartbeat to be visible as an ordinary row that operators can `SELECT` to confirm liveness, or when the requirements below rule logical-message out. **Requirements** * **Streamkap must be connected directly to a primary**, not to a read replica or other hot standby. The Connector executes `pg_logical_emit_message()` against the database it's connected to, and that function writes to the WAL—hot standbys are read-only and will reject the call with an error like `cannot execute ... during recovery`. If your Streamkap source is configured against a replica, use the **Read-only connections** tab instead. * PostgreSQL **14 or later** on the primary. Earlier versions are not supported because the built-in `pgoutput` replication plugin only began streaming logical-decoding messages to the Connector in PostgreSQL 14. On PostgreSQL 13 and earlier, `pg_logical_emit_message()` still appends records to the WAL, but the Connector cannot read them and the replication slot will not advance. * The Streamkap user needs `SELECT` privileges only—no `INSERT`, `UPDATE`, or `DELETE` on any heartbeat table. * Logical replication must already be enabled for the Connector itself (this is a prerequisite for any PostgreSQL source, regardless of heartbeat mode). During [Connection Settings](#2-connection-settings): * Set **Heartbeats** to **Yes**. * Leave **Heartbeat Table Schema** blank. * Set **Use Logical-Message Heartbeat** to **Yes**. No further setup is required in the source database. The Connector will issue the heartbeat query once per minute using the standard Streamkap heartbeat interval. After enabling, you can confirm that the replication slot is advancing against an otherwise idle database. Run on the primary: ```sql SQL theme={null} SELECT slot_name, confirmed_flush_lsn, restart_lsn FROM pg_replication_slots WHERE slot_name = 'streamkap_pgoutput_slot'; ``` Re-run the query after one or two minutes. Both `confirmed_flush_lsn` and `restart_lsn` should move forward. **What the Connector sends** When this mode is enabled, the Connector issues the following query on each beat: ```sql SQL theme={null} SELECT pg_logical_emit_message(true, '__debezium-heartbeat', now()::varchar); ``` The first argument (`true`) makes the emit **transactional**. Non-transactional emits advance the replication slot's `confirmed_flush_lsn` but do not recompute global WAL retention across all slots, so WAL files can still accumulate on an idle source database—transactional emits avoid that. The transactional cost is negligible (one extra transaction ID per beat, well below PostgreSQL's wraparound horizon at the default one-minute interval). The heartbeat record is filtered out before reaching Kafka, so it does not appear as a record in any topic in your pipeline. ### 6. Create Publication & Slot Publications contain a set of change events for the tables you want the Connector to capture. * Create a publication for your tables. You can create a publication for all tables or selected tables. ```SQL SQL - All Tables theme={null} -- Create a publication for all tables to capture CREATE PUBLICATION streamkap_pub FOR ALL TABLES WITH (publish_via_partition_root = true); ``` ```SQL SQL - Selected Tables theme={null} -- Create a publication for specific tables to capture CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, table3, ... WITH (publish_via_partition_root = true); -- Verify the tables to capture were added to the publication SELECT * FROM pg_publication_tables where pubname = 'streamkap_pub'; ``` **Altering publications** You cannot alter `FOR ALL TABLES` publications to include/exclude tables. If you set up a `FOR ALL TABLES` publication and later decide to change that, you have to drop the publication and create another to include specific tables e.g. `CREATE PUBLICATION ... TABLE table1, table2, table3, ...`. However, any change events that occur before the new publication's created will not be included in it, so a snapshot's required to ensure they are not missed by your Streamkap pipelines. You should also stop the [Source](https://app.streamkap.com/connectors/sources) *before* changing the publication. A replication slot represents a stream of change events the Connector reads from. * Create a replication slot. ```SQL SQL theme={null} -- Create a logical replication slot SELECT pg_create_logical_replication_slot('streamkap_pgoutput_slot', 'pgoutput'); -- Verify the replication slot is working (this may take a few moments to return the count) SELECT count(*) FROM pg_logical_slot_peek_binary_changes('streamkap_pgoutput_slot', null, null, 'proto_version', '1', 'publication_names', 'streamkap_pub'); ``` **Replication slot auto-creation** If the Connector cannot find the replication slot named in [Replication Settings](/google-cloud-sql-postgresql#4-replication-settings) during Streamkap Setup, it will attempt to create one automatically as it cannot read the database transaction logs without one. This is assuming the database user has been created `WITH REPLICATION` privileges as per [Create Database User](/google-cloud-sql-postgresql#3-create-database-user). #### Excluding columns from replication By default a publication streams every column of each captured table. On **PostgreSQL 15+** you can attach a *column list* to a table in the publication so that only the listed columns are replicated — any column you leave out is excluded at the source and never enters the replication stream. ```SQL SQL - Selected Columns theme={null} -- Only the listed columns are published; every other column is excluded. -- Each table's list MUST include its replica identity (primary key), -- otherwise UPDATE/DELETE for that table cannot be replicated. CREATE PUBLICATION streamkap_pub FOR TABLE public.users (id, email, created_at, updated_at), public.orders (id, user_id, status, total), public.products (id, sku, name, price); -- Add or change a table's column list on an existing publication ALTER PUBLICATION streamkap_pub SET TABLE public.users (id, email, updated_at); -- Verify the published columns per table SELECT pubname, schemaname, tablename, attnames FROM pg_publication_tables WHERE pubname = 'streamkap_pub'; ``` * Requires **PostgreSQL 15 or later**. On earlier versions a publication always streams all columns. * Each table's column list **must include the table's replica identity** — its primary key, or the columns from `REPLICA IDENTITY USING INDEX` / `FULL`. Otherwise `UPDATE` and `DELETE` events for that table are not replicated. * Column lists are only valid with `FOR TABLE`, not `FOR ALL TABLES`. A table listed without parentheses publishes all of its columns, so you can mix restricted and unrestricted tables in one publication. A publication column list only governs **streamed (CDC) changes**. The initial snapshot reads columns directly from the table, so an excluded column can still appear in snapshot records. If a column must never be captured, exclude it from the snapshot as well, not only from the publication. *** ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **PostgreSQL**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Hostname**: Specify the hostname. * **Port**: Default is `5432`. * **Connect via SSH Tunnel**: The Connector will connect to an SSH server in your network which has access to your database. This is necessary if the Connector cannot connect directly to your database. * See [SSH Tunnel](/ssh-tunnel) for setup instructions. * **Username**: Username to access the database. By default, Streamkap scripts use `streamkap_user`. * **Password**: Password to access the database. * **Database**: Specify the database to stream data from. * **Read only**: Whether or not to use a read-only connection. Requires PostgreSQL version 13 or higher. * When connecting to a read replica, set this to **Yes** to support Streamkap snapshots. * **Heartbeats**: Enabled by default. Choose one of three modes—see [Heartbeats](#5-heartbeats) for setup instructions: * **Read-write** connections: configure a heartbeat table in the source database and set **Heartbeat Table Schema**. * **Read-only** connections: configure a scheduled heartbeat job on the primary database using `pg_cron`, and include the heartbeat table in [Schema and Table Capture](#6-schema-and-table-capture). * **Logical-message** (PostgreSQL 14+, works under read-only): set **Use Logical-Message Heartbeat** to **Yes** and leave **Heartbeat Table Schema** blank. No table or write privileges required on the source. ### 3. Snapshot Settings If you set Read only to **No**, you will need to create a snapshot signal table and give permissions to the `streamkap_user`. See [Enable Snapshots](/google-cloud-sql-postgresql#4-enable-snapshots) for setup instructions. * **Signal Table**: Full path to the signal table including schema and table name (e.g., `streamkap.streamkap_signal`). This table is used for incremental snapshotting. See [Enable Snapshots](#4-enable-snapshots) for setup instructions. ### 4. Replication Settings * **Replication Slot Name**: The name of the replication slot for the connector to use. Default is `streamkap_pgoutput_slot`. * **Publication Name**: The name of the publication for the connector to use. Default is `streamkap_pub`. ### 5. Advanced Parameters * **SSL mode**: Whether to use an encrypted connection to the PostgreSQL server. By default, it's required. * **Prefix with Database Name?**: Changes the format of topics to `DatabaseName_TopicName` * **Represent binary data as**: Specifies how the data for binary columns e.g. `blob`, `binary`, `varbinary` should be interpreted. Your destination for this data can impact which option you choose. Default is `bytes`. Click **Next**. ### 6. Schema and Table Capture * **Add Schemas/Tables**: Specify the schema(s) and table(s) for capture * You can bulk upload here. The format is a simple list of schemas and tables, with each entry on a new row. Save as a `.csv` file without a header. **CDC only captures base tables, not Views** Change Data Capture reads the PostgreSQL write-ahead log (WAL), which only records changes to physical tables. Database Views are query-time computations with no physical storage—they don't generate WAL entries. **What you cannot capture:** Views, temporary tables, unlogged tables, foreign tables (FDW), or system tables (information\_schema, pg\_catalog). **Solution:** Specify only the underlying base tables that feed your views. You can recreate the view logic in your destination or transformation layer. **Generated columns are not kept up to date** PostgreSQL [generated columns](https://www.postgresql.org/docs/current/ddl-generated-columns.html) (defined with `GENERATED ALWAYS AS (...) STORED`) are calculated by the database from other columns. PostgreSQL does **not** include them in the replication stream Streamkap reads, so live streaming cannot keep their values current. **We recommend excluding generated columns from capture.** In the Source's **Settings → Advanced** section, set **Column Selection Mode** to **Exclusion** and add the column to the **Column Exclusion List** (fully-qualified, e.g. `schema.table.column`). If you need the value at your destination, calculate it there — or with a [transform](/transforms) — from the columns it is derived from. **Snapshots:** An incremental snapshot (the watermarked **Filtered** and **Full** options) of a table that contains a generated column will **fail** unless the column is excluded. **Blocking** and **Parallel** snapshots are not affected, though any value they capture will become stale because streaming cannot update it. See [Snapshots & Backfilling](/snapshots). PostgreSQL 18 and later can optionally stream stored generated columns when the publication is created with `publish_generated_columns = stored`; earlier versions cannot. Click **Save**. ## Troubleshooting When upgrading the PostgreSQL database used by Streamkap, there are specific steps to prevent data loss and ensure continued operation.

Streamkap handles network failures and outages well. If a monitored database stops, the connector resumes from the last recorded log sequence number (LSN) once communication is restored. It retrieves this offset and queries PostgreSQL for a matching LSN in the replication slot.

A replication slot is required for change capture, but PostgreSQL removes slots during upgrades and doesn’t restore them. When the connector restarts, it requests the last known offset, but PostgreSQL cannot return it.

Creating a new replication slot isn't enough to prevent data loss. New slots only track changes from their creation point and lack earlier offsets. The connector fetches its last known offset from Kafka but can't retrieve corresponding data from the new slot. It skips older change events and resumes from the latest log position, causing silent data loss with no warnings.

### Procedure Follow these steps to minimize data loss. Note that a few steps may require support from Streamkap. We recommend notifying us about your database upgrade ahead of time to ensure you have the necessary support. * Using your database's upgrade procedure, ensure writes to it have stopped. * Allow the connector to capture all change events before starting the upgrade procedure. Ask Streamkap to confirm this for you. **Interrupting change data capture** If all the events were not captured before stopping the Source and upgrading the database, you can perform a snapshot (after the procedure is completed) in Streamkap for that Source to ensure no change events were missed. * Assuming all events are captured, stop the Source in the Streamkap app. This flushes the last records and saves the last offset. * Stop the database and upgrade it using your upgrade procedure. Once the database is upgraded, and **before** allowing writes again: * Recreate the logical replication slot, otherwise, Streamkap will miss changes. See [Create Publication & Slot](/google-cloud-sql-postgresql#5-create-publication--slot). * Verify the publication for Streamkap exists; recreate it if necessary: [Create Publication & Slot](/google-cloud-sql-postgresql#5-create-publication--slot). **Update the Source in the Streamkap app** If you have chosen different names for the logical replication slot or publication, make sure to update them in the Streamkap setup page for the relevant PostgreSQL Source. In this case, contact Streamkap, as they may need to reset your Connector's offsets in this scenario. * Restore write access to the database. * Resume or restart the Source in the Streamkap app.
Introduced in PostgreSQL 13, the `REPLICA IDENTITY` table setting controls what data is logged for row updates and deletes. By default, only the primary key and Streamkap metadata column values are retained for deleted records. All other columns will be empty. This leaves you with an incomplete record. If you require - for auditing and historical tracking purposes - **all** column values for deleted records, or if your deletion strategy for your destination is 'soft deletes' (retain the deleted record with a deletion flag), you should set the `REPLICA IDENTITY` to `FULL` for all capture tables. ```SQL SQL theme={null} ALTER TABLE {table} REPLICA IDENTITY FULL; ``` This ensures complete data retention. For capturing partitioned tables, it's essential to enable `publish_via_partition_root` on the publication. By default, changes to partitions are published from the partition itself. The connector expects changes to come from the root table. To ensure compatibility and consistent replication, enable this setting: `ALTER PUBLICATION streamkap_pub SET (publish_via_partition_root = true);`
# Heartbeat Configuration Source: https://docs.streamkap.com/heartbeat-configuration Configure heartbeats for PostgreSQL, SQL Server, and other sources to keep replication slots advancing and prevent WAL or log bloat on low-traffic databases. ## Why Heartbeats Matter Streamkap connectors use **offsets** (bookmarks) to track their position in the database's change log (WAL, binlog, redo log, or change stream). When no data changes occur for an extended period, these offsets can become stale. If the database rotates or purges its change log files before the connector advances past them, you risk: * **Data loss** -- the connector can no longer read the changes it missed. * **Log file accumulation** -- some databases retain logs indefinitely until the consumer advances, consuming disk space. * **Connector restart failures** -- a stale offset may point to a log position that no longer exists. Heartbeats solve this by generating periodic change events that keep the connector's position advancing, even when no real data changes occur on your monitored tables. ## How Heartbeats Work Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. **PostgreSQL: logical-message heartbeats (no source-DB table required)** PostgreSQL 14+ sources can optionally use **logical-message heartbeats** instead of a heartbeat table. Each beat emits a transactional record to the write-ahead log via `pg_logical_emit_message()`, which advances the replication slot without requiring any new table, scheduled job, or write privileges on the source database. This is the recommended option when you don't own the source database or want to avoid adding objects to it. See the **Logical-message** tab in your connector's setup page for details. ## Recommended Interval The default heartbeat interval is **1 minute**, which works well for most deployments. This interval balances the need for timely offset advancement against the minimal overhead of a single row update. | Scenario | Recommended interval | | -------------------------------------------------- | ----------------------------------------------------------------- | | Standard deployments | 1 minute | | Very low-traffic databases (hours between changes) | 1 minute | | High-traffic databases (changes every few seconds) | 1--5 minutes (heartbeats are less critical but still recommended) | ## Setup Instructions Heartbeat configuration varies by database type. Follow the setup guide for your specific source connector: **MySQL / MariaDB:** * [MySQL (Generic)](/mysql-generic#6-heartbeats) * [Amazon RDS MySQL](/amazon-rds-mysql#7-heartbeats) * [Amazon RDS Aurora MySQL](/amazon-rds-aurora-mysql#7-heartbeats) * [Google Cloud MySQL](/google-cloud-mysql#5-heartbeats) * [Azure MySQL Database](/azure-mysql-database#5-heartbeats) * [MariaDB (Generic)](/mariadb-generic#heartbeats) * [Amazon RDS MariaDB](/amazon-rds-mariadb#heartbeats) **PostgreSQL:** * [PostgreSQL (Self-hosted)](/postgresql-self-hosted#5-heartbeats) * [Amazon RDS PostgreSQL](/postgresql-on-amazon-rds#5-heartbeats) * [Amazon RDS Aurora PostgreSQL](/amazon-rds-aurora-postgresql#5-heartbeats) * [Amazon RDS PostgreSQL Serverless](/amazon-rds-postgresql-serverless#5-heartbeats) * [Google Cloud SQL PostgreSQL](/google-cloud-sql-postgresql#5-heartbeats) * [Azure Database for PostgreSQL](/postgresql-on-azure-database#5-heartbeats) * [Neon](/neon-postgresql-cdc#4-heartbeats) * [AlloyDB](/alloydb#5-heartbeats) * [Supabase](/supabase#5-heartbeats) **SQL Server:** * [SQL Server (Generic)](/sql-server-generic#5-heartbeats) * [Amazon RDS SQL Server](/amazon-rds-sql-server#5-heartbeats) * [Azure SQL Database](/sql-server-on-azure-sql-database#5-heartbeats) * [Google Cloud SQL SQL Server](/sql-server-on-google-cloud-sql#5-heartbeats) **Oracle:** * [Oracle (Generic)](/oracle-generic#6-heartbeats) * [Amazon RDS Oracle](/amazon-rds-oracle#6-heartbeats) **MongoDB:** * [MongoDB Atlas](/mongodb-atlas#4-heartbeats) * [MongoDB (Self-hosted)](/mongodb-1#4-heartbeats) ## Troubleshooting **Symptoms:** The connector logs errors about a missing heartbeat table, or heartbeat events are not being generated. **Solutions:** * Verify the heartbeat table exists in the correct schema. Run a `SELECT` query against it to confirm. * For PostgreSQL, check that the `streamkap` schema exists: `SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'streamkap';` * For MySQL/MariaDB, check that the `streamkap` database exists: `SHOW DATABASES LIKE 'streamkap';` * For Oracle, remember the table is in the `STREAMKAP_USER` schema, not a separate `streamkap` schema. * Ensure the initial row has been inserted (`INSERT INTO ... VALUES ('test_heartbeat')`). **Symptoms:** The connector cannot read from or write to the heartbeat table. Errors reference permission denied or access issues. **Solutions:** * Verify the Streamkap user has `SELECT`, `UPDATE`, `INSERT`, and `DELETE` permissions on the heartbeat table. * For PostgreSQL, also verify `USAGE` permission on the `streamkap` schema. * For SQL Server, verify the `streamkap_role` has the required grants. * For Oracle, verify permissions are granted to both `STREAMKAP_USER` and `C##STREAMKAP_USER`. * Re-run the `GRANT` statements from the setup instructions for your database type. **Symptoms:** The heartbeat table exists and has a row, but the `last_update` timestamp is not being updated. **Solutions:** * **PostgreSQL:** Verify the `pg_cron` job is running: `SELECT * FROM cron.job;` and check `SELECT * FROM cron.job_run_details ORDER BY start_time DESC LIMIT 5;` for errors. If you cannot run `pg_cron` on the primary—for example, because you don't own the database—consider switching to **logical-message heartbeats** (PostgreSQL 14+), which require no scheduled job or table. See the **Logical-message** tab in your connector's heartbeat setup section. * **MySQL/MariaDB:** Verify the event scheduler is enabled: `SHOW VARIABLES WHERE VARIABLE_NAME = 'event_scheduler';` should return `ON`. Check event status: `SHOW EVENTS IN streamkap;` * **MongoDB:** Verify your scheduled trigger or cron job is executing. For Atlas, check the trigger logs in the App Services console. * Confirm the scheduled job is updating the correct row (typically `WHERE id = 1`). **Symptoms:** The heartbeat table is being updated, but the connector does not detect the changes. **Solutions:** * **PostgreSQL:** If you created a publication for specific tables, add the heartbeat table: `ALTER PUBLICATION streamkap_pub ADD TABLE streamkap.streamkap_heartbeat;` * **SQL Server:** Verify CDC is enabled on the heartbeat table using `SELECT is_tracked_by_cdc FROM sys.tables WHERE name = 'streamkap_heartbeat';` -- the result should be `1`. * **MongoDB:** Ensure the heartbeat collection is included in the connector's namespace configuration. If you configured specific databases/collections, add `streamkap.streamkap_heartbeat`. **Symptoms:** Even with heartbeats configured, the connector reports stale offsets or cannot resume from its last position. **Solutions:** * Verify that both Layer 1 (connector heartbeats) and Layer 2 (database heartbeats) are active. * Check your database's log retention settings: * **PostgreSQL:** Ensure `wal_keep_size` or `wal_keep_segments` is sufficient. See [Monitoring the PostgreSQL WAL Log](/monitoring-the-postgresql-wal-log). * **MySQL:** Verify `binlog_expire_logs_seconds` (or `expire_logs_days`) is set to at least 3 days. See [MySQL Low Volume Log Rotation](/mysql-low-volume-log-rotation). * Ensure the heartbeat interval (1 minute) is shorter than your log rotation period. * Contact [Streamkap support](mailto:support@streamkap.com) if the issue persists. ## Quick Reference | Database | Heartbeat table | Schema/Location | Read-only support | Scheduler for read-only | No-table alternative | | ---------- | --------------------- | --------------------------------- | --------------------- | ----------------------------------- | ------------------------ | | PostgreSQL | `streamkap_heartbeat` | `streamkap` schema | Yes | `pg_cron` extension | Logical-message (PG 14+) | | MySQL | `streamkap_heartbeat` | `streamkap` database | Yes | MySQL Event Scheduler | — | | Oracle | `STREAMKAP_HEARTBEAT` | `STREAMKAP_USER` schema | No | N/A | — | | SQL Server | `streamkap_heartbeat` | `streamkap` schema | No | N/A | — | | MariaDB | `streamkap_heartbeat` | `streamkap` database | Yes | MariaDB Event Scheduler | — | | MongoDB | `streamkap_heartbeat` | `streamkap` database (collection) | N/A (always external) | Atlas Triggers / cron / K8s CronJob | — | # HTTP Sink Source: https://docs.streamkap.com/http-sink Deliver Kafka topic records to an external HTTP endpoint # Overview Use the HTTP Sink Destination to push streamed records from your topics to any reachable HTTP(S) endpoint. This is useful for integrating with ingestion APIs, webhooks, or custom services that accept POST requests. # Prerequisites * An HTTPS endpoint that accepts POST requests (e.g. ingestion API) * Decision on authentication: None, Static Authorization header (Bearer/API key, Basic Auth), or OAuth2 Client Credentials * Capacity planning for batch size vs endpoint rate limits # Endpoint Preparation Before adding the Destination: 1. Confirm the endpoint supports idempotency (recommended). If possible, design it to ignore duplicate records using a unique key. 2. Record the base URL you will send data to. Example: `https://api.example.com/ingest`. 3. If using a static token/key, create it and store securely. 4. If using OAuth2 Client Credentials, collect: * Token URL * Client ID * Client Secret # Streamkap Setup (UI) 1. Navigate to [Destinations](https://app.streamkap.com/connectors/add?tab=Destinations) and choose **HTTP Sink**. 2. Fill in the fields: 1. **Name** – A memorable identifier. 2. **URL** – The full HTTPS endpoint to POST each record or batch to. 3. **Authentication Type** – Select `None`, `Static`, or `OAuth2`. * **Static**: Paste the Authorization header value. Do not include the word `Authorization:` – just the header value. Examples: * **Bearer Token**: `Bearer eyJ...` * **API Key** (standard format): `ApiKey sk-1234567890abcdef` * **API Key** (provider-specific): Check your API documentation—enter `` directly or the format specified (e.g., `Bearer`, `Token`, etc.). * **Basic Auth**: `Basic dXNlcjpwYXNzMTIz` (base64-encoded `username:password`) * **Custom Headers** (non-Authorization): If your API uses custom headers like `X-API-Key`, use the **Additional Headers** field with format `X-API-Key:sk-1234567890abcdef`. * **OAuth2**: Enter Token URL, Client ID, Client Secret, optional Scope. Leave defaults for grant type unless your provider differs. 4. **Content-Type** – Enter the media type expected by your endpoint (commonly `application/json`). Must be non-blank. 5. **Additional Headers** – Comma separated `Header:Value` pairs (case-insensitive). Omit duplicates. Example: `X-Env:prod,X-Source:streamkap`. 6. **Batching** – Enable if your endpoint supports multiple records per request. * **Batch Max Size** – Maximum number of records per batch (default 500). Start conservatively (50–200). Increase after observing latency. * **Enable Batch Buffering** – When enabled, records are held and only sent when either batch max size is reached OR batch max time expires. When disabled, records are sent immediately as they arrive (but still grouped up to batch max size). * **Batch Max Time (ms)** – Maximum time (in milliseconds) to hold records before flushing, even if batch size is not reached (default 1000 ms). Useful to ensure latency-sensitive endpoints receive data within a predictable window. * **Separator** – Typically `\n` for newline-delimited JSON. Leave default unless your endpoint requires another delimiter. * **Prefix/Suffix** – Optional text inserted before the first and after the last record. Leave blank unless wrapping in JSON array (e.g. Prefix `[` Separator `,\n` Suffix `]`). 7. **Timeout (Seconds)** – How long to wait for an HTTP response. Raise if your endpoint is slow; default works for most. 8. **Retries** – Max attempts for transient HTTP failures. Increase only if backend occasionally returns 5xx. 9. **Backoff (ms)** – Delay between retries. Adjust to respect rate limits. 10. **Decimal Format** – Choose numeric if the endpoint expects plain numbers; otherwise keep base64. 3. Save the Destination. 4. Attach Pipelines / Topics to the Destination as needed. # How It Works Each record (or batch) is serialized (JSON by default) and POSTed to the configured URL. Batching writes multiple serialized records separated by the chosen delimiter within one request. Retries apply to whole batches. Failures after max retries raise task errors. # Authentication Modes (UI Perspective) * **None** – No Authorization header sent. * **Static** – Streamkap sends `Authorization: ` exactly once per request. Supports multiple formats: * **Bearer Tokens**: `Authorization: Bearer ` for API tokens and JWTs. * **API Keys**: Depending on your provider, enter one of these formats: * `Authorization: ApiKey ` – Some APIs use this standardized format. * `Authorization: ` – Others accept the key directly without a prefix. * If your API uses a custom header like `X-API-Key: `, use the **Additional Headers** field instead. * **Basic Auth**: `Authorization: Basic ` for username/password authentication. You must manually convert `username:password` to Base64 before entering it. Online tools or command-line utilities (e.g., `echo -n "user:pass" | base64`) can help with encoding. * **OAuth2** – Streamkap fetches and refreshes tokens automatically using Client Credentials flow. Tokens are inserted as `Authorization: Bearer `. # Batching Guidance * Start small to measure endpoint latency (e.g. 100 records). * Increase gradually while monitoring HTTP 429 or 5xx responses. * Use newline separation for simple lines-based ingestion; use array wrapping (Prefix `[`, Separator `,\n`, Suffix `]`) if the endpoint expects a JSON array. # Batch Buffering & Flushing Behavior When batching is enabled, you control when batches are sent using the **Enable Batch Buffering** setting: ## Without Batch Buffering (Default) * Records are immediately POSTed in batches as they arrive, up to the configured **Batch Max Size**. * If fewer records arrive before the next batch, they are sent without waiting. * Use this mode for low-latency, record-by-record processing with minimal delay. ## With Batch Buffering * Records are held in memory and only sent when **either** condition is met: 1. **Batch reaches max size** – The batch hits the configured **Batch Max Size** limit. 2. **Time window expires** – The **Batch Max Time (ms)** interval elapses since the first record arrived. * This mode ensures predictable latency and can improve endpoint efficiency by batching sparse traffic. ### Configuration Dependencies | Setting | Requires | Effect | | --------------------------------- | ---------------------------- | --------------------------------------------------------------- | | **Enable Batching** | – | When enabled, all batch-related options become available. | | **Batch Max Size** | Batching enabled | Maximum records per request. Default 500; affects payload size. | | **Enable Batch Buffering** | Batching enabled | When enabled, **Batch Max Time** becomes available. | | **Batch Max Time (ms)** | Batching + Buffering enabled | Default 10000 ms. Only active when buffering is on. | | **Batch Prefix/Suffix/Separator** | Batching enabled | Control JSON or text formatting of the batch payload. | ### Example Scenarios **Scenario 1: Real-time ingestion, low latency** * Enable Batching: ✓ * Enable Batch Buffering: ✗ * Batch Max Size: 100 * **Result:** Records sent every \~100 records or immediately if fewer arrive, minimizing delay. **Scenario 2: Bulk ingestion, efficient batching** * Enable Batching: ✓ * Enable Batch Buffering: ✓ * Batch Max Size: 1000 * Batch Max Time: 5000 ms * **Result:** Records held up to 5 seconds or until 1000 records accumulated, maximizing throughput. **Scenario 3: Hybrid (responsive but efficient)** * Enable Batching: ✓ * Enable Batch Buffering: ✓ * Batch Max Size: 500 * Batch Max Time: 2000 ms * **Result:** Balances latency and efficiency; sends between 2–5 seconds depending on record volume. The connector does not de-duplicate at HTTP layer. Implement idempotency server-side (e.g. reject duplicates based on a unique record key). Include that key in the payload your endpoint parses. # Security Notes * Secrets (client secret, static token) are stored encrypted. * Prefer HTTPS only; do not use plain HTTP for production. * Validate payload size limits on the receiving side to prevent oversized batch denial-of-service. # Limitations * No automatic schema negotiation with the endpoint – ensure the receiving service tolerates new fields. * Retries are whole-batch; a single problematic record causes re-send of the entire batch. * No built-in rate limiting; configure backoff and batch size to respect endpoint limits. # Overview Source: https://docs.streamkap.com/ibm-db2 Streamkap supports syncing Db2 in real-time using change data capture for: * [Db2 (Generic)](/db2-generic) If you don't see your platform listed, please contact us as we're happy to help find and provide the setup documentation you need. # Overview Source: https://docs.streamkap.com/iceberg If you don't see your platform listed, please contact us as we're happy to help find and provide the setup documentation you need. # Overview Source: https://docs.streamkap.com/informix Streamkap supports syncing IBM Informix in real-time using change data capture for: * [Informix (Generic)](/informix-generic) If you don't see your platform listed, please contact us as we're happy to help find and provide the setup documentation you need. # Informix (Generic) Source: https://docs.streamkap.com/informix-generic IBM Informix Change Data Capture Self Hosted or Generic with Streamkap ## Prerequisites **IBM Informix Change Data Capture required** The Connector uses Informix's built-in CDC API, which is exposed through the `syscdcv1` system database. `syscdcv1` ships with Informix and must be present on the server. CDC is supported on Informix **Enterprise Edition**. * Informix version ≥ 12.10 (14.10 or 15 recommended) * The source database must be created **with logging** (ANSI-logged or buffered-log). Non-logged databases cannot be captured. * A database user with privileges to: * Connect to the source database and `syscdcv1` * `SELECT` on tables you want to capture * `SELECT`, `INSERT`, `UPDATE` on the Streamkap signal table ## Informix Setup The Informix CDC API reads the physical logical log and streams row-level change events back to the Connector over a JDBC session against `syscdcv1`. The Connector opens a capture session, enables Full Row Logging on the tables you've selected, and then consumes change records as they are written to the log. Streamkap keeps Full Row Logging enabled on captured tables across Connector restarts (e.g. pod reschedules, deployment updates) so that DML occurring while the Connector is disconnected is still written to the log and can be replayed on reconnect. Without this, a restart would create a silent data gap. ### 1. Grant Database Access * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your database. ### 2. Create Database User It's recommended to create a separate user for Streamkap. Below is an example script that does that. ```SQL SQL theme={null} -- Run as user 'informix' or another DBA -- Replace { ... } placeholders as required -- Create the OS-level user 'streamkap_user' on the Informix host first -- (e.g. `useradd streamkap_user` on Linux). Informix authenticates against OS users -- unless you are using an alternative authentication module. -- Grant Streamkap access to the source database DATABASE {databaseName}; GRANT CONNECT TO streamkap_user; -- Grant SELECT on every table Streamkap should capture GRANT SELECT ON {schemaName}.{tableName} TO streamkap_user; -- Grant access to the CDC API database DATABASE syscdcv1; GRANT CONNECT TO streamkap_user; ``` ### 3. Enable Change Data Capture CDC itself is activated at runtime by the Connector — you do not need to manually enable full row logging on each table. However, the `syscdcv1` database must be available on the server and the source database must be a logged database. Verify the `syscdcv1` database exists: ```SQL SQL theme={null} -- Run as user 'informix' (DBA) DATABASE sysmaster; SELECT name FROM sysdatabases WHERE name = 'syscdcv1'; ``` If `syscdcv1` is missing, create it by running the installer script shipped with Informix: ```bash Shell theme={null} # Run on the Informix server host as the 'informix' user dbaccess sysadmin $INFORMIXDIR/etc/syscdcv1.sql ``` Verify your source database is logged: ```SQL SQL theme={null} -- Replace {databaseName} placeholder DATABASE sysmaster; SELECT name, is_logging, is_buff_log, is_ansi FROM sysdatabases WHERE name = '{databaseName}'; ``` If `is_logging` is `0`, enable logging on the database: ```bash Shell theme={null} # Replace {databaseName} placeholder # Requires a level-0 archive to exist before logging can be enabled ontape -s -L 0 ondblog buf {databaseName} ``` ### 4. Enable Snapshots To backfill your data, the Connector needs to be able to perform **snapshots** (See [Snapshots & Backfilling](/snapshots) for more information). To enable this process, a table must be created for the Connector to use. Please create the signal table with the name `streamkap_signal`. It will not be recognised if given another name. ```SQL SQL theme={null} -- Run as a DBA (e.g. the 'informix' user) so the table can be created on behalf of streamkap_user. -- Replace {databaseName} placeholder. DATABASE {databaseName}; -- Create the signal table owned by streamkap_user. -- Using the explicit schema prefix guarantees the table is owned by streamkap_user -- regardless of which user runs the script, and makes the fully-qualified name you -- enter in the Streamkap UI unambiguous: streamkap_user.streamkap_signal. CREATE TABLE streamkap_user.streamkap_signal ( id VARCHAR(255) NOT NULL PRIMARY KEY, type VARCHAR(32) NOT NULL, data VARCHAR(2000) ); ``` ### 5. Heartbeats Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. For read-write connections, the Connector writes to the heartbeat table directly. ```SQL SQL theme={null} -- Run as a DBA (e.g. the 'informix' user) so the table is created on behalf of streamkap_user. -- Replace {databaseName} placeholder. DATABASE {databaseName}; -- Create the heartbeat table owned by streamkap_user. CREATE TABLE streamkap_user.streamkap_heartbeat ( id INT NOT NULL PRIMARY KEY, text VARCHAR(255), last_update DATETIME YEAR TO FRACTION(3) DEFAULT CURRENT YEAR TO FRACTION(3) ); -- Insert the first row that the Connector will update on each heartbeat tick. INSERT INTO streamkap_user.streamkap_heartbeat (id, text) VALUES (1, 'test_heartbeat'); ``` ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **Informix**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Hostname**: IP address or hostname of the Informix database server. * **Port**: Default is `9088` (the Informix SQLI listener port). * **Connect via SSH Tunnel**: The Connector will connect to an SSH server in your network which has access to your database. This is necessary if the Connector cannot connect directly to your database. * See [SSH Tunnel](/ssh-tunnel) for setup instructions. * **Username**: Username to access the database. By default, Streamkap scripts use `streamkap_user`. * **Password**: Password to access the database. * **Source Database**: The name of the Informix database from which to stream the changes. * **Heartbeats**: * **Heartbeat Table Schema**: Streamkap will use a table in this schema to manage heartbeats. For Informix this is typically the owner of the `streamkap_heartbeat` table (e.g. `streamkap_user`). See [Heartbeats](#5-heartbeats) for setup instructions. ### 3. Snapshot Settings * **Signal Table (Schema.Table)**: The Connector will use this table to manage snapshots. You can specify either just the schema/owner name (e.g., `streamkap_user`) or the full path in `schema.table` format (e.g., `streamkap_user.streamkap_signal`). See [Enable Snapshots](#4-enable-snapshots) for setup instructions. ### 4. Advanced Parameters * **Represent binary data as**: Specifies how the data for binary columns e.g. `byte`, `blob` should be interpreted. Your destination for this data can impact which option you choose. Default is `bytes`. * **CDC Engine Buffer Size (bytes)** (Default `65536`) — Size of the read buffer used by the CDC engine. Increase only if you see CDC-side back-pressure on very high-volume tables. * **CDC Engine Timeout (seconds)** (Default `5`) — How long the CDC engine will wait on a blocking read before breaking out to check for shutdown signals. The default is correct for most deployments. * **Capture Only Captured Databases DDL** (Default `false`) — Whether the Connector records schema structures for all databases on the server or only the one you've configured. Enabling this can improve performance and reduce startup time when you have many databases. See [Schema History Optimization](/schema-history-optimization) for details. * **Capture Only Captured Tables DDL** (Default `false`) — Whether the Connector records schema structures for all tables in the configured database or only the ones it is capturing. Enabling this can improve performance when the database has many tables. See [Schema History Optimization](/schema-history-optimization) for details. Click **Next**. ### 5. Schema and Table Capture * **Add Schemas/Tables**: Specify the schema(s) and table(s) for capture. Enter each entry as `schema.table` (for example `informix.customers`). Streamkap automatically qualifies each entry with the **Source Database** you configured above, so you do not need to repeat the database name in the UI. * You can bulk upload here. The format is a simple list of schemas and tables, with each entry on a new row. Save as a `.csv` file without a header. **CDC only captures base tables, not Views** Change Data Capture reads Informix's logical log, which only records changes to physical base tables. Database Views are query-time computations with no physical storage — they generate no log records. **What you cannot capture:** Views, synonyms, temporary tables, external tables, virtual tables, or system catalog tables (`sys*`). **Solution:** Specify only the underlying base tables that feed your views. You can recreate the view logic in your destination or transformation layer. Click **Save**. ## Troubleshooting There can be a number of reasons. The most common are misconfiguration of CDC and missing privileges. **1. Confirm `syscdcv1` is present and reachable** ```SQL SQL theme={null} DATABASE sysmaster; SELECT name FROM sysdatabases WHERE name = 'syscdcv1'; ``` If `syscdcv1` is missing, re-run `dbaccess sysadmin $INFORMIXDIR/etc/syscdcv1.sql` on the Informix host as the `informix` user. **2. Confirm the source database is logged** ```SQL SQL theme={null} -- Replace {databaseName} placeholder DATABASE sysmaster; SELECT name, is_logging, is_buff_log FROM sysdatabases WHERE name = '{databaseName}'; ``` If `is_logging` is `0`, the database is unlogged and no change events can be captured. Enable logging (see [Enable Change Data Capture](#3-enable-change-data-capture)). **3. Confirm the Streamkap user has access** ```SQL SQL theme={null} -- Replace {databaseName} placeholder DATABASE {databaseName}; SELECT username, usertype FROM sysusers WHERE username = 'streamkap_user'; ``` The user must have `C` (Connect) access to both the source database and `syscdcv1`, plus `SELECT` on every table you want to capture. If you're still stuck after these checks, please reach out to us. Informix refuses to change a database's logging mode until a full archive has been taken. Run a level-0 archive first and then retry the `ondblog` command. ```bash Shell theme={null} # Run on the Informix server host as the 'informix' user ontape -s -L 0 # Once the archive completes, enable buffered logging on the database ondblog buf {databaseName} ``` If you don't need to retain the archive, you can target `/dev/null` (or Windows equivalent) for the tape device in your `$INFORMIXDIR/etc/onconfig` (`TAPEDEV`) before running `ontape -s -L 0`. Informix's CDC API emits a metadata record to the change stream whenever a captured table's structure changes (for example, after an `ALTER TABLE ... ADD COLUMN`). The Connector processes these records inline — it re-reads the current table definition from the Informix catalog, writes the updated structure to the schema-history topic, and subsequent change events for that table use the new schema automatically. No Connector restart is required. If you still don't see new columns after a DDL change: 1. Confirm the DDL ran against the **captured** base table (not a view or synonym — CDC only fires for base tables). 2. Confirm the Connector is **streaming** (not paused or in error). The metadata record is only processed during active streaming. 3. Wait for at least one DML event on the altered table after the DDL. The metadata record is emitted as part of the ongoing CDC stream; if the table is completely idle, downstream consumers may not immediately observe the updated schema. If the Connector is stuck emitting events against an outdated schema despite the above, please reach out to us. # Ingestion Modes Source: https://docs.streamkap.com/insertsupserts Understand how Streamkap writes records to destinations: insert vs upsert behavior, primary keys, and delete handling. For real-time data streaming, the **Insert** method is typically the **fastest** at loading data and the **most cost-effective**. Streamkap Destinations support one or more of the following ingestion modes: ## Insert This method inserts each change as a new record in the destination. For example, if you have an e-commerce record where the order status has changed, you will have two change events: one showing the status *before* and one *after*. This is useful for tracking data changes and optimizing data loading for some destinations. When choosing this method, consider when to clean up older records, as it affects query performance. Users may prefer not to include additional clauses in their queries to filter out older records, so creating views on top of the change data to retrieve only the latest state (i.e., the *after* change event) can simplify usage. ## Upsert Upserts replace a matching record (based on a [primary key](#primary-key-modes)) to avoid retaining older versions in the destination -- a common approach in batch data processing. For example, if you have an e-commerce record where the order status has changed, instead of having two records, you retain only the latest state (i.e., the *after* change event). This is summarised in the image below: However, upserting a record isn't as fast as inserting one. Still, it eliminates the need to modify queries to filter out older records or to create additional views for retrieving the latest state. ## Primary Key Modes Primary key mode controls how the destination connector resolves the primary key columns for each table. The primary key determines how records are matched during upsert operations, deduplication, and delete handling. This setting is available on **JDBC destinations**: PostgreSQL, MySQL, Oracle, SQL Server, CockroachDB, Db2, and Neon. Snowflake and ClickHouse do not use primary key mode. They handle deduplication through their own ingestion mode logic. See [Snowflake](/snowflake) and [ClickHouse](/clickhouse) for details. There are three primary key modes available: ### `record_key` (default) Uses the message key -- typically the source table's primary key -- as the primary key in the destination table. This is the recommended mode for most Change Data Capture (CDC) use cases because the source primary key naturally identifies each record. When a record is updated or deleted in the source, the destination can match it by the same key. **When to use:** * Standard CDC replication where the source table has a primary key * You want the destination table's primary key to match the source table's primary key * You need upsert or delete support ### `record_value` Derives the primary key from one or more fields within the message value (the record payload) rather than the message key. This is useful when the message key does not contain the fields you want to use as the destination primary key, or when you need a composite key based on payload fields. **When to use:** * The message key does not match your desired destination primary key * You need a composite primary key derived from specific payload fields * Custom key logic is required for the destination table When using `record_value`, you should specify the field(s) in the **Custom primary key** setting. Provide a single field name or a comma-separated list of fields. ### `none` No primary key is enforced on the destination table. Every record is inserted as a new row regardless of whether a matching record already exists. **When to use:** * Append-only or log-style tables where you want to retain every change event * Audit trails or event history tables * Scenarios where deduplication is not needed With `none` mode, upsert behavior is disabled. The connector cannot match existing records, so all events result in inserts. Delete operations are also not supported in this mode. ### Custom Primary Key When primary key mode is set to `record_key` or `record_value`, you can optionally specify a **Custom primary key**. This accepts either: * A single column name (e.g., `order_id`) * A comma-separated list of column names for a composite key (e.g., `order_id,line_item_id`) If left empty with `record_key` mode, the connector uses the full message key structure. If left empty with `record_value` mode, the connector uses all fields from the record value as the key, which is rarely the desired behavior -- so specifying fields explicitly is strongly recommended. ### Applicable Destinations Primary key mode is available on the following JDBC destinations: | Destination | Supports PK Mode | Default Mode | | ------------------------------------- | ---------------- | ------------ | | [PostgreSQL](/stream-into-postgresql) | Yes | `record_key` | | [MySQL](/stream-into-mysql) | Yes | `record_key` | | [Oracle](/stream-into-oracle) | Yes | `record_key` | | [SQL Server](/stream-into-sqlserver) | Yes | `record_key` | | [CockroachDB](/cockroachdb) | Yes | `record_key` | | [Db2](/stream-into-db2) | Yes | `record_key` | | [Neon](/neon-destination) | Yes | `record_key` | ## Delete Handling When a row is deleted in a source database, Streamkap's CDC pipeline captures that event and propagates it to the destination. How that delete is applied -- whether the row is physically removed, marked as deleted, or simply appended as a new event -- depends on the **ingestion mode** and the **delete configuration** of your destination connector. ### How Deletes Flow A delete event follows these steps from source to destination: 1. **Source database**: A row is deleted (e.g., `DELETE FROM orders WHERE id = 42`). The database records this operation in its transaction log (WAL, binlog, redo log, etc.). 2. **CDC capture**: Streamkap reads the delete event from the transaction log and produces a message to the internal Kafka topic. This message includes the primary key of the deleted row and a delete marker (sometimes called a *tombstone*). 3. **Kafka message**: The delete event is stored as a Kafka message with the row's primary key and metadata indicating a delete operation. 4. **Destination connector**: The destination connector consumes the message and applies the delete according to the configured **ingestion mode** and **delete mode** settings. Delete events require a **primary key** to identify which row was deleted. Tables without a primary key may not propagate deletes correctly. Ensure your source tables have a primary key defined. ### Delete Behavior Matrix The outcome of a delete event at the destination depends on two factors: the **ingestion mode** (insert or upsert) and whether **delete mode is enabled**. | Ingestion Mode | Delete Enabled | What Happens at the Destination | | ------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------- | | **Upsert** | Yes | The row is **physically removed** from the destination table. | | **Upsert** | No | Depends on destination type (see note below). | | **Insert / Append** | N/A | A new row is **appended** with the `__DELETED` column set to `'true'`. The original row is not modified or removed. | The behavior when delete mode is disabled differs by destination type: * **JDBC destinations** (Delete mode off): Delete events are ignored entirely. The row remains at the destination unchanged. * **Snowflake and ClickHouse** (Delete Mode off): The row remains but the `__DELETED` column is set to `'true'` (soft delete). **Which mode should I use?** * Use **upsert with delete enabled** when you want the destination to mirror the source exactly -- deleted rows are removed. * Use **upsert with delete disabled** when you want to retain deleted rows for auditing or compliance, but still deduplicate on primary key. * Use **insert/append** when you need a full change history, including deletes, as an append-only event log. ### Configuring Delete Mode The setting name and behavior vary depending on your destination connector type. #### JDBC Destinations Applies to: **PostgreSQL**, **MySQL**, **Oracle**, **SQL Server**, **CockroachDB**, **Db2**, **Neon** | Setting | **Delete mode** | | -------------------------- | ------------------------------------------------------------------------------------------ | | **Type** | Boolean | | **Default** | Off (deletes are not processed) | | **Where to configure** | Destination connector settings | | **Behavior when enabled** | DELETE or tombstone events cause the corresponding row to be removed from the destination. | | **Behavior when disabled** | Delete events are ignored. The row remains at the destination unchanged. | **Redshift** also supports Delete mode. Check your Redshift destination settings for the current configuration. For JDBC destinations, delete mode works with both **insert** and **upsert** ingestion modes. However, physical row removal only occurs in **upsert** mode. In **insert** mode, delete events are appended as new rows with the `__DELETED` metadata column set to `'true'`. #### Snowflake and ClickHouse | Setting | **Delete Mode** | | -------------------------- | ------------------------------------------------------------------------------------------------ | | **Type** | Boolean | | **Default** | On (deletes are processed) | | **Applies to** | **Upsert mode only** | | **Where to configure** | Destination connector settings | | **Behavior when enabled** | DELETE or tombstone events cause the corresponding row to be removed from the destination table. | | **Behavior when disabled** | The row remains at the destination. The `__DELETED` column is set to `'true'`. | The Delete Mode setting only applies when the Snowflake or ClickHouse destination is configured in **upsert** mode. In **append** mode, delete events are always appended as new rows with the `__DELETED` column, regardless of this setting. #### Other Destinations For other destinations (BigQuery, Databricks, S3, MotherDuck), check your specific destination documentation for delete mode availability. ### The `__DELETED` Column Streamkap adds a `__DELETED` metadata column to destination tables. This column indicates whether the corresponding row has been deleted in the source database. | Value | Meaning | | --------- | ------------------------------------------------------------- | | `'false'` | The row exists (has not been deleted) in the source database. | | `'true'` | The row has been deleted in the source database. | **When `__DELETED` appears:** * **Insert/append mode**: Every row includes the `__DELETED` column. Delete events are appended as new rows with `__DELETED = 'true'`. * **Upsert mode with delete disabled (Snowflake/ClickHouse)**: The existing row is updated with `__DELETED = 'true'` instead of being removed. * **Upsert mode with delete disabled (JDBC)**: Delete events are ignored. The row remains unchanged. * **Upsert mode with delete enabled**: Deleted rows are physically removed, so you will not typically see `__DELETED = 'true'` rows in the destination (they are removed upon processing). **Using `__DELETED` for soft deletes:** If you are using **insert/append** mode or **upsert mode with delete disabled**, you can filter out deleted rows in your queries: ```sql theme={null} -- Get only active (non-deleted) rows SELECT * FROM my_table WHERE __DELETED = 'false'; ``` This pattern is commonly used when building **final-state tables** or **materialized views** from append-only data. See [Creating Final State Tables](/creating-final-state-tables-from-insertsappend) for detailed examples with Snowflake Dynamic Tables, Tasks, and dbt models. For a full list of metadata columns Streamkap adds, see [Metadata](/metadata). ### Truncate Events Truncate event handling may vary by destination. Contact support for details on truncate behavior for your specific destination type. ## How Modes Interact The primary key mode, ingestion mode, and delete mode work together to determine how records are delivered to the destination. The table below shows how these settings interact. When **Delete mode** is enabled on JDBC destinations, the primary key mode is automatically set to `record_key`. This is because delete operations require a stable, well-defined primary key to identify which row to remove. If you do not see the primary key mode option in the destination settings, check whether delete mode is enabled. Disabling delete mode will reveal the primary key mode selector. | Primary Key Mode | Ingestion Mode | Delete Enabled | Behavior | | ---------------- | -------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `record_key` | Upsert | Yes | Rows are upserted by source PK. Deletes physically remove the matching row. | | `record_key` | Upsert | No | Rows are upserted by source PK. Delete behavior depends on destination type: JDBC ignores delete events; Snowflake/ClickHouse sets `__DELETED = 'true'`. | | `record_key` | Insert | Yes / No | Each change is appended as a new row. Delete events are appended with `__DELETED = 'true'`. | | `record_value` | Upsert | N/A | Rows are upserted by the specified payload fields. Delete events may not propagate correctly because tombstone records have a `null` value, so the connector cannot extract a primary key. | | `record_value` | Insert | N/A | Each change is appended as a new row. Delete events are appended with `__DELETED = 'true'`. | | `none` | Insert | N/A | All records are appended (insert-only). No deduplication or delete support. Delete events are appended with `__DELETED = 'true'`. | If you need delete events to be applied at the destination (physical row removal or soft-delete updates), use `record_key` primary key mode with **upsert** ingestion mode. Other primary key modes either cannot resolve the target row for a delete or treat all records as append-only. **Decision matrix for choosing primary key mode:** | Use Case | Recommended Mode | Delete Support | Custom PK | | ------------------------------------- | ----------------------- | -------------------------- | --------- | | Standard CDC replication | `record_key` | Yes | Optional | | Destination PK differs from source PK | `record_value` | No (requires `record_key`) | Required | | Composite key from payload fields | `record_value` | No (requires `record_key`) | Required | | Append-only / event log table | `none` | No | N/A | | Delete handling required | `record_key` (auto-set) | Yes | Optional | ## Delivery Guarantees Streamkap provides **at-least-once** delivery: * Every change is delivered at least once * In failure scenarios, some events may be delivered more than once * **Upsert mode** handles duplicates automatically via primary key deduplication * **Insert/append mode** may result in duplicate rows -- use [metadata columns](/metadata) (`_STREAMKAP_TS_MS` and `_STREAMKAP_OFFSET`) to deduplicate at query time See [Creating Final State Tables](/creating-final-state-tables-from-insertsappend) for deduplication patterns using Snowflake Dynamic Tables, Tasks, and dbt models. ## Troubleshooting If you see duplicate rows in your destination table for the same logical record: * **Check primary key mode.** If the mode is `none`, duplicates are expected because every event is inserted as a new row. Switch to `record_key` or `record_value` if you need upsert (deduplication) behavior. * **Check the Custom primary key setting.** If you are using `record_value` mode without specifying the primary key fields, the connector may use all payload fields as the key, which can produce unexpected matching behavior. Specify the correct field(s) explicitly. * **Check for schema changes.** If the source table's primary key changed, existing destination rows keyed on the old primary key will not match new records keyed on the updated primary key. A [snapshot](/snapshots#snapshotting-after-schema-changes) may be needed. * **Check destination table DDL.** Ensure the destination table has a primary key or unique constraint defined on the expected columns. Some destinations require this for upsert behavior to work. If records are being inserted as new rows instead of updating existing rows: * **Verify ingestion mode.** Upsert behavior requires the ingestion mode to be set to **upsert** on the destination connector. If the mode is set to **insert**, all records are appended regardless of primary key mode. * **Verify primary key mode.** Upsert requires either `record_key` or `record_value` mode. The `none` mode does not support upserts. * **Verify the destination supports upserts.** Not all destination types support upsert operations. Check your destination's documentation for details. * **Check Custom primary key alignment.** The fields specified in the Custom primary key setting must match columns that exist in the destination table and are defined as the primary key or unique key. If delete events from the source are not removing rows at the destination: * **Check delete mode.** Delete handling requires **Delete mode** to be enabled on the destination connector. When delete mode is enabled on JDBC destinations, the primary key mode is automatically set to `record_key`. * **Check primary key mode.** Only `record_key` mode supports delete propagation. If you are using `record_value` or `none`, delete events cannot be matched to destination rows. * **Check source configuration.** For MySQL/MariaDB sources, ensure `binlog_row_image` is set to `FULL` so that delete events contain the full row data needed for matching. * **Check ingestion mode.** Delete handling (physical row removal) requires **upsert** ingestion mode. Insert-only mode does not process deletes -- it appends them as new rows with `__DELETED = 'true'`. There are several common reasons why a deleted row may still appear at the destination: 1. **Delete mode is disabled.** Check your destination connector settings and verify that **Delete mode** is enabled. 2. **You are using insert/append mode.** In insert mode, delete events do not remove existing rows. Instead, a new row is appended with `__DELETED = 'true'`. The original row remains. This is expected behavior -- filter on `WHERE __DELETED = 'false'` to see only active rows. 3. **The source table has no primary key.** Delete events require a primary key to identify which row to remove. If your source table lacks a primary key, the delete event may not be applied correctly at the destination. 4. **There is pipeline lag.** The delete event may not have been processed yet. Check the pipeline status and consumer lag in the [Streamkap UI](https://app.streamkap.com). Use one of these approaches: * **Insert/append mode**: All changes, including deletes, are appended as new rows. Query with `WHERE __DELETED = 'false'` to see only active records. * **Upsert mode with delete disabled**: The row stays at the destination but its `__DELETED` column is updated to `'true'`. You retain the latest state of every row, including those that have been deleted. A tombstone is a Kafka message with a valid key but a `null` value. In CDC, tombstones represent delete events -- they indicate that the row identified by the key has been removed from the source database. Streamkap processes tombstones according to your destination's delete mode configuration. MySQL (including all 8.x versions) does not log `ON DELETE CASCADE` events to the binlog. InnoDB handles foreign key cascades internally. This means child table DELETE events from cascading foreign keys are NOT captured by CDC -- only the parent table delete is propagated. This is a MySQL limitation, not a Streamkap limitation. If you rely on cascading deletes in MySQL, verify that child table deletions are reflected at the destination by checking the child tables directly. Consider using application-level deletes instead of database-level cascades if full CDC coverage is required. Yes. Delete events include the full primary key (all key columns) from the source table. The destination connector uses all key columns to identify and remove the correct row. Streamkap processes events in order. If a row is deleted and then a new row with the same primary key is inserted: * **Upsert mode with delete enabled**: The row is first removed, then re-inserted with the new values. * **Insert/append mode**: Both events are appended -- a delete event (`__DELETED = 'true'`) followed by an insert event (`__DELETED = 'false'`). ## Related Documentation * [Metadata](/metadata) -- Metadata columns added by Streamkap for deduplication and tracking * [Creating Final State Tables](/creating-final-state-tables-from-insertsappend) -- How to build deduplicated views from append-only data * [Snapshots](/snapshots) -- How initial and incremental snapshots work # Kafka Access Source: https://docs.streamkap.com/kafka-access Manage Kafka users, ACLs, and access control Kafka Access allows you to create and manage Kafka users with fine-grained access control through ACLs (Access Control Lists), safe listed IPs, and secure proxy endpoints for your Streamkap projects. ## Overview The Kafka Access page provides a centralized interface to: * Create and manage Kafka users with authentication credentials * Configure IP safe listing for enhanced security * Define granular ACLs for topic and consumer group access * View proxy endpoints for connecting to your Kafka cluster * Search and filter users by username or IP address ## Table Columns The Kafka Access table displays the following information: * **Username**: The Kafka username (lowercase only) * **Safe listed IPs**: CIDR-notated IP addresses allowed to connect * **Proxy Endpoint**: The Kafka proxy connection endpoints (host:port) * **ACLs**: Summary of configured Access Control Lists showing resource name, operation, pattern type, and resource type ## Quick Actions Each user row includes action buttons on the right: * **Edit** (pencil icon): Modify user settings, password, IPs, and ACLs * **Delete** (trash icon): Remove the Kafka user ## Creating a Kafka User To create a new Kafka user: Click the **Create User** button at the top of the page. Provide a unique username (lowercase characters only). Usernames must be lowercase and unique within your project. Enter a secure password for the user. This will be used for SASL authentication. Add IP addresses or CIDR ranges that are allowed to connect with this user. * Enter IP/CIDR notation (e.g., `192.168.1.0/24`) * Press Enter or comma to add multiple IPs * Leave empty to allow connections from any IP Using IP safe listing adds an extra layer of security by restricting where connections can originate. Define Access Control Lists to control what this user can access. Click **+ Add ACL** to add a new rule. For each ACL, specify: * **Name**: The resource name (topic name, consumer group name, or pattern) * **Resource**: The type of resource * `TOPIC`: Kafka topics * `GROUP`: Consumer groups * `CLUSTER`: Cluster-level operations * `TRANSACTIONAL_ID`: Transactional IDs * `DELEGATION_TOKEN`: Delegation tokens * **Operation**: The allowed operation * `READ`: Consume from topics or read group offsets * `WRITE`: Write data to topics * `CREATE`: Create topics or other resources * `DELETE`: Delete resources * `ALTER`: Modify resource configurations * `DESCRIBE`: View resource metadata (required by many clients alongside READ/WRITE) * `CLUSTER_ACTION`: Perform cluster-level actions * `DESCRIBE_CONFIGS`: View configurations * `ALTER_CONFIGS`: Modify configurations * `IDEMPOTENT_WRITE`: Idempotent writes * `ALL`: All operations * **Pattern Type**: How the resource name is matched * `LITERAL`: Exact match (e.g., specific topic name) * `PREFIXED`: Matches resources starting with this prefix You can add multiple ACL rules for the same user to grant access to different resources. **Import .CSV**: You can bulk import ACL rules from a CSV file by clicking the "Import .CSV" button. Click **Save** to create the user. The user will appear in the table with their proxy endpoints. ## Editing a Kafka User To modify an existing Kafka user: 1. Click the **Edit** button (pencil icon) for the user you want to modify 2. The Edit Kafka User dialog will appear showing current settings ### Editable Fields * **Username**: Display only (cannot be changed after creation) * **Password (optional)**: Enter a new password to change it, or leave empty to keep the current password * **Safe listed IPs**: Add or remove IP addresses/CIDR ranges * **Kafka ACLs**: Add, modify, or remove ACL rules Removing or modifying ACLs will immediately affect the user's access permissions. Ensure you understand the impact before making changes. ## Deleting a Kafka User To remove a Kafka user: 1. Click the **Delete** button (trash icon) for the user 2. Confirm the deletion when prompted Deleting a user is permanent and will immediately revoke all access. Any applications using these credentials will lose connectivity. ## Searching and Filtering Use the search bar to quickly find users: * Search by **username** * Search by **IP address** (full or partial match) The search filters the table in real-time as you type. ## Connecting to Kafka Once a user is created, you can connect to Kafka using the proxy endpoints displayed in the table. ### Connection Details * **Bootstrap Servers**: Use the proxy endpoints from the "Proxy Endpoint" column * **Security Protocol**: `SASL_SSL` * **SASL Mechanism**: `PLAIN` * **Username**: The Kafka username you created * **Password**: The password you set for the user ### Example Connection Configuration ```properties theme={null} bootstrap.servers=tenant-demo-testare.streamkap.net:32400,tenant-demo-testare.streamkap.net:32401,tenant-demo-testare.streamkap.net:32402 security.protocol=SASL_SSL sasl.mechanism=PLAIN ``` ## Project Selection The Kafka Access page is project-specific. You can switch between projects using the project selector in the navigation bar. Each project has its own set of Kafka users and proxy endpoints. ## ACL Best Practices When configuring ACLs, follow these best practices: 1. **Principle of Least Privilege**: Grant only the minimum permissions required for each user 2. **Use Literal Matches**: When possible, use `LITERAL` pattern type for specific topic names to avoid over-permissioning 3. **Separate Users by Function**: Create different users for producers, consumers, and administrative tasks 4. **Combine READ and DESCRIBE**: Consumers typically need both `READ` (for data) and `DESCRIBE` (for metadata) 5. **Consumer Groups**: Don't forget to grant `READ` access to consumer groups (resource type `GROUP`) for consumer applications 6. **Testing**: Test ACL configurations in a development environment before applying to production ### Common ACL Patterns #### Read-Only Consumer * Resource: `TOPIC` with topic name * Operation: `READ` * Pattern Type: `LITERAL` Plus: * Resource: `GROUP` with consumer group name * Operation: `READ` * Pattern Type: `LITERAL` or `PREFIXED` #### Producer * Resource: `TOPIC` with topic name * Operation: `WRITE` * Pattern Type: `LITERAL` #### Admin User * Resource: `CLUSTER` * Operation: `ALL` * Pattern Type: `LITERAL` ## Security Considerations * **Rotate Passwords Regularly**: Change user passwords periodically * **Use IP Safe listing**: Restrict access to known IP ranges when possible * **Monitor Access**: Check audit logs regularly for unauthorized access attempts * **Remove Unused Users**: Delete users that are no longer needed * **Strong Passwords**: Use complex passwords with a mix of characters ## Troubleshooting ### Connection Refused If you cannot connect to Kafka: 1. Verify the proxy endpoints are correct 2. Check that your IP address is safe listed (if configured) 3. Confirm the username and password are correct 4. Ensure your firewall allows outbound connections to the proxy ports ### Permission Denied If you receive permission denied errors: 1. Review the user's ACLs to ensure they have the required permissions 2. Check that the ACL resource name matches your topic/group name exactly (for `LITERAL` matches) 3. Verify the operation type includes what you're trying to do (e.g., `READ` for consuming) ### User Not Found If a user appears in the list but cannot authenticate: 1. Verify you're connecting to the correct project endpoints 2. Check that the password was set correctly 3. Try editing the user and resetting the password ## Related Documentation * [Projects](/projects) - Managing Streamkap projects * [Topics](/topics) - Working with Kafka topics * [Organisation Security](/organisation-security) - Overall security settings # Kafka Direct Source: https://docs.streamkap.com/kafka-direct Consume messages from an external Kafka cluster and stream them through Streamkap pipelines. Kafka Direct lets you consume messages from an external Kafka cluster and stream them through Streamkap pipelines to your destinations. Unlike CDC-based sources that capture changes from a database transaction log, Kafka Direct simply reads messages from the specified Kafka topics and forwards them downstream. Kafka Direct is for **pulling data into** Streamkap from an external Kafka cluster. If you need to **push data to** Streamkap's internal Kafka, see [Kafka (Writing)](/write-to-kafka). If you need to **replicate topics from** Streamkap to another Kafka cluster, see [Kafka (Push)](/kafka-to-kafka). ## Prerequisites * An external Kafka cluster accessible from Streamkap * Kafka bootstrap server URLs (external listener addresses, not internal) e.g. `broker-1.mycompany.com:9094,broker-2.mycompany.com:9094` * The names of the Kafka topics you want to consume * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your Kafka cluster ## Supported Platforms Kafka Direct works with any Kafka-compatible cluster, including: | Platform | Notes | | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------- | | **Apache Kafka** | Open-source Apache Kafka clusters (self-hosted or managed) | | **Confluent Cloud / Confluent Platform** | Fully compatible; use SASL\_SSL authentication with API key and secret | | **Amazon MSK** | Amazon Managed Streaming for Apache Kafka; supports IAM and SASL\_SSL authentication | | **Azure Event Hubs (Kafka protocol)** | Azure Event Hubs exposes a Kafka-compatible endpoint; use the Event Hubs connection string as the SASL password | | **Redpanda** | Kafka API-compatible streaming platform; works with standard Kafka authentication methods | ## Authentication Kafka Direct supports several authentication mechanisms to connect securely to your external Kafka cluster. Configure authentication in the connector settings when creating or editing your Kafka Direct source. ### SASL\_PLAIN Username and password authentication transmitted in plaintext. Suitable when the connection is already secured by a private network or VPN. * **Security protocol:** `SASL_PLAINTEXT` * **SASL mechanism:** `PLAIN` * Provide the **username** and **password** in the connector settings SASL\_PLAIN transmits credentials in cleartext. Only use this mechanism over trusted, private networks. For production environments, prefer SASL\_SSL. ### SASL\_SSL Username and password authentication over a TLS-encrypted connection. This is the recommended authentication method for most managed Kafka services (Confluent Cloud, Amazon MSK, etc.). * **Security protocol:** `SASL_SSL` * **SASL mechanism:** `PLAIN`, `SCRAM-SHA-256`, or `SCRAM-SHA-512` depending on your cluster configuration * Provide the **username** and **password** in the connector settings ### SSL / mTLS (Mutual TLS) Client certificate authentication where both the client and broker verify each other's identity using TLS certificates. Use this when your Kafka cluster requires certificate-based authentication. * **Security protocol:** `SSL` * Provide the **client certificate**, **client key**, and **CA certificate** in the connector settings For detailed information on network connectivity options (SSH tunnels, VPN, AWS PrivateLink, IP allowlisting), see [Connection Options](/connection-options). ## Key Differences from CDC Sources Kafka Direct behaves differently from CDC-based sources (such as PostgreSQL, MySQL, or MongoDB): * **No transaction log** -- Kafka Direct reads messages directly from Kafka topics rather than tailing a database transaction log. * **No snapshots** -- There is no initial snapshot or backfill mechanism. Consumption starts based on the configured offset policy. * **No heartbeats** -- Heartbeat monitoring is not applicable to Kafka Direct sources. * **Topic naming** -- Kafka Direct uses the user-defined `topic.prefix` value (not the `source_{id}` prefix used by CDC sources). *** ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **Kafka Direct**. ### 2. Connection Settings * **Name**: Enter a unique and memorable name for your connector. * **Kafka Bootstrap Servers**: A comma-separated list of `host:port` pairs for the external Kafka brokers, such as `broker1.kafka.company.com:9092,broker2.kafka.company.com:9092`. * **Topic Prefix**: A prefix applied to topic names inside Streamkap. This helps organise and identify topics originating from this source within your Streamkap pipelines. ### 3. Topic Selection * **Topics to include**: A comma-separated list of the external Kafka topic names you want to consume, such as `orders,events,user-activity`. ### 4. Format Settings * **Format**: The message format of the external Kafka topics. * **string** (default) -- Messages are treated as plain strings. * **json** -- Messages are parsed as JSON. * **Schemas Enable** (only when Format is set to **json**): Toggle this to enable schema support. When enabled, Streamkap uses its internal Schema Registry to validate and enforce message structure. Enabling schema support is recommended when your JSON messages have a consistent structure. Streamkap's internal Schema Registry handles schema management automatically — you do not need to provide an external Schema Registry URL. Click **Save**. ## Consumer Group Configuration Streamkap manages consumer groups automatically for Kafka Direct sources. Each Kafka Direct connector uses a dedicated consumer group to track its position (offset) in each topic partition. The consumer group ID is managed by Streamkap and is tied to the connector instance. If you delete and recreate a Kafka Direct source, a new consumer group is created and consumption restarts according to the configured offset policy. If you need to reset offsets for an existing connector, contact Streamkap support. Key considerations: * **Offset policy**: When the connector starts for the first time (or when the consumer group has no committed offsets), it uses the configured offset policy to determine where to begin reading. Typical options are `earliest` (read from the beginning of the topic) or `latest` (read only new messages). * **Parallel consumption**: Streamkap assigns consumer group members to topic partitions automatically. The level of parallelism is determined by the number of partitions in the source topic. * **Rebalancing**: If partitions are added to a source topic, the consumer group rebalances automatically to include the new partitions. ## Adding Topics Programmatically You can add topics to an existing Kafka Direct source via the API or Terraform using the `topic.include.list.user.defined` parameter (Terraform: `topic_include_list_user_defined`). This lets you append topics to the source without modifying the original topic list configured through the UI. See [Terraform Resources](/terraform-resources) for a full Kafka Direct Terraform example. ## Troubleshooting **Symptoms:** The connector fails to start or reports connection errors referencing broker addresses. **Possible Causes:** * Firewall rules are blocking traffic from Streamkap to the Kafka brokers * The broker address or port is incorrect (e.g., using an internal DNS name instead of an external listener) * The Kafka broker port is not open or the broker is not running **Resolution:** 1. Verify that the bootstrap server addresses use externally reachable hostnames and the correct listener port 2. Ensure your firewall or security group allows inbound connections from Streamkap's IP addresses (see [Connection Options](/connection-options)) 3. Test connectivity to the broker address and port from a machine on the same network as Streamkap **Symptoms:** The connector fails with SASL authentication errors or SSL handshake failures. **Possible Causes:** * Incorrect username or password * Wrong SASL mechanism selected (e.g., using `PLAIN` when the broker expects `SCRAM-SHA-256`) * Expired or invalid client certificates (for mTLS) * The security protocol does not match the broker's listener configuration **Resolution:** 1. Verify that the username, password, and SASL mechanism match the broker configuration 2. For mTLS, ensure the client certificate and key are valid and not expired 3. Confirm the security protocol (`SASL_PLAINTEXT`, `SASL_SSL`, or `SSL`) matches the Kafka listener you are connecting to **Symptoms:** The connector is running but no data appears in the pipeline or destination. **Possible Causes:** * The topic name is incorrect or misspelled * The topic has no new messages and the offset policy is set to `latest` * The consumer group has already consumed all available messages * The topic has no partitions with data **Resolution:** 1. Verify the topic names in the connector configuration match the exact topic names on the Kafka cluster 2. Check the offset policy -- if set to `latest`, only new messages produced after the connector started will be consumed 3. Confirm that the source Kafka cluster is actively producing messages to the configured topics 4. Inspect the topic partitions on the source cluster to verify they contain data **Symptoms:** Messages are routed to the DLQ or the connector reports deserialization errors. **Possible Causes:** * The selected format (string or JSON) does not match the actual message format on the topic * Schema support is enabled but messages do not conform to a consistent JSON structure * Messages contain unsupported or malformed data **Resolution:** 1. Verify that the **Format** setting matches the actual format of messages on the source topic 2. If using JSON with schema support enabled, ensure all messages on the topic share a consistent structure 3. Inspect a sample of messages on the source topic to confirm the format before configuring the connector 4. Check the [DLQ](/dlq-operations) for detailed error information on failed messages # Kafka (Push) Source: https://docs.streamkap.com/kafka-to-kafka Replicate topics from Streamkap to another Kafka cluster The Kafka (Push) Destination allows replication of topics from Streamkap to another Kafka cluster. **Apache MirrorMaker 2** This Streamkap Destination should not be confused with Apache MirrorMaker 2. Although both replicate topics between Kafka clusters, there are fundamental differences. For example, Apache MirrorMaker 2 supports offset translation, allowing Consumers of the origin cluster to be switched over to the replica and resume without losing their position. It also supports metadata propagation i.e. topic configuration, consumer groups, ACLs. Streamkap's Kafka Destination simply reads the messages from an origin topic and writes them to a destination topic. There is no offset translation or metadata propagation. # Prerequisites * Provide Streamkap's services access to the Kafka cluster using one of the available [Connection Options](/connection-options) * Kafka bootstrap server URLs (external not internal listener addresses) e.g. `broker-1.mycompany.com:9094,broker-2.mycompany.com:9094, ...` * The topic data format. For **Avro**, you will need the URL for a Schema Registry service running and accessible to Streamkap e.g. `https://schema-registry.mycompany.com:8081` # Streamkap Setup 1. Go to [Destinations](https://app.streamkap.com/connectors/add?tab=Destinations) and choose **Kafka (Push)** 2. Input the following information: 3. **Name** - A unique and memorable name for this Connector 4. **Kafka Bootstrap Servers** - A comma-separated list of `host:port` pairs for the destination Kafka brokers, such as `broker1.kafka.company.com:9092,broker2.kafka.company.com:9092` 5. **Format (default: JSON)** - The topic data format 1. **Avro:** Messages will be serialised in Avro format using schemas from the Schema Registry 1. **Schema Registry URL** - The URL for the destination Kafka Schema Registry e.g. `http://schema-registry.company.com:8081` 2. **JSON:** Messages will be serialised in JSON format 1. **Include JSON Schema** - Choose to include or exclude the schema in the JSON formatted messages 6. **Topic Prefix** - Specify a prefix to add to each destination topic name. This can help with organising topics in the Kafka cluster 7. **Topic Suffix (Optional)** - Specify a suffix to add to each destination topic name 8. Click **Save** # Login Source: https://docs.streamkap.com/login Sign in to your Streamkap account using various authentication methods Login to the app is via [app.streamkap.com](https://app.streamkap.com) You should use the same method to sign-in which you used to [Sign Up](/sign-up) ## Sign-in Methods Sign in with any of the following methods: * **Business Email** - Enter your email to receive a magic link * **Google** - Sign in with your Google account * **GitHub** - Sign in with your GitHub account * **Microsoft** - Sign in with your Microsoft account * **Slack** - Sign in with your Slack workspace Streamkap Login Page # Logs Source: https://docs.streamkap.com/logs View and search system logs for troubleshooting pipelines and connectors The Logs page provides comprehensive access to system logs for monitoring and debugging your Streamkap pipelines, sources, and destinations. View INFO, WARN, and ERROR messages with powerful filtering capabilities to quickly identify and resolve issues. ## Overview The Logs page displays a real-time stream of log messages from all connectors and system components. Use the search and filtering tools to narrow down logs to specific connectors, log levels, or time ranges. ## Features ### Search by Message Use the search bar at the top to filter logs by message content: * **Real-time search**: Filter logs as you type * **Full-text search**: Searches across the entire log message including stack traces * **Case-insensitive**: Search is not case-sensitive ### Filter by Log Level Click the **Log levels** dropdown to filter by severity: * **ALL**: Show all log levels (default) * **INFO**: Informational messages about normal operations * **WARN**: Warning messages that may require attention * **ERROR**: Error messages indicating failures or issues Multiple log levels can be selected simultaneously to view combinations (e.g., WARN + ERROR). ### Filter by Connector Click the **Filter by Connector** dropdown to show logs from specific connectors: * **All Connectors**: Show logs from all sources and destinations (default) * **Specific Connector**: Select a source or destination connector by name * **Connector Type Indicator**: Icon shows whether it's a source or destination When viewing a connector's detail page, click the logs link to automatically filter logs for that connector. ### Time Range Selection Use the time range dropdown to control which logs are displayed: * **Last 15 minutes** (default) * **Last hour** * **Last 24 hours** * **Last 7 days** * **Custom range**: Select specific start and end times ## Log Table The logs table displays the following columns: * **Timestamp**: When the log event occurred (ISO 8601 format with millisecond precision) * **Connector**: The source or destination connector that generated the log * Connector badge shows name and type (src for source, dest for destination) * Click to navigate to the connector detail page * **Type**: The component type (source, destination, pipeline, service) * **Level**: Log severity level (INFO, WARN, ERROR) * Color-coded: green for INFO, yellow for WARN, red for ERROR * **Message**: The log message content * Click the expand icon to view full message with stack traces ### Message Details Click the expand icon (↗) on any log row to view the complete message including: * Full log text * Stack traces (for errors) * Thread information * Task identifiers * Additional context ## Pagination Navigate through logs using the pagination controls at the bottom: * **Results per page**: Select 10, 20, 50, or 100 logs per page * **Page navigation**: Jump between pages or use Previous/Next buttons * **Result count**: Shows "Showing logs 1 to 50" to track your position ## Access Methods ### Via Sidebar Click **Logs** in the sidebar to view all logs across your service. ### Via Connector Detail Page Navigate to any source or destination connector detail page and click the logs link to view pre-filtered logs for that specific connector only. ### Via Pipeline Detail Page Some pipeline pages provide quick access to logs for debugging data flow issues. ## Common Use Cases ### Debugging Failed Pipelines 1. Navigate to the Logs page 2. Set log level filter to **ERROR** 3. Filter by the affected **connector name** 4. Set time range to when the issue occurred 5. Expand error messages to view stack traces ### Monitoring Connector Health 1. Filter logs to a specific connector 2. Set log level to **WARN** and **ERROR** 3. Set time range to **Last 24 hours** 4. Look for recurring warnings or error patterns ### Investigating Performance Issues 1. Set log level to **INFO** 2. Filter by the slow connector 3. Search for keywords like "lag", "latency", or "backlog" 4. Review timestamps to identify bottlenecks ### Tracking Configuration Changes 1. Search for "config" or "settings" 2. Set log level to **INFO** 3. Filter by time range when changes were made 4. Review connector-specific logs ## Best Practices 1. **Use Specific Filters**: Narrow down logs with connector and level filters before searching to improve performance 2. **Monitor ERROR Logs**: Set up alerts for ERROR-level logs to catch issues early (see [Alerts](/alerts)) 3. **Review WARN Logs**: Warnings often indicate problems before they become critical errors 4. **Capture Context**: When reporting issues, include timestamp, connector, level, and full message 5. **Use Time Ranges Wisely**: Shorter time ranges load faster and are easier to navigate 6. **Search Strategically**: Use specific keywords rather than generic terms for better results ## Troubleshooting ### Logs Not Appearing If logs are not displaying: 1. **Check Time Range**: Expand the time range to include when events occurred 2. **Clear Filters**: Remove connector and log level filters to see all logs 3. **Verify Connector**: Ensure the connector is running and generating logs 4. **Refresh Page**: Reload the page to fetch latest logs ### Performance Slow If the Logs page loads slowly: 1. **Reduce Time Range**: Use shorter time windows (e.g., Last 15 minutes) 2. **Apply Filters**: Filter by connector and log level before searching 3. **Limit Page Size**: Use fewer results per page (10 or 20 instead of 100) 4. **Avoid Broad Searches**: Use specific search terms instead of single characters ### Missing Stack Traces If error messages don't show stack traces: 1. **Expand Message**: Click the expand icon (↗) to view full details 2. **Check Log Level**: Ensure you're viewing ERROR-level logs 3. **Verify Configuration**: Some connectors may have verbose logging disabled 4. **Contact Support**: If stack traces are consistently missing, contact Streamkap support ## Related Documentation * [Alerts](/alerts) - Set up notifications for log events * [Pipelines](/pipelines) - Monitor and configure data pipelines * [Sources](/sources) - Troubleshoot source connectors * [Destinations](/destinations) - Troubleshoot destination connectors * [Topics](/topics) - View message-level details for Kafka topics # Overview Source: https://docs.streamkap.com/mariadb Streamkap supports syncing MariaDB in real-time using change data capture for: If you don't see your platform listed, please contact us as we're happy to help find and provide the setup documentation you need. # MariaDB (Generic) Source: https://docs.streamkap.com/mariadb-generic Connect a self-hosted or generic MariaDB server to Streamkap for change data capture, including binary log configuration, user grants, and heartbeats. # Prerequisites * MariaDB version ≥ 11.4.3 * MariaDB [binlog](https://mariadb.com/kb/en/activating-the-binary-log/) enabled on the primary server * Connection details * Streamkap user and role The following table summarizes all permissions the Streamkap user needs. The setup steps on each source connector page walk through granting each one. **Core permissions (always required):** | Permission | Scope | Purpose | | -------------------- | ------------ | -------------------------------------------------------- | | `REPLICATION CLIENT` | `*.*` | Read binlog metadata and positions | | `REPLICATION SLAVE` | `*.*` | Read binlog events for CDC streaming | | `RELOAD` | `*.*` | Flush operations required for consistent snapshots | | `SHOW DATABASES` | `*.*` | Discover available databases and schemas | | `SELECT` | `{schema}.*` | Read table data during initial and incremental snapshots | **Snapshot signal table (required if GTID is disabled):** | Permission | Scope | Purpose | | ---------- | ---------------------------- | -------------------------- | | `SELECT` | `streamkap.streamkap_signal` | Read signal table state | | `INSERT` | `streamkap.streamkap_signal` | Trigger snapshot signals | | `UPDATE` | `streamkap.streamkap_signal` | Update signal table state | | `DELETE` | `streamkap.streamkap_signal` | Clean up processed signals | **Heartbeat table (required if heartbeats are enabled):** | Permission | Scope | Purpose | | ---------- | ------------------------------- | ------------------------------ | | `SELECT` | `streamkap.streamkap_heartbeat` | Read heartbeat state | | `INSERT` | `streamkap.streamkap_heartbeat` | Write heartbeat records | | `UPDATE` | `streamkap.streamkap_heartbeat` | Update heartbeat timestamps | | `DELETE` | `streamkap.streamkap_heartbeat` | Clean up old heartbeat records | MariaDB has GTID enabled by default. If you are using the default GTID configuration with a read-only connection, the signal table permissions are not needed. The heartbeat table still requires permissions for monitoring and diagnostics. **Combined GRANT statements:** ```sql SQL theme={null} -- Core permissions (always required) GRANT REPLICATION CLIENT, RELOAD, SHOW DATABASES, REPLICATION SLAVE ON *.* TO 'streamkap_user'@'%'; GRANT SELECT ON {schema}.* TO 'streamkap_user'@'%'; -- Signal table (if GTID is disabled) GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_signal TO 'streamkap_user'@'%'; -- Heartbeat table (if heartbeats are enabled) GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO 'streamkap_user'@'%'; ``` # Granting Privileges It's recommended to create a separate user and role for Streamkap to access your MariaDB database. Below is an example script that does that. ```SQL SQL theme={null} -- Replace { ... } placeholders as required -- Create user CREATE USER streamkap_user@'%' IDENTIFIED BY 'password'; -- Grant permissions GRANT REPLICATION CLIENT, RELOAD, SHOW DATABASES, REPLICATION SLAVE ON *.* TO streamkap_user@'%'; -- Grant select on all schemas needed GRANT SELECT ON {schema}.* TO 'streamkap_user'@'%'; ``` ## Enable Snapshots You can perform ad-hoc snapshots of all or some of your tables in the Streamkap app. See [Snapshots & Backfilling](/snapshots) for more information. To enable this feature, there are 2 methods available for MariaDB databases. ### Method 1: Enable GTID (default) Global transaction identifiers (GTIDs) uniquely identify transactions that occur on a server within a cluster. Though not required, using GTIDs simplifies replication and enables you to more easily confirm if primary and replica servers are consistent as well as carry out incremental snapshots. For MariaDB, this is **enabled by default**, no additional setup is necessary. ### Method 2: Create a table in the source database If for some reason you have disabled GTIDs and cannot enable them, you will need to create the table and give permissions to the `streamkap_user`. Streamkap will use this collection for managing snapshots. The examples below use `streamkap_signal` as the signal table name, but you can choose any name. During [Streamkap Setup](#3-snapshot-settings), provide the full path to your signal table in `database.table` format (e.g., `streamkap.streamkap_signal`). ```SQL SQL theme={null} -- Create the schema CREATE SCHEMA streamkap; CREATE TABLE streamkap.streamkap_signal ( id VARCHAR(255) PRIMARY KEY, type VARCHAR(32) NOT NULL, data VARCHAR(2000) NULL ); GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_signal TO 'streamkap_user'@'%'; ``` ## Heartbeats Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. For read-write connections (when **Read only** is **No** during Streamkap Setup), the Connector writes to the heartbeat table directly. ```SQL SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Switch to the streamkap schema USE streamkap; -- Create the heartbeat table with id, text, and last_update fields CREATE TABLE streamkap_heartbeat ( id INT AUTO_INCREMENT PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ); -- Grant permission to the Streamkap user GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO 'streamkap_user'@'%'; -- Insert the first row into the heartbeat table INSERT INTO streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` For read-only connections (when **Read only** is **Yes** during Streamkap Setup), the Connector cannot write to the heartbeat table directly. Instead, you must configure a scheduled job on the **primary** database to generate artificial traffic. These changes will replicate to the read replica, which the Connector then consumes. **Run these commands on the primary database, not the read replica.** The heartbeat table and scheduled event must be created on the primary database. The changes will automatically replicate to the read replica. The MariaDB Event Scheduler flag must be enabled on your database (`event_scheduler=ON`). See your provider's documentation: Modifying parameters in a DB parameter group in Amazon RDS Event Scheduler documentation Check if the event scheduler is enabled: ```sql SQL theme={null} SHOW VARIABLES WHERE VARIABLE_NAME = 'event_scheduler'; ``` **Making this setting persistent** For self-hosted MariaDB, add `event_scheduler = ON` to your MariaDB configuration file (usually `my.cnf` or `my.ini`) in the `[mysqld]` section. For managed databases (RDS), configure the `event_scheduler` parameter through your provider's console or CLI. ```sql SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Switch to the streamkap schema USE streamkap; -- Create the heartbeat table CREATE TABLE streamkap.streamkap_heartbeat ( id INT AUTO_INCREMENT PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Insert the initial row INSERT INTO streamkap.streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` ```sql SQL theme={null} CREATE EVENT streamkap.streamkap_heartbeat_event ON SCHEDULE EVERY 1 MINUTE DO UPDATE streamkap.streamkap_heartbeat SET text = 'updated_heartbeat', last_update = CURRENT_TIMESTAMP WHERE id = 1; ``` Whichever database user is used to create and run the event scheduler (often the MariaDB root user or a dedicated event scheduler user) needs appropriate permissions on the heartbeat table. Additionally, the Streamkap user also needs permissions to monitor the heartbeat table. ```sql SQL theme={null} GRANT EVENT ON streamkap.* TO {event scheduler user}; GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO {event scheduler user}; -- Grant permissions to the Streamkap user for monitoring and diagnostics GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO 'streamkap_user'@'%'; ``` ```sql SQL theme={null} -- View all scheduled events in the streamkap schema SHOW EVENTS IN streamkap; -- View event details SELECT * FROM information_schema.EVENTS WHERE EVENT_SCHEMA = 'streamkap'; -- Disable an event temporarily ALTER EVENT streamkap.streamkap_heartbeat_event DISABLE; -- Enable an event ALTER EVENT streamkap.streamkap_heartbeat_event ENABLE; -- Drop an event DROP EVENT IF EXISTS streamkap.streamkap_heartbeat_event; ``` # Update Server Configuration File Open a connection to your MariaDB database's server. Access your MariaDB server configuration file (usually `/etc/my.cnf`). These lines enable `ROW` format binary log replication which Streamkap needs to perform incremental updates. * Enable binary logging * Set a unique `server-id` number if not set already. If your configuration already has a `server-id` entry, you don't need to change it. Otherwise, choose a number between 1 and 4294967295 as the `server-id`. * Set a minimum 3 days for log expiry (default is 30 days) ```bash theme={null} binlog_format=ROW binlog_row_image=FULL log_bin=mariadb-binlog server-id=123456789 binlog_expire_logs_seconds=259200 ``` * Restart your MariaDB server for these changes to take effect ## Validate binlog row value options To enable the connector to consume `UPDATE` events, this variable must be set to a value other than `PARTIAL_JSON`. * Check current variable value: `show global variables where variable_name = 'binlog_row_value_options';` * If the value of the variable is set to `PARTIAL_JSON`, run the following command to unset it: `set @@global.binlog_row_value_options="";` ## Verify binary logs are enabled You can either: * Run the following SQL query on the DB instance `SHOW VARIABLES LIKE '%log_bin%';`. Result should be `ON` * Run `SHOW BINARY LOGS` # Consider Access Restrictions * Visit [Connection Options](/connection-options) to ensure Streamkap can reach your database # Setup MariaDB Connector in Streamkap * Go to Sources and click Create New * Input * Name for your Connector * Hostname * Port (Default `3306`) * Username (Username you chose earlier, our scripts use `streamkap_user`) * Password * Read only * Whether or not to use a read-only connection. MariaDB has GTID enabled by default, so no additional configuration is necessary. See [Enable GTID](/mariadb-generic#method-1-enable-gtid) for more information. * If you have disabled GTID mode and cannot enable it, set **Read only** to **No** and create the signal table as described [here](/mariadb-generic#method-2-create-a-table-in-the-source-database). * Signal Table: Full path to the signal table including database and table name (e.g., `streamkap.streamkap_signal`). This table is used for incremental snapshotting. See [Enable Snapshots](/mariadb-generic#enable-snapshots) for setup instructions. * Heartbeats: Enabled by default. * For **read-write** connections, configure a heartbeat table in the source database and set **Heartbeat Table Database**. See [Heartbeats](#heartbeats) for setup instructions. * For **read-only** connections, configure a scheduled heartbeat event on the primary database using the MariaDB Event Scheduler, and include the heartbeat table in [Add Schemas/Tables](#setup-mariadb-connector-in-streamkap). See [Heartbeats](#heartbeats) for setup instructions. * Connection Timezone - The timezone of your database * **Timezone conversion** MariaDB converts `TIMESTAMP` values from the current time zone to UTC for storage, and back from UTC to the current time zone for retrieval. By default, the current time zone for each connection is the database server's time zone but this option allows you to override that. As long as the time zones remain the same, you get back the same value you store. We recommend using the default `SERVER` option which attempts to detect the session time zone from the values configured on the MariaDB server session variables 'time\_zone' or 'system\_time\_zone'. It also reduces the chance of problems with daylight savings adjustment 'fall back' and 'spring forward'. If either time zones change, an [ad-hoc snapshot](/snapshots#triggering-a-snapshot) is recommended so your source and destination timestamps are consistent. * Connect via SSH Tunnel. See [SSH Tunnel](/ssh-tunnel) * Advanced Parameters * Represent Binary Data As (Default `bytes`) * Capture Only Captured Databases DDL (Default `false`) - Used to control whether the connector records schema structures from all databases defined in the server (the default) or only those databases for which you've explicitly configured the connector. Specify `true` to capture schema history only for the specific databases you've configured. This is particularly valuable when databases are large, to reduce the volume of DDL stored in the schema history topic. It also improves startup times when the connector restarts or recovers from failures. See [Schema History Optimization](/schema-history-optimization) for details. * Capture Only Captured Tables DDL (Default `false`) - Used to control whether the connector records the schema structure for all tables in the configured databases (the default) or only the tables whose changes the connector captures. Specify `true` to capture schema history only for the specific tables you've configured. This is particularly valuable when tables are large, to reduce the volume of DDL statements stored in the schema history topic. It also improves startup times when the connector restarts or recovers from failures. See [Schema History Optimization](/schema-history-optimization) for details. * Add Schemas/Tables. Can also bulk upload here. The format is a simple list of each schema or table per row saved in csv format without a header. **CDC only captures base tables, not Views** Change Data Capture reads the MariaDB binary log (binlog), which only records changes to physical tables. Database Views are query-time computations with no physical storage—they don't generate binlog entries. **What you cannot capture:** Views, temporary tables, MEMORY tables, BLACKHOLE tables, or system tables (information\_schema, performance\_schema). **Solution:** Specify only the underlying base tables that feed your views. You can recreate the view logic in your destination or transformation layer. **VIRTUAL generated columns are not streamed** MariaDB [VIRTUAL generated columns](https://mariadb.com/kb/en/generated-columns/) are computed when rows are read and are **not** written to the binary log, so live streaming cannot capture or update their values. (PERSISTENT/stored generated columns are written to the binary log and stream normally.) Snapshots read the table directly, so they **do** capture a value for VIRTUAL columns — but because streaming cannot keep it current, that value will become stale. **We recommend excluding VIRTUAL generated columns from capture:** in the Source's **Settings → Advanced** section, set **Column Selection Mode** to **Exclusion** and add the column to the **Column Exclusion List** (fully-qualified, e.g. `database.table.column`). If you need the value at your destination, calculate it there — or with a [transform](/transforms) — from the columns it is derived from. * Click Save The connector will take approximately 1 minute to start processing data. **Adding tables programmatically** -- You can add tables to an existing source via the API or Terraform using the `table.include.list.user.defined` parameter (Terraform: `table_include_list_user_defined`). See the [REST API](/api) for getting started with the API, or [Terraform Resources](/terraform-resources#sources) for Terraform source configuration. # MCP Server Source: https://docs.streamkap.com/mcp-server Connect Claude, VS Code Copilot, and other MCP clients to Streamkap with OAuth sign-in or API tokens to manage CDC pipelines via natural language. The Streamkap MCP Server lets AI agents interact with your Streamkap Change Data Capture (CDC) infrastructure using natural language. It wraps the [Streamkap REST API](/api-reference/authentication/access-token) as [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) tools that agents can call to monitor pipeline health, inspect sources, destinations, topics, and transforms, manage the full lifecycle of your resources, and troubleshoot issues through logs and metrics. ## Prerequisites * A Streamkap account. The hosted server supports browser-based [OAuth sign-in](#sign-in-with-oauth-remote) — no API keys to create. * **For header-credential or local setups:** an API token (Client ID and Client Secret — see [API Tokens](/api-tokens)) or a [Project Key](/project-keys). * **Local mode only:** Node.js 20+ Check your Node.js version: ```bash theme={null} node -v # should be v20.x or higher ``` If you need to install or update Node.js, visit [nodejs.org](https://nodejs.org) or use [nvm](https://github.com/nvm-sh/nvm). **macOS users with nvm + Claude Desktop:** Claude Desktop is a GUI app that does not source your shell profile (`~/.zshrc`, `~/.bashrc`), so it cannot find your nvm-managed Node.js. You must provide the absolute path to `npx` and set the `PATH` environment variable explicitly in your config — see the [Claude Desktop tab](#setup) below for the verified configuration. ## Setup Modes You can connect in three ways: * **Remote — OAuth sign-in (recommended):** Connect to the hosted server at `https://mcp.streamkap.com/mcp` and authorize with your Streamkap account in the browser. No local install, no API keys to create or store. See [Sign in with OAuth](#sign-in-with-oauth-remote). * **Remote — header credentials:** Connect to the same hosted URL and pass an API token as HTTP headers (`X-Streamkap-Client-ID` / `X-Streamkap-Client-Secret`). Best for headless and CI clients that can set headers but can't complete a browser sign-in. * **Local (via npx):** Run the server locally as a child process with `npx -y @streamkap/tools`. Credentials are passed as environment variables. Requires Node.js 20+. [Real-time topic streaming](#real-time-streaming-remote-mode-only) is available on the hosted server. ## Sign in with OAuth (remote) The hosted server is an OAuth 2.1 provider, so most MCP clients can connect with a browser sign-in instead of API keys. Point your client at `https://mcp.streamkap.com/mcp` with no credentials; on first connect it opens a browser where you sign in to Streamkap and authorize access. Your client stores and refreshes the tokens automatically. ```bash theme={null} claude mcp add --scope user --transport http streamkap https://mcp.streamkap.com/mcp ``` Then run `/mcp` inside Claude Code and choose **Authenticate** for the `streamkap` server to complete sign-in in your browser. The `--scope user` flag registers the server for every project, not just the current directory. Open **Settings → Connectors → Add custom connector**, enter the URL `https://mcp.streamkap.com/mcp`, then click **Connect** and sign in. This is the recommended way to use the hosted server with Claude Desktop. Add to `.vscode/mcp.json` — no credentials needed; VS Code prompts you to sign in on first use: ```json theme={null} { "servers": { "streamkap": { "type": "http", "url": "https://mcp.streamkap.com/mcp" } } } ``` Any client that supports remote MCP servers with OAuth can connect to `https://mcp.streamkap.com/mcp` with no credentials and will prompt for sign-in. If your client can't complete a browser flow, use [header credentials](#setup) instead. OAuth sign-in grants a curated set of tools: agents can read, create, update, and operate your resources, but **destructive actions are blocked** — they can't delete resources or reset connectors. For unrestricted access or custom tool scoping, use [header credentials](#setup) or a [Project Key](/project-keys#step-3-mcp-scoping). ## Setup The tabs below cover the **header-credential** (remote) and **local (npx)** setups. To connect with a browser sign-in instead, see [Sign in with OAuth](#sign-in-with-oauth-remote). **Remote (header credentials):** ```bash theme={null} claude mcp add --scope user \ --header "X-Streamkap-Client-ID: your-client-id" \ --header "X-Streamkap-Client-Secret: your-client-secret" \ --transport http \ streamkap https://mcp.streamkap.com/mcp ``` **Local (via npx):** ```bash theme={null} claude mcp add --scope user streamkap \ -e STREAMKAP_CLIENT_ID=your-client-id \ -e STREAMKAP_CLIENT_SECRET=your-client-secret \ -- npx -y @streamkap/tools ``` **Verify the server is registered:** ```bash theme={null} claude mcp list ``` Add to `.mcp.json` in your project root: **Remote (header credentials):** ```json theme={null} { "mcpServers": { "streamkap": { "type": "http", "url": "https://mcp.streamkap.com/mcp", "headers": { "X-Streamkap-Client-ID": "your-client-id", "X-Streamkap-Client-Secret": "your-client-secret" } } } } ``` **Local (via npx):** ```json theme={null} { "mcpServers": { "streamkap": { "command": "npx", "args": ["-y", "@streamkap/tools"], "env": { "PATH": "/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin", "STREAMKAP_CLIENT_ID": "your-client-id", "STREAMKAP_CLIENT_SECRET": "your-client-secret" } } } } ``` For the hosted server, connect Claude Desktop with [OAuth sign-in](#sign-in-with-oauth-remote). Header-credential forwarding via `claude_desktop_config.json` is unreliable across Claude Desktop builds, so for **header-credential** setups use local stdio mode instead — add the entry to your config file: * **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json` * **Windows:** `%APPDATA%\Claude\claude_desktop_config.json` The `streamkap` entry must be nested inside the `mcpServers` object. Placing it at the root level of the config file will cause it to be silently ignored. **Local (via npx) — macOS with nvm:** Use the absolute path to your nvm-managed `npx`. Claude Desktop is a GUI app and does not source your shell profile, so it will not find `npx` on its own. Replace the version below with your installed Node.js version (`node -v`): ```json theme={null} { "mcpServers": { "streamkap": { "command": "/Users//.nvm/versions/node/v22.22.0/bin/npx", "args": ["-y", "@streamkap/tools"], "env": { "PATH": "/Users//.nvm/versions/node/v22.22.0/bin:/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin", "STREAMKAP_CLIENT_ID": "your-client-id", "STREAMKAP_CLIENT_SECRET": "your-client-secret" } } } } ``` Find your absolute path with: ```bash theme={null} echo "$HOME/.nvm/versions/node/$(node -v)/bin/npx" ``` **Local (via npx) — macOS without nvm (Homebrew):** ```json theme={null} { "mcpServers": { "streamkap": { "command": "/opt/homebrew/bin/npx", "args": ["-y", "@streamkap/tools"], "env": { "PATH": "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin", "STREAMKAP_CLIENT_ID": "your-client-id", "STREAMKAP_CLIENT_SECRET": "your-client-secret" } } } } ``` Use `/usr/local/bin/npx` instead on Intel Macs. **Local (via npx) — Windows:** ```json theme={null} { "mcpServers": { "streamkap": { "command": "C:\\Program Files\\nodejs\\npx.cmd", "args": ["-y", "@streamkap/tools"], "env": { "STREAMKAP_CLIENT_ID": "your-client-id", "STREAMKAP_CLIENT_SECRET": "your-client-secret" } } } } ``` **Windows:** Run `where npx` in Command Prompt to find the correct path to `npx.cmd` on your system. Add to `.vscode/mcp.json` (uses a different schema than other clients): **Remote (header credentials):** ```json theme={null} { "servers": { "streamkap": { "type": "http", "url": "https://mcp.streamkap.com/mcp", "headers": { "X-Streamkap-Client-ID": "your-client-id", "X-Streamkap-Client-Secret": "your-client-secret" } } } } ``` **Local (via npx):** ```json theme={null} { "servers": { "streamkap": { "type": "stdio", "command": "npx", "args": ["-y", "@streamkap/tools"], "env": { "PATH": "/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin", "STREAMKAP_CLIENT_ID": "your-client-id", "STREAMKAP_CLIENT_SECRET": "your-client-secret" } } } } ``` Using a different MCP-compatible client? Connect with the URL `https://mcp.streamkap.com/mcp` plus `X-Streamkap-Client-ID` / `X-Streamkap-Client-Secret` headers (remote), or run `npx -y @streamkap/tools` with the same values as environment variables (local). Running locally? You can pass additional configuration via [Optional Environment Variables](#optional-environment-variables) — for example, `LOG_LEVEL=debug` for troubleshooting, or Kafka credentials to enable the direct Kafka tools. ## Verify Your Setup (Optional) If things aren't working, the fastest way to check whether it's your credentials or your client config is to test the same credentials from the terminal using the [Streamkap CLI](/cli): ```bash theme={null} npm install -g @streamkap/tools export STREAMKAP_CLIENT_ID="your-client-id" export STREAMKAP_CLIENT_SECRET="your-client-secret" streamkap doctor ``` If `doctor` passes the credentials and API authentication checks, the same values will work in your MCP client. This step is optional but the fastest way to catch a bad credential before debugging your client config. ## Available Tools Once connected, your agent has access to tools covering the full Streamkap API surface: | Category | What your agent can do | | -------------------- | ------------------------------------------------------------------------------------------------------------ | | **Pipelines** | List, create, update, delete, start, stop, restart, view metrics and logs, bulk operations | | **Sources** | Manage CDC connectors — deploy, pause, resume, restart, stop, snapshot, check status | | **Destinations** | Manage sinks — deploy, pause, resume, restart, stop, monitor throughput and lag | | **Transforms** | Create and manage stream processors, deploy to preview or production, run unit tests, inspect failed records | | **Topics** | List and inspect Kafka topics, read sample messages | | **Tags** | Organise and search resources by tag | | **Schema Registry** | Browse subjects and schemas | | **Consumer Groups** | Inspect lag, identify stuck consumers, reset offsets | | **Dashboard & Logs** | Organisation-wide stats, data lineage between sources and destinations, search and filter logs | | **Alerts** | Manage notification subscribers and preferences | | **Usage** | Query and export usage metrics | | **Kafka Access** | Manage direct-Kafka users (list, create, update, delete) | | **Cluster Scaling** | Inspect cluster status, scale up or down, track scaling operations | | **Admin** | List services and switch between them | For the full list of API operations, see the [API Reference](/api-reference/authentication/access-token). ### Optional: Direct Kafka Tools Two additional tools — `streamkap_consume_messages` and `streamkap_produce_message` — let agents interact with Kafka directly. They require Kafka credentials (bootstrap servers, username, password — all three together) from the [Kafka Access](/kafka-access) page. In local mode, set `KAFKA_BOOTSTRAP_SERVERS`, `KAFKA_API_KEY`, and `KAFKA_API_SECRET` as environment variables. Without Kafka credentials, agents can still read topic data via the REST-based `streamkap_get_topic_messages` tool. ## Workflow Prompts The MCP server registers seven workflow prompts that surface as slash commands or prompt picks in most clients. Each prompt walks an agent through a structured operational task: | Prompt | Purpose | | ------------------------- | ----------------------------------------------------------------------------------------------- | | `troubleshoot-pipeline` | Diagnose a specific pipeline (or all pipelines): health, metrics, logs, dead letter queue (DLQ) | | `setup-pipeline` | End-to-end guide for creating a new source → destination pipeline | | `infrastructure-overview` | High-level health check across sources, destinations, pipelines, transforms | | `diagnose-dlq-errors` | Investigate dead letter queue topics and classify errors | | `report-pipeline-health` | Generate a comprehensive pipeline health report with metrics and DLQ analysis | | `deploy-monitoring-agent` | Set up monitoring across pipelines, sources, and destinations | | `triage-agent-error` | Triage issues with any Streamkap entity using logs, metrics, and DLQ inspection | These prompts appear in your client's prompt picker UI (or as slash commands in clients that support them — the exact format varies between clients). ## Real-Time Streaming (Remote Mode Only) The hosted server exposes an SSE endpoint for real-time topic streaming at `https://mcp.streamkap.com/subscribe/`. Supports single topics, comma-separated lists, and regex patterns (`?pattern=source_.*`). Uses the same `X-Streamkap-Client-ID` / `X-Streamkap-Client-Secret` headers as MCP tool calls. Connect with any SSE client, `curl`, or from your application code. Not available in local mode. For terminal-based real-time consumption, use [`streamkap kafka subscribe`](/cli#direct-kafka-access). ## Authentication The MCP server accepts three credentials: * **OAuth sign-in** (hosted server) — authorize with your Streamkap account in the browser; no keys to manage. See [Sign in with OAuth](#sign-in-with-oauth-remote). * **API token** — a Client ID and Client Secret, passed as headers in remote mode or environment variables in local mode. See [API Tokens](/api-tokens). * **Project Key** — a single bundled credential that also carries [tool scoping](/project-keys#step-3-mcp-scoping). See [Project Keys](/project-keys). For API tokens and Project Keys, Streamkap handles token exchange and refresh automatically. Keep your Client Secret safe. If compromised, delete the token from the [API Tokens](/api-tokens) page and create a new one. ### Restricting which tools an agent can use By default an agent can call every tool its credential is allowed. To narrow that surface: * **Project Key (recommended):** set a tool profile and allow/block lists when you create the key — scoping is enforced server-side and can't be tampered with client-side. See [MCP scoping](/project-keys#step-3-mcp-scoping). * **Headers (remote) or environment variables (local):** set `X-Streamkap-Tool-Profile` / `MCP_TOOL_PROFILE`, `X-Streamkap-Allow-Tools` / `MCP_ALLOW_TOOLS`, or `X-Streamkap-Block-Tools` / `MCP_BLOCK_TOOLS`. Allow lists win over block lists, which win over the profile. * **OAuth sign-in:** uses a fixed curated profile that blocks destructive actions — see [Sign in with OAuth](#sign-in-with-oauth-remote). ## Optional Environment Variables (Local Mode) | Variable | Description | | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | `STREAMKAP_PROJECT_KEY` | Base64-encoded [Project Key](/project-keys) — bundles API credentials, Kafka access, and tool scoping in a single value | | `STREAMKAP_API_URL` | Override the API base URL (defaults to `https://api.streamkap.com`) | | `MCP_TOOL_PROFILE` | Restrict the agent to a tool profile: `read-only`, `agent-operator`, `infra-admin`, or `full` | | `MCP_ALLOW_TOOLS` / `MCP_BLOCK_TOOLS` | Comma-separated tool allow/block lists. Allow wins over block, which wins over the profile | | `LOG_LEVEL` | Server log verbosity: `trace`, `debug`, `info` (default), `warn`, `error`, `fatal` | For Kafka and Schema Registry credentials, see [Direct Kafka Tools](#optional-direct-kafka-tools) and [Kafka Access](/kafka-access). ## Troubleshooting ### Log file locations **Claude Desktop** (log file name matches the key in your `mcpServers` config): * macOS: `~/Library/Logs/Claude/mcp-server-streamkap.log` * Windows: `%APPDATA%\Claude\logs\mcp-server-streamkap.log` ### macOS: nvm users — `npx` not found or wrong version Claude Desktop does not source your shell profile, so it cannot find nvm-managed Node.js. Find your absolute `npx` path: ```bash theme={null} echo "$HOME/.nvm/versions/node/$(node -v)/bin/npx" ``` Then use it as the `command` in your config. See the [Claude Desktop tab](#setup) above for the full JSON example. **Homebrew users (no nvm):** use `/opt/homebrew/bin/npx` (Apple Silicon) or `/usr/local/bin/npx` (Intel). ### Common errors | Symptom | Likely cause | Fix | | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ERROR: You must supply a command` | Old `npx` resolving via default PATH instead of Node 20+ | Use the absolute path to `npx` from Node 20+ (see nvm section above) | | `Authentication failed` / 401 errors on every tool call | Missing or invalid Client ID / Secret | Verify your credentials in the [API Tokens](/api-tokens) dashboard, then run `streamkap doctor` to confirm | | Server appears in client but tools fail silently | Credentials reached the server but are wrong for your tenant | Run `streamkap doctor` with the same credentials to surface the underlying error | | Server connects then immediately disconnects | Wrong Node version or package install failure | Check the MCP log file for `npx` stderr output | | Claude Desktop ignores the `streamkap` entry | Entry placed at root of `claude_desktop_config.json` instead of inside `mcpServers` | Move it inside the `mcpServers` object | | Claude Desktop fails to connect with `"type": "http"` or `"type": "sse"` config | Header-credential remote MCP via `claude_desktop_config.json` is not reliable in Claude Desktop | For the hosted server, connect via [OAuth sign-in](#sign-in-with-oauth-remote) (Add custom connector); otherwise switch to local stdio mode using the [Claude Desktop tab](#setup) above | | Need more detail in local mode | Default log level is `info` | Add `"LOG_LEVEL": "debug"` to the `env` block and re-check the log file | ## Documentation MCP The Streamkap docs site runs its own MCP server at `https://docs.streamkap.com/mcp`. It is **separate** from the API server above and carries **no credentials** — it lets an agent search and read the Streamkap documentation (connector configs, how-tos, API reference) while it works. Add it alongside the `streamkap` server so your agent can look something up and act on it in the same session. ```bash theme={null} claude mcp add --scope user --transport http streamkap-docs https://docs.streamkap.com/mcp ``` Add to `.vscode/mcp.json` (VS Code uses the `servers` key): ```json theme={null} { "servers": { "streamkap-docs": { "type": "http", "url": "https://docs.streamkap.com/mcp" } } } ``` Cursor, Windsurf, and most other clients use the `mcpServers` key: ```json theme={null} { "mcpServers": { "streamkap-docs": { "type": "http", "url": "https://docs.streamkap.com/mcp" } } } ``` In Claude Desktop, add it as a custom connector instead: **Settings → Connectors → Add custom connector**, then enter `https://docs.streamkap.com/mcp`. The docs MCP serves public documentation only — no sign-in, and it cannot read or change your infrastructure. To manage resources, use the [Streamkap MCP Server](#sign-in-with-oauth-remote) above. ## Related * [Agents](/agents) — overview of all agent integration paths * [CLI](/cli) — command-line tool with agent-friendly JSON output and scripting support * [API Reference](/api-reference/authentication/access-token) — full REST API documentation # Metadata Source: https://docs.streamkap.com/metadata Metadata columns added by Streamkap to destination tables Streamkap Connectors (where applicable) insert the following metadata columns into the data and destination tables: ## Using Metadata Columns Metadata columns enable several important patterns: * **Delete handling**: Use the `__DELETED` column to identify rows deleted at the source. See [Delete Handling](/insertsupserts#delete-handling) for configuration options and query patterns. * **Deduplication**: When using insert/append mode, use `_STREAMKAP_TS_MS` and `_STREAMKAP_OFFSET` to build deduplicated final-state tables. See [Creating Final State Tables](/creating-final-state-tables-from-insertsappend) for examples with Snowflake Dynamic Tables, Tasks, and dbt. * **Debugging**: Use `_STREAMKAP_SOURCE_TS_MS` to determine when events occurred at the source, and `_STREAMKAP_TS_MS` to determine when Streamkap processed them. The difference indicates pipeline latency. # Overview Source: https://docs.streamkap.com/mongodb Streamkap supports syncing MongoDB in real-time using change data capture for: If you don't see your platform listed, please contact us as we're happy to help find and provide the setup documentation you need. # MongoDB (Generic) Source: https://docs.streamkap.com/mongodb-1 ## Prerequisites * MongoDB version ≥ 5.0 * A MongoDB user with sufficient privileges to create database users and collections ## MongoDB Setup ### 1. Grant Database Access * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your database. ### 2. Create Database User #### MongoDB Shell * Using MongoDB Shell, connect to your primary node or replica set. * Create a user for Streamkap using the script below. Replace password with your choice. ```bash Shell theme={null} use admin db.createUser({ user: "streamkap_user", pwd: "{password}", roles: [ "readAnyDatabase", {role: "read", db: "local"} ] }) ``` ### 3. Enable Snapshots To backfill your data, the Connector needs to be able to perform snapshots. See [Snapshots & Backfilling](/snapshots) for more information. You will need to create the table and give necessary permissions to the `streamkap_user`. The Connector will use this collection for managing snapshots. Below is an example script that does that. This collection can exist in a different database (on the same MongoDB cluster) to the database Streamkap captures data from. The examples below use `streamkap_signal` as the signal collection name, but you can choose any name. During [Streamkap Setup](#3-snapshot-settings), provide the full path to your signal collection in `database.collection` format (e.g., `streamkap.streamkap_signal`). #### MongoDB Shell ```bash Shell theme={null} db.createCollection("streamkap_signal") db.grantRolesToUser("streamkap_user", [ { role: "read", db: "{database}" }, { role: "readWrite", db: "{database}", collection: "streamkap_signal" } ]) ``` ### 4. Heartbeats MongoDB uses change streams to track changes. While change streams use resume tokens to track position, these tokens can expire or become invalidated—particularly on clusters with high write activity or when using custom aggregation pipelines that filter events. Heartbeats ensure the Connector receives regular change events, keeping resume tokens fresh and providing liveness monitoring. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** Layer 2 is especially important when: * Your database has low or intermittent traffic * You use custom aggregation pipelines that filter out many events * You need reliable liveness monitoring We recommend configuring Layer 2 for all deployments to provide additional resilience. You can configure regular updates to a dedicated heartbeat collection in the source database. This simulates activity, ensuring change events are generated consistently and resume tokens remain valid. Since the MongoDB Connector doesn't write directly to the database, you must configure an external scheduler (e.g., cron job, Kubernetes CronJob) to generate artificial traffic. Connect to your MongoDB instance and create the heartbeat collection: ```javascript theme={null} use streamkap db.createCollection("streamkap_heartbeat") // Insert initial document db.streamkap_heartbeat.insertOne({ _id: "heartbeat", last_update: new Date() }) ``` ```javascript theme={null} db.grantRolesToUser("streamkap_user", [ { role: "read", db: "streamkap" } ]) ``` Create a script that updates the heartbeat document: ```bash theme={null} #!/bin/bash # heartbeat.sh MONGO_URI="mongodb://heartbeat_user:password@localhost:27017/streamkap?authSource=admin" mongosh "$MONGO_URI" --eval ' db.streamkap_heartbeat.updateOne( { _id: "heartbeat" }, { $set: { last_update: new Date() } }, { upsert: true } ) ' ``` Make the script executable: ```bash theme={null} chmod +x heartbeat.sh ``` **Using cron (Linux/macOS):** ```bash theme={null} # Edit crontab crontab -e # Add this line to run every minute * * * * * /path/to/heartbeat.sh >> /var/log/mongodb-heartbeat.log 2>&1 ``` **Using Kubernetes CronJob:** ```yaml theme={null} apiVersion: batch/v1 kind: CronJob metadata: name: mongodb-heartbeat spec: schedule: "* * * * *" # Every minute jobTemplate: spec: template: spec: containers: - name: heartbeat image: mongo:latest command: - mongosh - "mongodb://heartbeat_user:password@mongodb-host:27017/streamkap?authSource=admin" - --eval - | db.streamkap_heartbeat.updateOne( { _id: "heartbeat" }, { $set: { last_update: new Date() } }, { upsert: true } ) restartPolicy: OnFailure ``` ### 5. Obtain Connection String You'll need the connection string for setting up the Connector in Streamkap. #### MongoDB Shell * Connect to your replica set or primary node using the MongoDB shell as an Admin user. * Run `db.getMongo()` method to return your connection string * We recommend the connection string have the following parameters. They will be added automatically if not included: * `w=majority` * `readPreference=primaryPreferred` For information on accepted connection string formats, please see [MongoDB - Connection String Formats](https://www.mongodb.com/docs/manual/reference/connection-string/#connection-string-formats) ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **MongoDB**. * **MongoDB**. ### 2. Connection Settings * **Name:** Enter a name for your connector * **Connection String:** Copy the connection string from earlier steps but replace username and password in the string with the one you created earlier. * **Array Encoding:** Specify how Streamkap should encode MongoDB array types. `Array` encodes them as a JSON array but requires all elements in the arrays to be of the same type e.g. array of integers. `Array_String` encodes them as a JSON string and must be used if the MongoDB arrays have mixed types. * **Nested Document Encoding:** Specify how Streamkap should encode nested documents. `Document` encodes them as JSON objects but may be problematic for complex (e.g. multiple levels of nested sub documents and arrays, sub arrays of nested documents) documents. `String` encodes them as a JSON string and we recommend it if the MongoDB nested documents are complex. * **Connect via SSH Tunnel:** The Connector will connect to an SSH server in your network which has access to your database. This is necessary if the Connector cannot connect directly to your database. * See [SSH Tunnel](/ssh-tunnel) for setup instructions. ### 3. Snapshot Settings * **Signal Collection:** Full path to the signal collection including database and collection name (e.g., `streamkap.streamkap_signal`). This collection is used for incremental snapshotting. See [Enable Snapshots](#3-enable-snapshots) for setup instructions. ### 4. Database and Collection Capture * **Add Database/Collections:** Specify the database(s) and collection(s) for capture. * You can bulk upload here. The format is a simple list of databases and collections, with each entry on a new row. Save as a .csv file without a header. * If you configured Layer 2 heartbeats, include the heartbeat collection (e.g., `streamkap.streamkap_heartbeat`). See [Heartbeats](#4-heartbeats) for setup instructions. **CDC only captures base collections, not Views** Change Data Capture reads MongoDB's oplog via change streams, which only record changes to physical collections. Database Views are query-time aggregations with no physical storage—they don't generate oplog entries. **What you cannot capture:** Views (aggregation pipeline results), system collections (system.\*, admin.\*, config.\*). **Time series collections** (MongoDB 5.0+): These use specialized columnar-like bucketing storage that compresses documents by time windows. **Change streams are NOT supported** because individual document changes cannot be tracked—they're absorbed into compressed buckets. **Workaround**: Use regular collections with compound indexes like `{metadata: 1, timestamp: 1}` for CDC-compatible time series data. **Capped collections caveat**: Can be captured, but there's risk of missing events if the connector falls behind and the oplog position it needs gets overwritten. For mission-critical data, use regular collections. **Solution:** Specify only the underlying base collections that feed your views. You can recreate the view aggregation pipeline in your destination or transformation layer. Click **Save**. # MongoDB Atlas Source: https://docs.streamkap.com/mongodb-atlas MongoDB Atlas Change Data Capture Setup with Streamkap ## Prerequisites * MongoDB version ≥ 5.0 * A MongoDB user with sufficient privileges to create database users and collections ## MongoDB Atlas Setup ### 1. Grant Database Access * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your database. ### 2. Create Database User #### MongoDB Atlas * Log in to MongoDB Atlas and navigate to the MongoDB cluster. * In the left-hand navigation menu, go to **Security > Database Access**. * Click **New Database User**. * Choose the password authentication method. * Enter the username and password for the new Streamkap user (e.g. `streamkap_user`). * In the **Database User Privileges** drop-down menu, select **Grant Specific User Privileges**. * Under **Specific Privileges**, add the following roles/privileges: * `readAnyDatabase` * `read` on the `local` database * Click **Add User**. #### MongoDB Shell * Using MongoDB Shell, connect to your primary node or replica set. * Create a user for Streamkap using the script below. Replace password with your choice. ```bash Shell theme={null} use admin db.createUser({ user: "streamkap_user", pwd: "{password}", roles: [ "readAnyDatabase", {role: "read", db: "local"} ] }) ``` ### 3. Enable Snapshots To backfill your data, the Connector needs to be able to perform snapshots. See [Snapshots & Backfilling](/snapshots) for more information. You will need to create the collection and give necessary permissions to the `streamkap_user`. The Connector will use this collection for managing snapshots. This collection can exist in a different database (on the same MongoDB cluster) to the database Streamkap captures data from. The examples below use `streamkap_signal` as the signal collection name, but you can choose any name. During [Streamkap Setup](#3-snapshot-settings), provide the full path to your signal collection in `database.collection` format (e.g., `streamkap.streamkap_signal`). #### MongoDB Atlas * Navigate to the MongoDB cluster. * Navigate to **Collections** and create a new collection in the database by clicking the `+` button next to the database name. * Name the collection `streamkap_signal`. * Give the Streamkap user permissions to `readWrite` on the `streamkap_signal` collection. * Return to the **Database User Privileges** drop-down menu, select **Grant Specific User Privileges**. * Under **Specific Privileges**, add the following roles/privileges: `readWrite@{database}.streamkap_signal` #### MongoDB Shell ```bash Shell theme={null} db.createCollection("streamkap_signal") db.grantRolesToUser("streamkap_user", [ { role: "read", db: "{database}" }, { role: "readWrite", db: "{database}", collection: "streamkap_signal" } ]) ``` ### 4. Heartbeats MongoDB uses change streams to track changes. While change streams use resume tokens to track position, these tokens can expire or become invalidated—particularly on clusters with high write activity or when using custom aggregation pipelines that filter events. Heartbeats ensure the Connector receives regular change events, keeping resume tokens fresh and providing liveness monitoring. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** Layer 2 is especially important when: * Your database has low or intermittent traffic * You use custom aggregation pipelines that filter out many events * You need reliable liveness monitoring We recommend configuring Layer 2 for all deployments to provide additional resilience. You can configure regular updates to a dedicated heartbeat collection in the source database. This simulates activity, ensuring change events are generated consistently and resume tokens remain valid. Since the MongoDB Connector doesn't write directly to the database, you must configure a scheduled job to generate artificial traffic. For MongoDB Atlas, use [Atlas Scheduled Triggers](https://www.mongodb.com/docs/atlas/app-services/triggers/scheduled-triggers/). Create a collection to store heartbeat documents. This can be in the same database you're capturing or a dedicated `streamkap` database. **Using MongoDB Atlas UI:** 1. Navigate to your cluster and click **Browse Collections** 2. Click **Create Database** or select an existing database 3. Create a collection named `streamkap_heartbeat` **Using MongoDB Shell:** ```javascript theme={null} use streamkap db.createCollection("streamkap_heartbeat") ``` Ensure the Streamkap user has `read` access to the heartbeat collection. **Using MongoDB Atlas UI:** 1. Go to **Security > Database Access** 2. Edit the `streamkap_user` 3. Under **Specific Privileges**, add: `read@streamkap.streamkap_heartbeat` **Using MongoDB Shell:** ```javascript theme={null} db.grantRolesToUser("streamkap_user", [ { role: "read", db: "streamkap" } ]) ``` 1. In MongoDB Atlas, go to **App Services** (or **Triggers** in the left menu) 2. Click **Create a Trigger** 3. Select **Scheduled** trigger type 4. Configure the trigger: * **Name**: `streamkap_heartbeat_trigger` * **Schedule Type**: Basic * **Repeat once by**: Minute * **Every**: 1 minute(s) 5. In the **Function** section, select **Function** and create a new function: ```javascript theme={null} exports = async function() { const serviceName = "mongodb-atlas"; // Your cluster service name const database = "streamkap"; const collection = "streamkap_heartbeat"; const coll = context.services .get(serviceName) .db(database) .collection(collection); const result = await coll.updateOne( { _id: "heartbeat" }, { $set: { last_update: new Date() } }, { upsert: true } ); console.log(`Heartbeat updated: ${JSON.stringify(result)}`); return result; }; ``` 6. Click **Save** **Atlas App Services usage and cost** The scheduled trigger counts as an [App Services Request](https://www.mongodb.com/docs/atlas/app-services/billing/#requests). With a 1-minute interval: * \~1,440 requests/day (60/hour × 24 hours) * **Daily free tier**: 50,000 requests—this heartbeat uses only \~3% of the free allowance * **Cost if exceeding free tier**: \$0.000002 per request The heartbeat has minimal impact on your App Services usage quota. ### 5. Obtain Connection String You'll need the connection string for setting up the Connector in Streamkap. #### MongoDB Atlas * Log in to MongoDB Atlas and navigate to the cluster to which Streamkap should connect. * Click **Connect**. * Click **Connect your Application**. * Copy the connection string. #### MongoDB Shell * Connect to your replica set or primary node using the MongoDB shell as an Admin user. * Run `db.getMongo()` method to return your connection string. * We recommend the connection string have the following parameters. They will be added automatically if not included: * `w=majority` * `readPreference=primaryPreferred` For information on accepted connection string formats, please see [MongoDB - Connection String Formats](https://www.mongodb.com/docs/manual/reference/connection-string/#connection-string-formats) *** ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **MongoDB**. * Select **MongoDB Atlas**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Connection String**: Copy the connection string from earlier steps but replace username and password in the string with the one you created earlier. * **Array Encoding**: Specify how Streamkap should encode MongoDB array types. `Array` encodes them as a JSON array but requires all elements in the arrays to be of the same type e.g. array of integers. `Array_String` encodes them as a JSON string and must be used if the MongoDB arrays have mixed types. * **Nested Document Encoding**: Specify how Streamkap should encode nested documents. `Document` encodes them as JSON objects but may be problematic for complex (e.g. multiple levels of nested sub documents and arrays, sub arrays of nested documents) documents. `String` encodes them as a JSON string and we recommend it if the MongoDB nested documents are complex. * **Connect via SSH Tunnel**: The Connector will connect to an SSH server in your network which has access to your database. This is necessary if the Connector cannot connect directly to your database. * See [SSH Tunnel](/ssh-tunnel) for setup instructions. ### 3. Snapshot Settings * **Signal Collection**: Full path to the signal collection including database and collection name (e.g., `streamkap.streamkap_signal`). This collection is used for incremental snapshotting. See [Enable Snapshots](#3-enable-snapshots) for setup instructions. ### 4. Database and Collection Capture * **Add Database/Collections**: Specify the database(s) and collection(s) for capture. * You can bulk upload here. The format is a simple list of databases and collections, with each entry on a new row. Save as a `.csv` file without a header. * If you configured Layer 2 heartbeats, include the heartbeat collection (e.g., `streamkap.streamkap_heartbeat`). See [Heartbeats](#4-heartbeats) for setup instructions. Click **Save**. **Have questions?** See the [MongoDB Source FAQ](/mongodb-source-faq) for answers to common questions about MongoDB sources, troubleshooting, and best practices. # MongoDB Source FAQ Source: https://docs.streamkap.com/mongodb-source-faq Answers to common MongoDB and MongoDB Atlas source questions in Streamkap, covering CDC, snapshots, heartbeats, views, and oplog retention. This FAQ focuses on using MongoDB as a source in streamkap, including general self-hosted setups and cloud variants (MongoDB Atlas, Amazon DocumentDB). Streamkap's MongoDB connector provides real-time CDC with managed features like automatic scaling, UI setup, and ETL transformations. A MongoDB source in streamkap enables real-time Change Data Capture (CDC) from MongoDB databases, capturing row-level inserts, updates, and deletes with sub-second latency. It uses change streams to stream changes to destinations, supporting snapshots for initial loads, schema evolution, and handling for nested data. Streamkap offers a serverless setup via UI or API. * MongoDB 4.0+ for basic CDC; 6.0+ for advanced features like post-images and full document lookups. * Compatible with MongoDB 3.6+ in some modes. Streamkap supports: * Self-hosted (on-prem/VM). * MongoDB Atlas. * Amazon DocumentDB (MongoDB-compatible). * Streamkap also supports sharded clusters and replica sets, with automatic handling of shard additions/removals and membership changes. * **CDC**: Change streams for inserts/updates/deletes; supports oplog for resume tracking. * **Snapshots**: Ad-hoc/initial backfills using incremental or blocking methods; phased chunking for minimal impact. * **Schema Evolution**: Automatic handling of document changes; field renaming/exclusion. * **Data Types**: Supports integers, floats, strings, dates, arrays, objects, binary (configurable), JSON; extended JSON for identifiers. * **Ingestion Modes**: Inserts (append) or upserts. * **Security**: SSL, authentication, access control. * **Monitoring**: Latency, lag, queue sizes in-app; heartbeat messages. * Streamkap adds transaction metadata, filtering by collections/fields, and aggregation pipelines. Streamkap uses MongoDB change streams to capture and decode oplog data, emitting changes as events. It starts from the last recorded transaction, performs a snapshot if needed, then streams from the oplog position. Supports full document updates with pre/post-images (MongoDB 6.0+). * Trigger ad-hoc at source/table level. Methods: Incremental (phased, chunked by ID, default 1024 documents) or blocking (stops streaming temporarily). Uses watermarking for progress; supports partial snapshots via conditions. * Modes: `initial` (default), `always`, `initial_only`, `no_data`, `when_needed`, `configuration_based`, `custom`. Streamkap simplifies triggering via UI. MongoDB uses change streams to track changes via resume tokens. These tokens can expire or become invalidated—particularly on clusters with high write activity, limited oplog retention, or when using custom aggregation pipelines that filter out many events. Heartbeats ensure the Connector receives regular change events, keeping resume tokens fresh and providing liveness monitoring. This is especially important for: * Low-traffic or intermittent databases * Custom aggregation pipelines that filter many events * Scenarios requiring reliable liveness detection **Layer 1 (Connector heartbeats)** is enabled by default—the Connector emits heartbeat messages to an internal topic even when no data changes occur. **Layer 2 (Source database heartbeats)** requires configuring a scheduled job to update a heartbeat collection: MongoDB uses change streams to track changes. While change streams use resume tokens to track position, these tokens can expire or become invalidated—particularly on clusters with high write activity or when using custom aggregation pipelines that filter events. Heartbeats ensure the Connector receives regular change events, keeping resume tokens fresh and providing liveness monitoring. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** Layer 2 is especially important when: * Your database has low or intermittent traffic * You use custom aggregation pipelines that filter out many events * You need reliable liveness monitoring We recommend configuring Layer 2 for all deployments to provide additional resilience. You can configure regular updates to a dedicated heartbeat collection in the source database. This simulates activity, ensuring change events are generated consistently and resume tokens remain valid. Since the MongoDB Connector doesn't write directly to the database, you must configure a scheduled job to generate artificial traffic. For MongoDB Atlas, use [Atlas Scheduled Triggers](https://www.mongodb.com/docs/atlas/app-services/triggers/scheduled-triggers/). Create a collection to store heartbeat documents. This can be in the same database you're capturing or a dedicated `streamkap` database. **Using MongoDB Atlas UI:** 1. Navigate to your cluster and click **Browse Collections** 2. Click **Create Database** or select an existing database 3. Create a collection named `streamkap_heartbeat` **Using MongoDB Shell:** ```javascript theme={null} use streamkap db.createCollection("streamkap_heartbeat") ``` Ensure the Streamkap user has `read` access to the heartbeat collection. **Using MongoDB Atlas UI:** 1. Go to **Security > Database Access** 2. Edit the `streamkap_user` 3. Under **Specific Privileges**, add: `read@streamkap.streamkap_heartbeat` **Using MongoDB Shell:** ```javascript theme={null} db.grantRolesToUser("streamkap_user", [ { role: "read", db: "streamkap" } ]) ``` 1. In MongoDB Atlas, go to **App Services** (or **Triggers** in the left menu) 2. Click **Create a Trigger** 3. Select **Scheduled** trigger type 4. Configure the trigger: * **Name**: `streamkap_heartbeat_trigger` * **Schedule Type**: Basic * **Repeat once by**: Minute * **Every**: 1 minute(s) 5. In the **Function** section, select **Function** and create a new function: ```javascript theme={null} exports = async function() { const serviceName = "mongodb-atlas"; // Your cluster service name const database = "streamkap"; const collection = "streamkap_heartbeat"; const coll = context.services .get(serviceName) .db(database) .collection(collection); const result = await coll.updateOne( { _id: "heartbeat" }, { $set: { last_update: new Date() } }, { upsert: true } ); console.log(`Heartbeat updated: ${JSON.stringify(result)}`); return result; }; ``` 6. Click **Save** **Atlas App Services usage and cost** The scheduled trigger counts as an [App Services Request](https://www.mongodb.com/docs/atlas/app-services/billing/#requests). With a 1-minute interval: * \~1,440 requests/day (60/hour × 24 hours) * **Daily free tier**: 50,000 requests—this heartbeat uses only \~3% of the free allowance * **Cost if exceeding free tier**: \$0.000002 per request The heartbeat has minimal impact on your App Services usage quota. MongoDB uses change streams to track changes. While change streams use resume tokens to track position, these tokens can expire or become invalidated—particularly on clusters with high write activity or when using custom aggregation pipelines that filter events. Heartbeats ensure the Connector receives regular change events, keeping resume tokens fresh and providing liveness monitoring. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** Layer 2 is especially important when: * Your database has low or intermittent traffic * You use custom aggregation pipelines that filter out many events * You need reliable liveness monitoring We recommend configuring Layer 2 for all deployments to provide additional resilience. You can configure regular updates to a dedicated heartbeat collection in the source database. This simulates activity, ensuring change events are generated consistently and resume tokens remain valid. Since the MongoDB Connector doesn't write directly to the database, you must configure an external scheduler (e.g., cron job, Kubernetes CronJob) to generate artificial traffic. Connect to your MongoDB instance and create the heartbeat collection: ```javascript theme={null} use streamkap db.createCollection("streamkap_heartbeat") // Insert initial document db.streamkap_heartbeat.insertOne({ _id: "heartbeat", last_update: new Date() }) ``` ```javascript theme={null} db.grantRolesToUser("streamkap_user", [ { role: "read", db: "streamkap" } ]) ``` Create a script that updates the heartbeat document: ```bash theme={null} #!/bin/bash # heartbeat.sh MONGO_URI="mongodb://heartbeat_user:password@localhost:27017/streamkap?authSource=admin" mongosh "$MONGO_URI" --eval ' db.streamkap_heartbeat.updateOne( { _id: "heartbeat" }, { $set: { last_update: new Date() } }, { upsert: true } ) ' ``` Make the script executable: ```bash theme={null} chmod +x heartbeat.sh ``` **Using cron (Linux/macOS):** ```bash theme={null} # Edit crontab crontab -e # Add this line to run every minute * * * * * /path/to/heartbeat.sh >> /var/log/mongodb-heartbeat.log 2>&1 ``` **Using Kubernetes CronJob:** ```yaml theme={null} apiVersion: batch/v1 kind: CronJob metadata: name: mongodb-heartbeat spec: schedule: "* * * * *" # Every minute jobTemplate: spec: template: spec: containers: - name: heartbeat image: mongo:latest command: - mongosh - "mongodb://heartbeat_user:password@mongodb-host:27017/streamkap?authSource=admin" - --eval - | db.streamkap_heartbeat.updateOne( { _id: "heartbeat" }, { $set: { last_update: new Date() } }, { upsert: true } ) restartPolicy: OnFailure ``` * **Basics**: Integers (INT32/64), floats (FLOAT32/64), strings, dates/timestamps. * **Advanced**: Arrays, objects (STRUCT/Tuple), binary (BYTES/base64/hex), decimals, JSON (`STRING/io.debezium.data.Json`). * **Identifiers**: Integer, float, string, document, ObjectId, binary (extended JSON strict mode). * **Unsupported**: Inconsistent nested structures without preprocessing; non-UTF8; oversized BSON (strategies: fail/skip/split in 6.0.9+). Use queries for oplog size; tools like Datadog/New Relic for lag/queue metrics. **Best Practices**: Retain oplog 3–5 days; alert on growth. * Standalone servers unsupported (convert to replica set) * Oplog purging during downtime may lose events * BSON size limits (fail/skip/split) * No transactions in older versions * General: Sharded clusters need careful config; incremental snapshots require stable primary keys (non-strings recommended) Captures deletes as events with before images; supports full records with pre-images. Encrypted connections (SSL), keystore/truststore, authentication; role-based access. * **Oplog Buildup**: Monitor retention; resume from last position. * **Connection Failures**: Verify firewall, SSL, authentication. * **Missing Events**: Check include/exclude lists; resnapshot. * **Streamkap-Specific**: Check logs for resume token issues. **No, CDC cannot capture Views or most virtual database objects.** **Why Views cannot be captured:**\ CDC captures changes by reading the database transaction log (binlog, WAL, oplog, redo log, etc.). Views are query-time computations over base tables—they don't store data or generate transaction log entries. When you query a view, the database engine executes the underlying SELECT statement against the base tables. Since views don't store data, they don't generate transaction log entries. **What cannot be captured:** * **Views**: Virtual collections defined by aggregation pipelines, no physical storage or oplog entries * **System Collections** (system.\*, admin.\*, config.\*): Metadata and internal state, not user data * **Time Series Collections** ([MongoDB 5.0+](https://www.mongodb.com/docs/manual/core/timeseries-collections/)): These use a specialized internal storage format optimized for time-stamped data (IoT sensors, metrics, logs). **Change streams are NOT supported** on time series collections because the bucketing storage engine compresses and reorganizes documents internally, making individual document change tracking incompatible with the oplog-based change stream mechanism. **Solution**: If you need CDC on time series data, store it in regular collections (with appropriate indexes on timestamp fields), or use aggregation pipelines to periodically export data snapshots. * **On-Demand Materialized Views** (`$merge`, `$out` results): Generated data, not original sources **What CAN be captured but with caveats:** * **Capped Collections** ([fixed-size collections](https://www.mongodb.com/docs/manual/core/capped-collections/)): These fixed-size collections automatically overwrite oldest documents when full. **CDC can capture them**, but there's risk of missing events. If your connector is offline or falls behind, and the oplog position it needs to resume from gets overwritten by newer operations, those change events are lost forever. Capped collections are typically used for high-throughput logs where data loss is acceptable. **Recommendation**: For mission-critical data requiring guaranteed capture, use regular collections instead. **Solution:**\ Configure CDC on the underlying base tables that power your views. The view logic can be recreated in your destination or transformation layer. **MongoDB-specific notes:** * **Aggregation pipelines on views**: Capture the source collections and apply the pipeline logic downstream * **Standalone MongoDB servers**: Not supported for CDC at all—must use a replica set (minimum 1 member) * **Time Series Collections** ([MongoDB 5.0+](https://www.mongodb.com/docs/manual/core/timeseries-collections/)): Feature with columnar-like storage for time-stamped data. The internal bucketing mechanism groups documents by time windows and metadata, then compresses them. This storage optimization is incompatible with change streams because individual document inserts/updates/deletes cannot be tracked in the oplog. Change streams require document-level granularity which time series collections don't provide. **Workaround**: Use regular collections with compound indexes on `{metadata_field: 1, timestamp: 1}` for CDC-compatible time series data. **Example:**\ If you have a view `active_users_summary` created from the `users` collection with filters and projections, capture the `users` collection instead, then apply the same aggregation logic in your destination. Usually it's a missing index. When a filtered snapshot starts, MongoDB runs a query to find the highest `_id` matching your filter. If you don't have an index covering both the filter field and `_id`, MongoDB scans a large portion of the collection — which can look stuck and, in current connector versions, may need an offset reset to recover. **What to do:** 1. Check that the right index exists. If you're filtering on `lastupdated`, you need `{ lastupdated: 1, _id: -1 }`. 2. Confirm it's being used: `db..find().sort({_id: -1}).limit(1).explain("queryPlanner")`. The winning plan should show `IXSCAN` with no `SORT` stage. 3. If the index is missing, create it before re-triggering the snapshot. See [Best Practices for Filtered Snapshots](/snapshots#best-practices-for-filtered-snapshots) for full guidance. The MongoDB oplog has been recycled before the connector could process all events. The connector's resume token points to a position that no longer exists in the oplog. **Resolution:** 1. Contact [Streamkap support](mailto:support@streamkap.com) for an offset reset 2. After the offset reset, trigger a [snapshot](/snapshots) to backfill any missed data 3. Increase oplog retention to 48 hours or more to prevent recurrence: ```javascript theme={null} db.adminCommand({ replSetResizeOplog: 1, minRetentionHours: 48 }) ``` The default oplog retention is typically 24 hours, which may be insufficient for production workloads — especially during maintenance windows, connector restarts, or periods of high write volume. **Recommendation:** Set oplog retention to 48 hours or more for production environments: ```javascript theme={null} db.adminCommand({ replSetResizeOplog: 1, minRetentionHours: 48 }) ``` For MongoDB Atlas, configure oplog retention in the cluster settings under **Additional Settings**. * Use replica sets (min 3 members for production) * Enable pre/post-images for full updates * Limit collections to needed ones * Test snapshots in staging Database version upgrades require careful planning to avoid data loss and minimize downtime for your CDC pipelines. See our [Database Upgrade Guide](/database-upgrade-guide) for step-by-step instructions. # Monitoring The PostgreSQL WAL Log Source: https://docs.streamkap.com/monitoring-the-postgresql-wal-log Guide to monitoring the PostgreSQL WAL Log ## What is the PostgreSQL WAL? Write-Ahead Logging (WAL) is how PostgreSQL records changes before applying them to the database. This ensures data durability, consistency, and recovery. WAL also enables Change Data Capture (CDC) for real-time streaming solutions like Streamkap. Starting with PostgreSQL 16, read replicas support **logical replication**, allowing pipelines to run from a replica—depending on your cloud provider. ## How WAL Works WAL continuously generates log files capturing all database changes. These logs help restore transactions after a crash and enable replication. Each WAL file is usually 16MB, and the number generated depends on database activity. Since WAL writes sequentially, it reduces random I/O and improves performance. ## Factors Affecting WAL Growth WAL size increases with database activity, particularly: * **Frequent Transactions**: More inserts, updates, and deletes generate more WAL. * **Retention Policies**: Keeping WAL files for recovery or replication increases storage needs. Streamkap recommends a 3-day retention. * **Publications**: Define which tables and changes are logged for replication. * **Replication Slots**: Ensure WAL is retained for subscribers (e.g., Streamkap). Unused or slow slots cause WAL buildup. * **Replication Delays**: If subscribers lag, WAL accumulates, consuming disk space. As subscribers consume data, WAL size decreases. The `restart_lsn` in `pg_replication_slots` shows how far back WAL is retained per slot. ## Disk Space Management Ensure at least 3 days of WAL storage to handle replication delays. Retaining 5+ days is ideal for troubleshooting issues. ## Determining Disk Space Requirements Use `pg_wal_lsn_diff()` to measure WAL generation over time: ```SQL SQL theme={null} `SELECT pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), '0/00000000'));` ``` Consider these factors when determining storage requirements: * **Transaction Volume**: Analyze typical and peak transaction rates to estimate WAL growth. * **Retention Needs**: Multiply average WAL generation by the desired retention period (e.g., 3 days). * **Auto-Growth Limits**: Configure auto-growth policies but don’t rely solely on them for space management. ## Reducing WAL Growth To prevent excessive WAL accumulation: * Limit publications to essential tables and columns. * Retain at least 3 days of WAL but avoid unnecessary extensions. * Monitor replication slots—inactive slots cause WAL buildup. ```SQL SQL theme={null} -- Monitor slots SELECT * FROM pg_replication_slots; -- Drop Slot SELECT pg_drop_replication_slot('{slot}'); ``` * Ensure you are using `VACUUM` and `ANALYZE` operations regularly ## Essential Metrics for Oversight ### WAL Disk Utilization * **Datadog**: postgresql.wal\_bytes * **New Relic**: Custom query needed, e.g., SELECT pg\_size\_pretty(pg\_wal\_lsn\_diff(pg\_current\_wal\_lsn(), '0/00000000')); * **Grafana**: Use pg\_stat\_wal.wal\_bytes from the pg\_stat\_wal view ### WAL Generation Velocity * **Datadog**: postgresql.wal\_write (to approximate velocity) * **New Relic**: Custom query for rate, e.g., SELECT pg\_size\_pretty(pg\_wal\_lsn\_diff(pg\_current\_wal\_lsn(), '0/00000000')) - pg\_size\_pretty(pg\_wal\_lsn\_diff(pg\_current\_wal\_lsn(), '0/00000000')::pg\_lsn - interval '1 minute') * **Grafana**: Use pg\_stat\_wal.wal\_bytes and calculate the change over time ### Replication Lag * **Datadog**: pg\_stat\_replication.flush\_lag * **New Relic**: Custom query on pg\_stat\_replication, e.g., SELECT sent\_lsn - write\_lsn AS lag FROM pg\_stat\_replication; * **Grafana**: Monitor pg\_stat\_replication.flush\_lag or pg\_stat\_replication.write\_lag from the pg\_stat\_replication view ### Replication Slot Status * **Datadog**: Custom metric needed, query pg\_replication\_slots * **New Relic**: Custom query, e.g., SELECT slot\_name, active, pg\_size\_pretty(pg\_wal\_lsn\_diff(pg\_current\_wal\_lsn(), restart\_lsn)) AS lag FROM pg\_replication\_slots; * **Grafana**: Query pg\_replication\_slots for active, restart\_lsn, etc. ### WAL File Count * **Datadog**: Custom metric based on pg*ls\_dir('pg\_wal'), e.g., SELECT count(*) FROM pg\_ls\_dir('pg\_wal') WHERE pg\_ls\_dir \~ '^\[0-9A-F]\{24}\$'; * **New Relic**: Custom query, e.g., SELECT count(\_) FROM pg\_ls\_dir('pg\_wal') WHERE pg\_ls\_dir \~ '^\[0-9A-F]\{24}\$'; * **Grafana**: Use a query like SELECT count(\*) FROM pg\_ls\_dir('pg\_wal') WHERE pg\_ls\_dir \~ '^\[0-9A-F]\{24}\$'; to get the count of WAL files # Overview Source: https://docs.streamkap.com/motherduck If you don't see your platform listed, please contact us as we're happy to help find and provide the setup documentation you need. # MotherDuck (Native) Source: https://docs.streamkap.com/motherduck-native Stream data to MotherDuck from Streamkap using native jdbc driver # Overview This guide explains how to stream data from a Kafka cluster into the MotherDuck database using native jdbc (duckdb) driver. Since this is a direct Kafka sink connector for MotherDuck, it will ingest the data into your database without S3 as an intermediary. # Prerequisites * MotherDuck Account: A valid MotherDuck account and database setup where the data will be loaded. # Motherduck Setup To set up the Connector, you will need to gather connection details and configure your Motherduck instance. Log in to your [Motherduck Account](https://app.motherduck.com) and then follow the steps below. ## Get database name You will need the database name to configure the connector. You can find it on the left side panel of the Motherduck UI, under the `Attached databases` section. ## Generate an access token For setting the Streamkap MotherDuck Token: 1. Open **Settings** page from the top left menu option 2. Open the **Access Tokens** page, listed under the **INTEGRATIONS** section 3. Click **+ Create token** 4. Enter a **Name** and select **Read/Write Token** option in Token type and keep **Automatically expire this token** to false 5. Click **Create token** 6. Copy the access token # Streamkap Setup 1. Go to [Destinations](https://app.streamkap.com/connectors/add?tab=Destinations) and choose **Motherduck** 2. Input the following information: 1. **Name** - A unique and memorable name for this Connector 2. **Ingestion Mode** (default: `upsert`) - See [Inserts/Upserts](/insertsupserts) for information about ingestion modes 3. **Delete Mode** (default: `none`) - Delete records in destination if deleted at source 4. **Tasks** - If Pipelines for this Destination have lag that's continuously growing over time and not reducing, increase the number of Tasks, otherwise, leave as default 5. **Schema Evolution** (default: `true`) - If enabled, the connector will automatically adapt to changes in the source schema by adding new columns in the target table 6. **Motherduck Token** - The access token you generated earlier 7. **Database** (Case sensitive) - The name of the database 8. **Schema** (Case sensitive - default: `streamkap`) - The schema within the database to write data to 3. Click **Save** # How it works As data's streamed from the source in to topics (think of them as partitioned tables), the Motherduck Sink connector will: * Check whether tables for the topics exist in Motherduck, if not, it creates them * Detect changes between the source data schema and target table schema, and if: * a new column (by name) is found, add to the end of the table * an existing column's data type is changed, add a new column to the end of the table named `_` # MotherDuck (S3) Source: https://docs.streamkap.com/motherduck-s3 Stream data to MotherDuck from Streamkap using S3 as an intermediary ## Overview This guide explains how to stream data from a Kafka cluster into the MotherDuck database using Amazon S3 as an intermediary. Since there is no direct Kafka sink connector for MotherDuck, we'll utilise the existing dedicated S3 connector to first stream data into an S3 bucket. Then, you can configure MotherDuck to read from the S3 bucket to ingest the data into your database. * Kafka to S3: Kafka messages are streamed into an Amazon S3 bucket via an existing dedicated S3 connector. * S3 to MotherDuck: MotherDuck is configured to read the data from the S3 bucket and load it into the database. ## Prerequisites * Amazon S3 Bucket: A bucket in Amazon S3 where data from Kafka will be streamed. * MotherDuck Account: A valid MotherDuck account and database setup where the data will be loaded. * Kafka S3 Connector: Your Kafka to S3 connector configured and running. If you have not yet configured this, please refer to the [Kafka to S3 Streaming Guide](/s3) for detailed instructions. ## MotherDuck Setup Once data is available in the S3 bucket, you can configure MotherDuck to read from the S3 bucket and load it into your database. Follow these steps: ## Configure the S3 Source in MotherDuck To read data from the S3 bucket into MotherDuck, you need to configure a data source that points to the S3 bucket. This involves creating a connection between MotherDuck and your S3 bucket using AWS credentials. 1. Log in to MotherDuck and navigate to your workspace or database. 2. Go to the Secrets. 3. Add new secret and choose Amazon S3 as the secret type. 4. Provide the necessary details to access the S3 bucket: * Secret Name: The name of your source connection details. * Region: The region of your S3 bucket (e.g., us-west-2). * Access Key ID: Your AWS Access Key ID. * Secret Access Key: Your AWS Secret Access Key. ### SQL Command for Secret Configuration Alternatively, you can configure the secret using SQL. Below is an example configuration for setting up the secret: ```SQL SQL theme={null} CREATE SECRET IN MOTHERDUCK ( TYPE S3, KEY_ID 'access_key', SECRET 'secret_key', REGION 'us-east-1' ); ``` ### Verify Existing Secrets To check your existing secrets, you can run the following SQL command: ```SQL SQL theme={null} FROM duckdb_secrets() ``` ## Query Data from the S3 Bucket Once the connection between MotherDuck and your S3 bucket is established, you can define a schema and table in MotherDuck or simply query the data directly from the S3 bucket. Since your Kafka stream might be writing multiple files to the S3 bucket, we recommend using a wildcard `*` to read all files in a folder. This will enable MotherDuck to automatically pick up new files as they are written to the S3 bucket. Here is an example SQL query to read data from your S3 bucket (using a wildcard for streaming): ```SQL SQL theme={null} SELECT key.id, value.name, value.note FROM read_parquet('s3://streamkap-s3-test-bucket/parquet_test/*') ``` # Overview Source: https://docs.streamkap.com/mysql Streamkap supports syncing MySQL in real-time using change data capture for: If you don't see your platform listed, please contact us as we're happy to help find and provide the setup documentation you need. # MySQL (Generic) Source: https://docs.streamkap.com/mysql-generic Connect a self-hosted or generic MySQL server to Streamkap for binlog-based change data capture, including server configuration, user grants, and heartbeats. ## Prerequisites * MySQL version ≥ 5.7 * MySQL [binlog](https://dev.mysql.com/doc/refman/8.0/en/binary-log.html) enabled on the primary server * Connection details including Server ID * A database user with sufficient privileges to configure the database, including enabling binary logging and creating users The following table summarizes all permissions the Streamkap user needs. The setup steps on each source connector page walk through granting each one. **Core permissions (always required):** | Permission | Scope | Purpose | | -------------------- | ------------ | -------------------------------------------------------- | | `REPLICATION CLIENT` | `*.*` | Read binlog metadata and positions | | `REPLICATION SLAVE` | `*.*` | Read binlog events for CDC streaming | | `RELOAD` | `*.*` | Flush operations required for consistent snapshots | | `SHOW DATABASES` | `*.*` | Discover available databases and schemas | | `SELECT` | `{schema}.*` | Read table data during initial and incremental snapshots | **Snapshot signal table (required if GTID is not enabled):** | Permission | Scope | Purpose | | ---------- | ---------------------------- | -------------------------- | | `SELECT` | `streamkap.streamkap_signal` | Read signal table state | | `INSERT` | `streamkap.streamkap_signal` | Trigger snapshot signals | | `UPDATE` | `streamkap.streamkap_signal` | Update signal table state | | `DELETE` | `streamkap.streamkap_signal` | Clean up processed signals | **Heartbeat table (required if heartbeats are enabled):** | Permission | Scope | Purpose | | ---------- | ------------------------------- | ------------------------------ | | `SELECT` | `streamkap.streamkap_heartbeat` | Read heartbeat state | | `INSERT` | `streamkap.streamkap_heartbeat` | Write heartbeat records | | `UPDATE` | `streamkap.streamkap_heartbeat` | Update heartbeat timestamps | | `DELETE` | `streamkap.streamkap_heartbeat` | Clean up old heartbeat records | If GTID is enabled (default on Google Cloud SQL), the signal table is not required. If using **read-only** mode with GTID enabled, the signal table permissions are not needed. The heartbeat table still requires permissions for monitoring and diagnostics. **Combined GRANT statements:** ```sql SQL theme={null} -- Core permissions (always required) GRANT REPLICATION CLIENT, RELOAD, SHOW DATABASES, REPLICATION SLAVE ON *.* TO 'streamkap_user'@'%'; GRANT SELECT ON {schema}.* TO 'streamkap_user'@'%'; -- Signal table (if GTID is not enabled) GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_signal TO 'streamkap_user'@'%'; -- Heartbeat table (if heartbeats are enabled) GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO 'streamkap_user'@'%'; ``` ## MySQL Setup ### 1. Grant Database Access * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your database. ### 2. Configure Binary Logging Binary logging records all changes to your database tables. The Connector relies on MySQL's implementation of this. Open a connection to your MySQL database's server. Access your MySQL server configuration file (usually `/etc/my.cnf`). Ensure that this file has the following lines in the `[mysqld]` section. These lines enable ROW format binary log replication, which the Connector needs to perform incremental updates. * Enable binlog * Set Server-ID if not set already * Set minimum 3 days for log expiry ```ini MySQL Configuration theme={null} [mysqld] binlog-format=ROW log-bin=mysql-binlog server-id=123456789 expire-logs-days=3 log-slave-updates=1 ``` If your configuration already has a `server-id` entry, you don't need to change it. Otherwise, choose any number between 1 and 4294967295 as the server-id. * Restart your MySQL server to effect these changes. ### 3. Verify Binary Logs Are Enabled You can verify using any of these methods: * Run the following SQL query on the DB instance `SHOW VARIABLES LIKE '%log_bin%';`. Result should be `ON` * Run `SHOW BINARY LOGS` If you upgrade your database version to 8.0.23 or above, please set the parameter `binlog_row_metadata` value to `MINIMAL` before upgrading. Failing to do this may result in your MySQL connectors failing. ### 4. Create Database User It's recommended to create a separate user and role for the Connector to access your MySQL database. Below is an example script that does that. ```SQL SQL theme={null} -- Replace { ... } placeholders as required -- Create user CREATE USER 'streamkap_user'@'%' IDENTIFIED WITH mysql_native_password BY '{password}'; -- Grant Permissions GRANT REPLICATION CLIENT, RELOAD, SHOW DATABASES, REPLICATION SLAVE ON *.* TO 'streamkap_user'@'%'; -- Grant Select on all schemas needed GRANT SELECT ON {schema}.* TO 'streamkap_user'@'%'; ``` ### 5. Enable Snapshots To backfill your data, the Connector needs to be able to perform snapshots. See [Snapshots & Backfilling](/snapshots) for more information. To enable this feature, there are 2 methods available: #### Method 1: Enable GTID (Recommended) This method is recommended if you cannot create and grant Streamkap read/write privileges on a 'signal' table (method 2) for any reason. It's the equivalent of a 'read only' connection. GTIDs only available in MySQL version ≥ 5.6.5 Global transaction identifiers (GTIDs) uniquely identify transactions that occur on a server within a cluster. Though not required, using GTIDs simplifies replication and enables you to more easily confirm if primary and replica servers are consistent as well as carry out incremental snapshots. Set up following these instructions. Ensure you follow the guide for your version [Replication GTIDs - How To](https://dev.mysql.com/doc/refman/8.0/en/replication-gtids-howto.html) and that `GTID` mode is `ON`. #### Method 2: Create a table in the source database If you cannot enable GTID mode, you will need to create the table and give permissions to the `streamkap_user`. The Connector will use this collection for managing snapshots. The examples below use `streamkap_signal` as the signal table name, but you can choose any name. During [Streamkap Setup](#3-snapshot-settings), provide the full path to your signal table in `database.table` format (e.g., `streamkap.streamkap_signal`). ```SQL SQL theme={null} -- Create the schema CREATE SCHEMA streamkap; CREATE TABLE streamkap_signal ( id VARCHAR(255) PRIMARY KEY, type VARCHAR(32) NOT NULL, data VARCHAR(2000) NULL ); GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_signal TO 'streamkap_user'@'%'; ``` ### 6. Heartbeats Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. For read-write connections (when **Read only** is **No** during Streamkap Setup), the Connector writes to the heartbeat table directly. ```SQL SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Switch to the streamkap schema USE streamkap; -- Create the heartbeat table with id, text, and last_update fields CREATE TABLE streamkap_heartbeat ( id INT AUTO_INCREMENT PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Grant permission to the Streamkap user GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO 'streamkap_user'@'%'; -- Insert the first row into the heartbeat table INSERT INTO streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` For read-only connections (when **Read only** is **Yes** during Streamkap Setup), the Connector cannot write to the heartbeat table directly. Instead, you must configure a scheduled job on the **primary** database to generate artificial traffic. These changes will replicate to the read replica, which the Connector then consumes. **Run these commands on the primary database, not the read replica.** The heartbeat table and scheduled event must be created on the primary database. The changes will automatically replicate to the read replica. The MySQL Event Scheduler flag must be enabled on your database (`event_scheduler=ON`). See your provider's documentation: Modifying parameters in a DB parameter group in Amazon RDS Aurora MySQL configuration parameters reference Configuring database flags Server parameters Check if the event scheduler is enabled: ```sql SQL theme={null} SHOW VARIABLES WHERE VARIABLE_NAME = 'event_scheduler'; ``` **Making this setting persistent** For self-hosted MySQL, add `event_scheduler = ON` to your MySQL configuration file (usually `my.cnf` or `my.ini`) in the `[mysqld]` section. For managed databases (RDS, Cloud SQL, Azure), configure the `event_scheduler` parameter through your provider's console or CLI. ```sql SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Create the heartbeat table CREATE TABLE streamkap.streamkap_heartbeat ( id INT AUTO_INCREMENT PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Insert the initial row INSERT INTO streamkap.streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` ```sql SQL theme={null} CREATE EVENT streamkap.streamkap_heartbeat_event ON SCHEDULE EVERY 1 MINUTE DO UPDATE streamkap.streamkap_heartbeat SET text = 'updated_heartbeat', last_update = CURRENT_TIMESTAMP WHERE id = 1; ``` Whichever database user is used to create and run the event scheduler (often the MySQL root user or a dedicated event scheduler user) needs appropriate permissions on the heartbeat table. Additionally, the Streamkap user also needs permissions to monitor the heartbeat table. ```sql SQL theme={null} GRANT EVENT ON streamkap.* TO {event scheduler user}; GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO {event scheduler user}; -- Grant permissions to the Streamkap user for monitoring and diagnostics GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO 'streamkap_user'@'%'; ``` ```sql SQL theme={null} -- View all scheduled events in the streamkap schema SHOW EVENTS IN streamkap; -- View event details SELECT * FROM information_schema.EVENTS WHERE EVENT_SCHEMA = 'streamkap'; -- Disable an event temporarily ALTER EVENT streamkap.streamkap_heartbeat_event DISABLE; -- Enable an event ALTER EVENT streamkap.streamkap_heartbeat_event ENABLE; -- Drop an event DROP EVENT IF EXISTS streamkap.streamkap_heartbeat_event; ``` *** ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **MySQL**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Hostname**: Specify the hostname. * **Port**: Default is `3306`. * **Connect via SSH Tunnel**: The Connector will connect to an SSH server in your network which has access to your database. This is necessary if the Connector cannot connect directly to your database. * See [SSH Tunnel](/ssh-tunnel) for setup instructions. * **Username**: Username to access the database. By default, Streamkap scripts use `streamkap_user`. * **Password**: Password to access the database. * **Read only**: Whether or not to use a read-only connection. Requires GTID to be enabled on the source database. See [Enable GTID](#method-1%3A-enable-gtid-recommended) for more information. * **Heartbeats**: Enabled by default. * For **read-write** connections, configure a heartbeat table in the source database and set **Heartbeat Table Database**. See [Heartbeats](#6-heartbeats) for setup instructions. * For **read-only** connections, configure a scheduled heartbeat event on the primary database using the MySQL Event Scheduler, and include the heartbeat table in [Schema and Table Capture](#5-schema-and-table-capture). See [Heartbeats](#6-heartbeats) for setup instructions. * **Connection Timezone**: The timezone of your database. **Timezone conversion** MySQL converts `TIMESTAMP` values from the current time zone to UTC for storage, and back from UTC to the current time zone for retrieval. By default, the current time zone for each connection is the database server's time zone but this option allows you to override that. As long as the time zones remain the same, you get back the same value you store. We recommend using the default `SERVER` option which attempts to detect the session time zone from the values configured on the MySQL server session variables 'time\_zone' or 'system\_time\_zone'. It also reduces the chance of problems with daylight savings adjustment 'fall back' and 'spring forward'. If either time zones change, an [ad-hoc snapshot](/snapshots#triggering-a-snapshot) is recommended so your source and destination timestamps are consistent. ### 3. Snapshot Settings If you set **Read only** to **No**, you will need to create a snapshot signal table and give permissions to the `streamkap_user`. See [Enable Snapshots](#5-enable-snapshots) for setup instructions. * **Signal Table**: Full path to the signal table including database and table name (e.g., `streamkap.streamkap_signal`). This table is used for incremental snapshotting. See [Enable Snapshots](#5-enable-snapshots) for setup instructions. ### 4. Advanced Parameters * **Represent binary data as**: Specifies how the data for binary columns e.g. `blob`, `binary`, `varbinary` should be interpreted. Your destination for this data can impact which option you choose. Default is `bytes`. * **Capture Only Captured Databases DDL**: Specifies whether the connector records schema structures from all logical databases in the database instance or only captured databases. Enabling this when you have many databases in your instance can improve performance and avoid timeouts. Default is `false`. See [Schema History Optimization](/schema-history-optimization) for details. * **Capture Only Captured Tables DDL**: Specifies whether the connector records schema structures from all logical tables in the captured schemas or databases, or only captured tables. Enabling this when you have many tables can improve performance and avoid timeouts. Default is `false`. See [Schema History Optimization](/schema-history-optimization) for details. Click **Next**. ### 5. Schema and Table Capture * **Add Schemas/Tables**: Specify the schema(s) and table(s) for capture. * You can bulk upload here. The format is a simple list of schemas and tables, with each entry on a new row. Save as a `.csv` file without a header. **Adding tables programmatically** -- You can add tables to an existing source via the API or Terraform using the `table.include.list.user.defined` parameter (Terraform: `table_include_list_user_defined`). See the [REST API](/api) for getting started with the API, or [Terraform Resources](/terraform-resources#sources) for Terraform source configuration. **CDC only captures base tables, not Views** Change Data Capture reads the MySQL binary log (binlog), which only records changes to physical tables. Database Views are query-time computations with no physical storage—they don't generate binlog entries. **What you cannot capture:** Views, temporary tables, MEMORY tables, BLACKHOLE tables, or system tables (information\_schema, performance\_schema). **Solution:** Specify only the underlying base tables that feed your views. You can recreate the view logic in your destination or transformation layer. **VIRTUAL generated columns are not streamed** MySQL [VIRTUAL generated columns](https://dev.mysql.com/doc/refman/8.0/en/create-table-generated-columns.html) are computed when rows are read and are **not** written to the binary log, so live streaming cannot capture or update their values. (STORED generated columns are written to the binary log and stream normally.) Snapshots read the table directly, so they **do** capture a value for VIRTUAL columns — but because streaming cannot keep it current, that value will become stale. **We recommend excluding VIRTUAL generated columns from capture:** in the Source's **Settings → Advanced** section, set **Column Selection Mode** to **Exclusion** and add the column to the **Column Exclusion List** (fully-qualified, e.g. `database.table.column`). If you need the value at your destination, calculate it there — or with a [transform](/transforms) — from the columns it is derived from. Click **Save**. **Have questions?** See the [MySQL Source FAQ](/mysql-source-faq) for answers to common questions about MySQL sources, troubleshooting, and best practices. # MySQL Low Volume Log Rotation Source: https://docs.streamkap.com/mysql-low-volume-log-rotation Set log rotation for low volume databases It is possible in low write databases; such as development or test databases; that log files will not fill up to rotate before the log retention setting. This will break the pipeline. For these databases, please execute the following to rotate the logs every 24 hours. ```SQL SQL theme={null} -- Execute the following SQL statement to turn on the event scheduler. SET GLOBAL event_scheduler = ON; -- Create a custom event that rotates (flushes) the binary logs every 24 hours DROP EVENT IF EXISTS rotate_binlog_24_hrs_event; CREATE EVENT streamkap_rotate_binlog_24_hrs_event ON SCHEDULE EVERY 24 hour DO FLUSH BINARY LOGS; ``` # MySQL Source FAQ Source: https://docs.streamkap.com/mysql-source-faq Answers to common MySQL source connector questions in Streamkap, covering binlog retention, schema changes, generated columns, and troubleshooting. This FAQ focuses on using MySQL as a source in streamkap, including general self-hosted setups and cloud variants (AWS RDS/Aurora, Google Cloud SQL, Azure MySQL Database). Streamkap's MySQL connector provides real-time CDC with managed features like automatic scaling, UI setup, and ETL transformations. A MySQL source in streamkap enables real-time Change Data Capture (CDC) from MySQL databases, capturing row-level inserts, updates, and deletes with sub-second latency. It uses the binary log (binlog) to stream changes to destinations, supporting snapshots for initial loads, schema evolution, and handling for schema changes. Streamkap offers a serverless setup via UI or API. **What MySQL versions are supported as sources?** Streamkap: MySQL 5.7+ for basic CDC; 8.0+ for advanced features like GTIDs and query log events; 8.4+ for latest enhancements. Compatible with MySQL 5.6.5+ in limited modes. Streamkap supports: * Self-hosted/Generic (on-prem/VM) * AWS RDS MySQL (including Aurora) * Google Cloud SQL for MySQL * Azure MySQL Database * Any other MySQL compatible platform Streamkap also supports standalone, primary/replica, high availability clusters, and multi-primary topologies. * **CDC**: Binlog-based for inserts/updates/deletes; captures schema changes * **Snapshots**: Ad-hoc/initial using global/table locks; incremental or blocking * **Schema Evolution**: Automatic DDL tracking; emits change events * **Data Types**: Standard mappings (numerics, temporals, strings, binary configurable, JSON, ENUM/SET); temporal/decimal/binary handling modes * **Ingestion Modes**: Inserts (append) or upserts * **Security**: SSL, authentication * **Monitoring**: Latency, lag, binlog metrics * **Heartbeats**: For low-traffic databases to advance binlog position via a heartbeat table * Streamkap adds transaction metadata, row filtering, and original SQL queries if enabled Streamkap reads MySQL's binary log (binlog) to capture committed operations, emitting changes as events. Requires `binlog_format=ROW` and `binlog_row_image=FULL`.\ Uses GTID mode for position tracking in multi-primary setups, recommended for reliability. `binlog_row_image` controls logged data for changes: * **MINIMAL**: Logs PK only (partial deletes) * **FULL**: Logs all columns (complete records, recommended) Set to **FULL** for full delete handling. Trigger ad-hoc at source/table level. * Methods: Global read lock or table-level; incremental (chunked by PK, default 1024 rows) or blocking * Modes: `initial` (default), `always`, `initial_only`, `no_data`, `when_needed`, `configuration_based`, `custom` Streamkap simplifies triggering via UI. * **Basics**: Booleans (BOOLEAN), integers (INT8/16/32/64), floats (FLOAT32/64), strings (STRING), dates/timestamps (adaptive/connect modes) * **Advanced**: Binary (BYTES/base64/hex), decimals (precise/double/string modes), JSON (`STRING/io.debezium.data.Json`), ENUM/SET (`STRING/io.debezium.data.Enum/Set`) * **Character**: CHAR/VARCHAR/TEXT (STRING) * **Unsupported**: Non-UTF8; spatial (GEOMETRY); MyISAM tables; binlog expiration losses Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. For read-write connections (when **Read only** is **No** during Streamkap Setup), the Connector writes to the heartbeat table directly. ```SQL SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Switch to the streamkap schema USE streamkap; -- Create the heartbeat table with id, text, and last_update fields CREATE TABLE streamkap_heartbeat ( id INT AUTO_INCREMENT PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Grant permission to the Streamkap user GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO 'streamkap_user'@'%'; -- Insert the first row into the heartbeat table INSERT INTO streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` Use queries like `SHOW BINARY LOGS` for size/position; tools like Datadog/New Relic for lag. **Best Practices**: Retain 3–5 days; alert on expiration/growth. * MyISAM unsupported * Binlog expiration can lose events * PK changes require manual handling * TOASTed equivalents incomplete without FULL image * High binlog growth without monitoring * Schema changes during snapshots unsupported * Read replicas for snapshots in some cloud setups * VIRTUAL generated columns not streamed (STORED columns stream normally) * Captures deletes as events with before images if `binlog_row_image=FULL` * Supports soft deletes Encrypted connections (SSL), IP allowlisting, role-based access (dedicated user recommended). The following table summarizes all permissions the Streamkap user needs. The setup steps on each source connector page walk through granting each one. **Core permissions (always required):** | Permission | Scope | Purpose | | -------------------- | ------------ | -------------------------------------------------------- | | `REPLICATION CLIENT` | `*.*` | Read binlog metadata and positions | | `REPLICATION SLAVE` | `*.*` | Read binlog events for CDC streaming | | `RELOAD` | `*.*` | Flush operations required for consistent snapshots | | `SHOW DATABASES` | `*.*` | Discover available databases and schemas | | `SELECT` | `{schema}.*` | Read table data during initial and incremental snapshots | **Snapshot signal table (required if GTID is not enabled):** | Permission | Scope | Purpose | | ---------- | ---------------------------- | -------------------------- | | `SELECT` | `streamkap.streamkap_signal` | Read signal table state | | `INSERT` | `streamkap.streamkap_signal` | Trigger snapshot signals | | `UPDATE` | `streamkap.streamkap_signal` | Update signal table state | | `DELETE` | `streamkap.streamkap_signal` | Clean up processed signals | **Heartbeat table (required if heartbeats are enabled):** | Permission | Scope | Purpose | | ---------- | ------------------------------- | ------------------------------ | | `SELECT` | `streamkap.streamkap_heartbeat` | Read heartbeat state | | `INSERT` | `streamkap.streamkap_heartbeat` | Write heartbeat records | | `UPDATE` | `streamkap.streamkap_heartbeat` | Update heartbeat timestamps | | `DELETE` | `streamkap.streamkap_heartbeat` | Clean up old heartbeat records | If GTID is enabled (default on Google Cloud SQL), the signal table is not required. If using **read-only** mode with GTID enabled, the signal table permissions are not needed. The heartbeat table still requires permissions for monitoring and diagnostics. **Combined GRANT statements:** ```sql SQL theme={null} -- Core permissions (always required) GRANT REPLICATION CLIENT, RELOAD, SHOW DATABASES, REPLICATION SLAVE ON *.* TO 'streamkap_user'@'%'; GRANT SELECT ON {schema}.* TO 'streamkap_user'@'%'; -- Signal table (if GTID is not enabled) GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_signal TO 'streamkap_user'@'%'; -- Heartbeat table (if heartbeats are enabled) GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO 'streamkap_user'@'%'; ``` * **Binlog Buildup**: Enable heartbeats; monitor retention; drop unused logs * **Data Loss on Upgrade**: Stop writes, capture all events, recreate positions post-upgrade, resnapshot * **Missing Events**: Ensure binlog enabled and tables included; check REPLICATION privileges **No, CDC cannot capture Views or most virtual database objects.** **Why Views cannot be captured:**\ CDC captures changes by reading the database transaction log (binlog, WAL, oplog, redo log, etc.). Views are query-time computations over base tables—they don't store data or generate transaction log entries. When you query a view, the database engine executes the underlying SELECT statement against the base tables. Since views don't store data, they don't generate transaction log entries. **What cannot be captured:** * **Views**: Virtual tables with no physical storage or binlog entries * **Temporary Tables**: Session-scoped, not logged persistently in binlog * **MEMORY Tables**: In-memory storage engine, data lost on restart, not in binlog * **BLACKHOLE Tables**: Discard all writes, no data to capture * **System Tables** (information\_schema, performance\_schema, mysql): Metadata representations, not user data * **CTEs (Common Table Expressions)**: Query-time constructs with no persistent storage **Solution:**\ Configure CDC on the underlying base tables that power your views. The view logic can be recreated in your destination or transformation layer. **MySQL-specific notes:** * **MyISAM tables**: Not supported for CDC as they don't participate in transactions or generate complete binlog entries. Convert to InnoDB. * Views created with `ALGORITHM=TEMPTABLE`: Still not capturable; capture the base tables instead **Example:**\ If you have a view `customer_orders_summary` that queries tables `customers` and `orders`, capture the `customers` and `orders` tables instead, then recreate the view logic downstream. **STORED** generated columns are written to the binary log and are captured normally during both streaming and snapshots. **VIRTUAL** generated columns are computed on read and are **not** written to the binary log, so streaming cannot capture or keep them up to date. Snapshots read the table directly and will capture a value for VIRTUAL columns, but streaming cannot update it afterwards, so it becomes stale. We recommend **excluding VIRTUAL generated columns from capture** — in the Source's **Settings → Advanced** section, set **Column Selection Mode** to **Exclusion** and add the column to the **Column Exclusion List** — and recomputing the value at your destination or in a [transform](/transforms) if needed. For large database instances with many databases or tables, schema history can impact performance. Learn about optimization settings that can reduce schema history topic size and improve connector startup times. See the [Schema History Optimization](/schema-history-optimization) guide for detailed information on when and how to use these settings. MySQL does not log `ON DELETE CASCADE` events to the binlog. InnoDB handles foreign key cascades internally, so child table DELETE events from cascading foreign keys are NOT captured by CDC — only the parent table delete is propagated. This is a MySQL limitation, not a Streamkap limitation. If you rely on cascading deletes, consider using application-level deletes instead of database-level cascades. See [Delete Handling](/insertsupserts#delete-handling) for more details. This error typically affects databases with 1000+ tables when schema history optimization is enabled. The schema history may become incomplete. **Resolution:** 1. Contact [Streamkap support](mailto:support@streamkap.com) for schema history recovery 2. Consider enabling **Capture Only Captured Tables DDL** in the source's Advanced settings to prevent recurrence 3. See [Schema History Optimization](/schema-history-optimization) for more information * Use dedicated replication user (not root) * Enable GTID mode for better failover * Limit databases/tables to reduce binlog size * Enable auto-vacuum equivalents; set binlog retention to 3-5 days * For deletes, use FULL `binlog_row_image` * Test snapshots in staging; monitor lag via tools * For cloud: Use provider monitoring; enable heartbeats for low traffic Database version upgrades require careful planning to avoid data loss and minimize downtime for your CDC pipelines. See our [Database Upgrade Guide](/database-upgrade-guide) for step-by-step instructions. # Neon Source: https://docs.streamkap.com/neon-destination Stream data into Neon PostgreSQL # Prerequisites * Connection details * Streamkap user and role # Granting Privileges It's recommended to create a separate user and role for Streamkap to access your PostgreSQL database. Below is an example script that does that. ```SQL SQL theme={null} -- Connect to the PostgreSQL server as admin -- Create the Streamkap User CREATE USER STREAMKAP_USER WITH PASSWORD 'user_password'; -- Create the Streamkap schema and grant privileges CREATE SCHEMA STREAMKAP; GRANT USAGE, CREATE ON SCHEMA STREAMKAP TO STREAMKAP_USER; -- Grant specific privileges on tables within the schema GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA STREAMKAP TO STREAMKAP_USER; ``` # Streamkap Setup 1. Go to [Destinations]() and choose **Neon** 2. Input the following information: * **Name** - A unique and memorable name for this Connector * **Hostname** - The hostname connection string without the port. * **Port** - The port number of the connection * **Username** (Case sensitive) - `STREAMKAP_USER` OR the username you chose * **Password** - The password for your username * **Database Name** (Case sensitive) - `STREAMKAPDB` OR the database name you chose * **Schema Name** (Case sensitive) - `STREAMKAP` OR the schema name you chose * **Insert Mode** Insert or Upserts (i.e. append only or replace) * **Delete Mode** - Delete records in destination if deleted at source * **Schema Evolution** - Handle additional columns automatically? * **Tasks** - Amount of parallelism in writing events. 3. Click **Save** # How this Connector Works The PostgreSQL connector supports idempotent write operations by using upsert semantics and basic schema evolution. The following features are supported: * At-least-once delivery * Delete mode * Idempotent writes (Insert/Upsert mode) * Schema evolution ## At-least-once delivery The PostgreSQL connector guarantees that events that is consumes are processed at least once. ## Delete mode The PostreSQL connector can delete rows in the destination database when a DELETE or tombstone event is consumed. ## Idempotent writes The PostgreSQL connector supports idempotent writes, allowing the same records to be replayed repeatedly and the final database state to remain consistent. In order to support idempotent writes, the PostgreSQL connector must be set to Upsert mode. An upsert operation is applied as either an update or an insert, depending on whether the specified primary key already exists. If the primary key value already exists, the operation updates values in the row. If the specified primary key value doesn’t exist, an insert adds a new row. ## Schema evolution The PostgreSQL connector supports schema evolution The connector automatically detects fields that are in the event payload but that do not exist in the destination table. The connector alters the destination table to add the new fields. When schema evolution is set to Yes, the connector automatically creates or alters the destination database table according to the structure of the incoming event. When an event is received from a topic for the first time, and the destination table does not yet exist, the PostgreSQL connector uses the event’s key, or the schema structure of the record to resolve the column structure of the table. If schema evolution is enabled, the connector prepares and executes a CREATE TABLE SQL statement before it applies the DML event to the destination table. When the PostgreSQL connector receives an event from a topic, if the schema structure of the record differs from the schema structure of the destination table, the connector uses either the event's key or its schema structure to identify which columns are new, and must be added to the database table. If schema evolution is enabled, the connector prepares and executes an ALTER TABLE SQL statement before it applies the DML event to the destination table. Because changing column data types, dropping columns, and adjusting primary keys can be considered dangerous operations, the connector is prohibited from performing these operations. ## Related Documentation * [Ingestion Modes](/insertsupserts) - Understand insert vs. upsert ingestion modes * [Primary Key Modes](/insertsupserts#primary-key-modes) - Configure how primary keys are determined for destination tables * [Delete Handling](/insertsupserts#delete-handling) - Configure how delete events are processed at the destination * [Error Reference](/error-reference) - Common error codes and resolution steps # Neon PostgreSQL Source: https://docs.streamkap.com/neon-postgresql-cdc Set up PostgreSQL change data capture from a Neon serverless database to Streamkap, including logical replication, roles, and heartbeat options. ## Prerequisites * PostgreSQL version ≥ 10 * A database user with sufficient privileges to configure the database, including enabling logical replication and creating users ## PostgreSQL Setup ### 1. Grant Database Access * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your database. ### 2. Create Database User It's recommended to create a separate user and role for Streamkap to access your PostgreSQL database. Below is an example script that does that. ```SQl SQl theme={null} -- Replace { ... } placeholders as required CREATE USER streamkap_user PASSWORD '{password}'; -- Create a role for Streamkap CREATE ROLE streamkap_role nologin PASSWORD '{password}'; GRANT streamkap_role TO streamkap_user; -- Grant Streamkap permissions on the database, schema and all tables to capture GRANT CONNECT ON DATABASE "{database}" TO streamkap_role; GRANT CREATE, USAGE ON SCHEMA "{schema}" TO streamkap_role; GRANT SELECT ON ALL TABLES IN SCHEMA "{schema}" TO streamkap_role; ALTER DEFAULT PRIVILEGES IN SCHEMA "{schema}" GRANT SELECT ON TABLES TO streamkap_role; -- Grant replication role to the user ALTER USER streamkap_user WITH REPLICATION; ``` ### 3. Enable Snapshots To backfill your data, the Connector needs to be able to perform snapshots. See [Snapshots & Backfilling](/snapshots) for more information. To enable this feature, there are 2 methods available: #### Method 1: Enable read only connection Requires PostgreSQL version 13 or higher. This method is recommended if you cannot create a table in the source database and grant the Connector read/write privileges to that. * Set **Read only** to **Yes** during Streamkap Setup. No other configuration should be necessary. #### Method 2: Create a table in the source database Not supported on read replicas. Please use method 1 instead. You will need to create the table and give necessary permissions to the `streamkap_user`. The Connector will use this collection for managing snapshots. Below is an example script that does that. The examples below use `streamkap_signal` as the signal table name, but you can choose any name. During [Streamkap Setup](#3-snapshot-settings), provide the full path to your signal table in `schema.table` format (e.g., `streamkap.streamkap_signal`). ```SQL SQL theme={null} -- Create the schema CREATE SCHEMA streamkap; -- Switch to the newly created schema SET search_path TO streamkap; -- Create the table CREATE TABLE streamkap_signal ( id VARCHAR(255) PRIMARY KEY, type VARCHAR(32) NOT NULL, data VARCHAR(2000) NULL ); -- Grant necessary privileges on the table to the role GRANT CREATE, USAGE ON SCHEMA streamkap TO streamkap_role; GRANT SELECT ON ALL TABLES IN SCHEMA streamkap TO streamkap_role; GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE streamkap_signal TO streamkap_role; ``` **Publications and signal tables** When you create the PostgreSQL publication in the next step, if you choose to specify tables for capture instead of all tables, you **must** include your signal table. ### 4. Heartbeats Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. For read-write connections (when **Read only** is **No** during Streamkap Setup), the Connector writes to the heartbeat table directly. ```SQL SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Switch to the streamkap schema SET search_path TO streamkap; -- Create the heartbeat table with id, text, and last_update fields CREATE TABLE streamkap_heartbeat ( id SERIAL PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Grant permission to the Streamkap user GRANT USAGE ON SCHEMA streamkap TO streamkap_user; GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE streamkap_heartbeat TO streamkap_user; -- Insert the first row into the heartbeat table INSERT INTO streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` **Heartbeat tables and PostgreSQL publications** If the `streamkap_pub` publication created during PostgreSQL Setup was for specific tables e.g. `CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, table3, ...;` instead of `FOR ALL TABLES;`, you **must** add the heartbeat table to the publication: `ALTER PUBLICATION streamkap_pub ADD TABLE streamkap.streamkap_heartbeat;`. For read-only connections (when **Read only** is **Yes** during Streamkap Setup), the Connector cannot write to the heartbeat table directly. Instead, you must configure a scheduled job on the **primary** database to generate artificial traffic. These changes will replicate to the read replica, which the Connector then consumes. **Run these commands on the primary database, not the read replica.** The heartbeat table and scheduled job must be created on the primary database. The changes will automatically replicate to the read replica. The `pg_cron` extension must be allowed in your database's configuration. See your provider's documentation for enabling extensions: Scheduling maintenance with pg\_cron Scheduling maintenance with pg\_cron PostgreSQL extensions PostgreSQL extensions pg\_cron extension pg\_cron extension Supported extensions Once allowed, create the extension: ```sql SQL theme={null} CREATE EXTENSION IF NOT EXISTS pg_cron; ``` ```sql SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Create the heartbeat table CREATE TABLE streamkap.streamkap_heartbeat ( id SERIAL PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Insert the initial row INSERT INTO streamkap.streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` ```sql SQL theme={null} -- Schedule a job to update the heartbeat table every minute SELECT cron.schedule( 'streamkap_heartbeat_job', '*/1 * * * *', $$UPDATE streamkap.streamkap_heartbeat SET text = 'updated_heartbeat', last_update = CURRENT_TIMESTAMP WHERE id = 1;$$ ); ``` Whichever database user is used to create and run the cron jobs (often the `postgres` user or a dedicated cron user) needs appropriate permissions on the heartbeat table. Additionally, the Streamkap user also needs permissions to monitor the heartbeat table. ```sql SQL theme={null} GRANT USAGE ON SCHEMA streamkap TO {cron user}; GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO {cron user}; -- Grant permissions to the Streamkap user for monitoring and diagnostics GRANT USAGE ON SCHEMA streamkap TO streamkap_user; GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO streamkap_user; ``` The `pg_cron` extension stores job execution history in the table `cron.job_run_details`. To prevent this table from growing indefinitely: ```sql SQL theme={null} SELECT cron.schedule( 'streamkap_cron_cleanup', '0 0 * * *', $$DELETE FROM cron.job_run_details WHERE end_time < now() - interval '7 days';$$ ); ``` ```sql SQL theme={null} -- View all scheduled jobs SELECT * FROM cron.job; -- View recent job execution history SELECT * FROM cron.job_run_details ORDER BY start_time DESC LIMIT 10; -- Unschedule a job (replace {jobid} with the actual job ID) SELECT cron.unschedule({jobid}); ``` **Heartbeat tables and PostgreSQL publications** If the `streamkap_pub` publication was created for specific tables (e.g., `CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, ...;`) instead of `FOR ALL TABLES`, you **must** add the heartbeat table to the publication: ```sql SQL theme={null} ALTER PUBLICATION streamkap_pub ADD TABLE streamkap.streamkap_heartbeat; ``` **Logical-message heartbeats** are an alternative to a heartbeat table. The Connector itself calls `pg_logical_emit_message()` on each beat, which appends a transactional record to the write-ahead log (WAL). PostgreSQL treats that record like any other committed change, so the replication slot advances and WAL files are released—without any new table, scheduled job, or write privileges on the source database. **When to choose this over a heartbeat table** Use **logical-message heartbeats** when you want zero footprint in the source database itself—no schemas, tables, scheduled jobs, or write grants to maintain. Use a **heartbeat table** when you want the heartbeat to be visible as an ordinary row that operators can `SELECT` to confirm liveness, or when the requirements below rule logical-message out. **Requirements** * **Streamkap must be connected directly to a primary**, not to a read replica or other hot standby. The Connector executes `pg_logical_emit_message()` against the database it's connected to, and that function writes to the WAL—hot standbys are read-only and will reject the call with an error like `cannot execute ... during recovery`. If your Streamkap source is configured against a replica, use the **Read-only connections** tab instead. * PostgreSQL **14 or later** on the primary. Earlier versions are not supported because the built-in `pgoutput` replication plugin only began streaming logical-decoding messages to the Connector in PostgreSQL 14. On PostgreSQL 13 and earlier, `pg_logical_emit_message()` still appends records to the WAL, but the Connector cannot read them and the replication slot will not advance. * The Streamkap user needs `SELECT` privileges only—no `INSERT`, `UPDATE`, or `DELETE` on any heartbeat table. * Logical replication must already be enabled for the Connector itself (this is a prerequisite for any PostgreSQL source, regardless of heartbeat mode). During [Connection Settings](#2-connection-settings): * Set **Heartbeats** to **Yes**. * Leave **Heartbeat Table Schema** blank. * Set **Use Logical-Message Heartbeat** to **Yes**. No further setup is required in the source database. The Connector will issue the heartbeat query once per minute using the standard Streamkap heartbeat interval. After enabling, you can confirm that the replication slot is advancing against an otherwise idle database. Run on the primary: ```sql SQL theme={null} SELECT slot_name, confirmed_flush_lsn, restart_lsn FROM pg_replication_slots WHERE slot_name = 'streamkap_pgoutput_slot'; ``` Re-run the query after one or two minutes. Both `confirmed_flush_lsn` and `restart_lsn` should move forward. **What the Connector sends** When this mode is enabled, the Connector issues the following query on each beat: ```sql SQL theme={null} SELECT pg_logical_emit_message(true, '__debezium-heartbeat', now()::varchar); ``` The first argument (`true`) makes the emit **transactional**. Non-transactional emits advance the replication slot's `confirmed_flush_lsn` but do not recompute global WAL retention across all slots, so WAL files can still accumulate on an idle source database—transactional emits avoid that. The transactional cost is negligible (one extra transaction ID per beat, well below PostgreSQL's wraparound horizon at the default one-minute interval). The heartbeat record is filtered out before reaching Kafka, so it does not appear as a record in any topic in your pipeline. ### 5. Create Publication & Slot Publications contain a set of change events for the tables you want the Connector to capture. * Create a publication for your tables. You can create a publication for all tables or selected tables. ```SQL SQL - All Tables theme={null} -- Create a publication for all tables to capture CREATE PUBLICATION streamkap_pub FOR ALL TABLES WITH (publish_via_partition_root = true); ``` ```SQL SQL - Selected Tables theme={null} -- Create a publication for specific tables to capture CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, table3, ... WITH (publish_via_partition_root = true); -- Verify the tables to capture were added to the publication SELECT * FROM pg_publication_tables where pubname = 'streamkap_pub'; ``` **Altering publications** You cannot alter `FOR ALL TABLES` publications to include/exclude tables. If you set up a `FOR ALL TABLES` publication and later decide to change that, you have to drop the publication and create another to include specific tables e.g. `CREATE PUBLICATION ... TABLE table1, table2, table3, ...`. However, any change events that occur before the new publication's created will not be included in it, so a snapshot's required to ensure they are not missed by your Streamkap pipelines. You should also stop the [Source](https://app.streamkap.com/connectors/sources) *before* changing the publication. A replication slot represents a stream of change events the Connector reads from. * Create a replication slot. ```SQL SQL theme={null} -- Create a logical replication slot SELECT pg_create_logical_replication_slot('streamkap_pgoutput_slot', 'pgoutput'); -- Verify the replication slot is working (this may take a few moments to return the count) SELECT count(*) FROM pg_logical_slot_peek_binary_changes('streamkap_pgoutput_slot', null, null, 'proto_version', '1', 'publication_names', 'streamkap_pub'); ``` **Replication slot auto-creation** If the Connector cannot find the replication slot named in [Replication Settings](http://localhost:3000/neon-postgresql-cdc#4-replication-settings) during Streamkap Setup, it will attempt to create one automatically as it cannot read the database transaction logs without one. This is assuming the database user has been granted the `REPLICATION` role as per [Create Database User](/neon-postgresql-cdc#2-create-database-user). #### Excluding columns from replication By default a publication streams every column of each captured table. On **PostgreSQL 15+** you can attach a *column list* to a table in the publication so that only the listed columns are replicated — any column you leave out is excluded at the source and never enters the replication stream. ```SQL SQL - Selected Columns theme={null} -- Only the listed columns are published; every other column is excluded. -- Each table's list MUST include its replica identity (primary key), -- otherwise UPDATE/DELETE for that table cannot be replicated. CREATE PUBLICATION streamkap_pub FOR TABLE public.users (id, email, created_at, updated_at), public.orders (id, user_id, status, total), public.products (id, sku, name, price); -- Add or change a table's column list on an existing publication ALTER PUBLICATION streamkap_pub SET TABLE public.users (id, email, updated_at); -- Verify the published columns per table SELECT pubname, schemaname, tablename, attnames FROM pg_publication_tables WHERE pubname = 'streamkap_pub'; ``` * Requires **PostgreSQL 15 or later**. On earlier versions a publication always streams all columns. * Each table's column list **must include the table's replica identity** — its primary key, or the columns from `REPLICA IDENTITY USING INDEX` / `FULL`. Otherwise `UPDATE` and `DELETE` events for that table are not replicated. * Column lists are only valid with `FOR TABLE`, not `FOR ALL TABLES`. A table listed without parentheses publishes all of its columns, so you can mix restricted and unrestricted tables in one publication. A publication column list only governs **streamed (CDC) changes**. The initial snapshot reads columns directly from the table, so an excluded column can still appear in snapshot records. If a column must never be captured, exclude it from the snapshot as well, not only from the publication. ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **PostgreSQL**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Hostname**: Specify the hostname. **PgBouncer and pooled connections** Neon uses PgBouncer to support connection pooling via pooler hostnames like this `ep-cool-darkness-123456-pooler.us-east-2.aws.neon.tech` (notice the `-pooler` option). However, PgBouncer has very limited support for PostgreSQL startup options, and Streamkap depends on one PostgreSQL option `replication` it does not support. The Connector will fail with a PgBouncer [unsupported startup parameter](https://neon.com/docs/connect/connection-errors#unsupported-startup-parameter) error in this scenario. Because of that, Neon's connection pooling cannot be used, so please remove (if present) the `-pooler option` from the hostname. This means connections from Streamkap will be unpooled, and Neon has limits on unpooled connections you should be aware of. * **Port**: Default is `5432`. * **Connect via SSH Tunnel**: The Connector will connect to an SSH server in your network which has access to your database. This is necessary if the Connector cannot connect directly to your database. * See [SSH Tunnel](/ssh-tunnel) for setup instructions. * **Username**: Username to access the database. By default, Streamkap scripts use `streamkap_user`. * **Password**: Password to access the database. * **Database**: Specify the database to stream data from. * **Read only**: Whether or not to use a read-only connection. Requires PostgreSQL version 13 or higher. * When connecting to a read replica, set this to **Yes** to support Streamkap snapshots. * **Heartbeats**: Enabled by default. Choose one of three modes—see [Heartbeats](#4-heartbeats) for setup instructions: * **Read-write** connections: configure a heartbeat table in the source database and set **Heartbeat Table Schema**. * **Read-only** connections: configure a scheduled heartbeat job on the primary database using `pg_cron`, and include the heartbeat table in [Schema and Table Capture](#6-schema-and-table-capture). * **Logical-message** (PostgreSQL 14+, works under read-only): set **Use Logical-Message Heartbeat** to **Yes** and leave **Heartbeat Table Schema** blank. No table or write privileges required on the source. ### 3. Snapshot Settings If you set Read only to **No**, you will need to create a snapshot signal table and give permissions to the `streamkap_user`. See [Enable Snapshots](/neon-postgresql-cdc#3-enable-snapshots) for setup instructions. * **Signal Table**: Full path to the signal table including schema and table name (e.g., `streamkap.streamkap_signal`). This table is used for incremental snapshotting. See [Enable Snapshots](#3-enable-snapshots) for setup instructions. ### 4. Replication Settings * **Replication Slot Name**: The name of the replication slot for the connector to use. Default is `streamkap_pgoutput_slot`. * **Publication Name**: The name of the publication for the connector to use. Default is `streamkap_pub`. ### 5. Advanced Parameters * **SSL mode**: Whether to use an encrypted connection to the PostgreSQL server. By default, it's required. * **Prefix with Database Name?**: Changes the format of topics to `DatabaseName_TopicName` * **Represent binary data as**: Specifies how the data for binary columns e.g. `blob`, `binary`, `varbinary` should be interpreted. Your destination for this data can impact which option you choose. Default is `bytes`. Click **Next**. ### 6. Schema and Table Capture * **Add Schemas/Tables**: Specify the schema(s) and table(s) for capture * You can bulk upload here. The format is a simple list of schemas and tables, with each entry on a new row. Save as a `.csv` file without a header. **CDC only captures base tables, not Views** Change Data Capture reads the PostgreSQL write-ahead log (WAL), which only records changes to physical tables. Database Views are query-time computations with no physical storage—they don't generate WAL entries. **What you cannot capture:** Views, temporary tables, unlogged tables, foreign tables (FDW), or system tables (information\_schema, pg\_catalog). **Solution:** Specify only the underlying base tables that feed your views. You can recreate the view logic in your destination or transformation layer. **Generated columns are not kept up to date** PostgreSQL [generated columns](https://www.postgresql.org/docs/current/ddl-generated-columns.html) (defined with `GENERATED ALWAYS AS (...) STORED`) are calculated by the database from other columns. PostgreSQL does **not** include them in the replication stream Streamkap reads, so live streaming cannot keep their values current. **We recommend excluding generated columns from capture.** In the Source's **Settings → Advanced** section, set **Column Selection Mode** to **Exclusion** and add the column to the **Column Exclusion List** (fully-qualified, e.g. `schema.table.column`). If you need the value at your destination, calculate it there — or with a [transform](/transforms) — from the columns it is derived from. **Snapshots:** An incremental snapshot (the watermarked **Filtered** and **Full** options) of a table that contains a generated column will **fail** unless the column is excluded. **Blocking** and **Parallel** snapshots are not affected, though any value they capture will become stale because streaming cannot update it. See [Snapshots & Backfilling](/snapshots). PostgreSQL 18 and later can optionally stream stored generated columns when the publication is created with `publish_generated_columns = stored`; earlier versions cannot. Click **Save**. ## Troubleshooting When upgrading the PostgreSQL database used by Streamkap, there are specific steps to prevent data loss and ensure continued operation. Streamkap handles network failures and outages well. If a monitored database stops, the connector resumes from the last recorded log sequence number (LSN) once communication is restored. It retrieves this offset and queries PostgreSQL for a matching LSN in the replication slot. A replication slot is required for change capture, but PostgreSQL removes slots during upgrades and doesn’t restore them. When the connector restarts, it requests the last known offset, but PostgreSQL cannot return it. Creating a new replication slot isn't enough to prevent data loss. New slots only track changes from their creation point and lack earlier offsets. The connector fetches its last known offset from Kafka but can't retrieve corresponding data from the new slot. It skips older change events and resumes from the latest log position, causing silent data loss with no warnings. ### Procedure Follow these steps to minimize data loss. Note that a few steps may require support from Streamkap. We recommend notifying us about your database upgrade ahead of time to ensure you have the necessary support. * Using your database's upgrade procedure, ensure writes to it have stopped. * Allow the connector to capture all change events before starting the upgrade procedure. Ask Streamkap to confirm this for you. **Interrupting change data capture** If all the events were not captured before stopping the Source and upgrading the database, you can perform a snapshot (after the procedure is completed) in Streamkap for that Source to ensure no change events were missed. * Assuming all events are captured, stop the Source in the Streamkap app. This flushes the last records and saves the last offset. * Stop the database and upgrade it using your upgrade procedure. Once the database is upgraded, and **before** allowing writes again: * Recreate the logical replication slot, otherwise, Streamkap will miss changes. See [Create Publication & Slot](/neon-postgresql-cdc#4-create-publication--slot). * Verify the publication for Streamkap exists; recreate it if necessary: [Create Publication & Slot](/neon-postgresql-cdc#4-create-publication--slot). **Update the Source in the Streamkap app** If you have chosen different names for the logical replication slot or publication, make sure to update them in the Streamkap setup page for the relevant PostgreSQL Source. In this case, contact Streamkap, as they may need to reset your Connector's offsets in this scenario. * Restore write access to the database. * Resume or restart the Source in the Streamkap app. Introduced in PostgreSQL 13, the `REPLICA IDENTITY` table setting controls what data is logged for row updates and deletes. By default, only the primary key and Streamkap metadata column values are retained for deleted records. All other columns will be empty. This leaves you with an incomplete record. If you require - for auditing and historical tracking purposes - **all** column values for deleted records, or if your deletion strategy for your destination is 'soft deletes' (retain the deleted record with a deletion flag), you should set the `REPLICA IDENTITY` to `FULL` for all capture tables. ```SQl SQl theme={null} ALTER TABLE {table} REPLICA IDENTITY FULL; ``` This ensures complete data retention. For capturing partitioned tables, it's essential to enable `publish_via_partition_root` on the publication. By default, changes to partitions are published from the partition itself. The connector expects changes to come from the root table. To ensure compatibility and consistent replication, enable this setting: `ALTER PUBLICATION streamkap_pub SET (publish_via_partition_root = true);` # Observability Source: https://docs.streamkap.com/observability Monitor system health, data lineage, and record count accuracy across your Streamkap pipelines ## Observability Navigate to **Observability** in the project sidebar to see the observability dashboard. Observability overview The observability view includes: 1. **[System Health](#system-health)** — operational status summary at the top 2. **[Lineage](#lineage)** — data flow details with three tabs: [By Source](#by-source), [Consumer Groups](#consumer-groups), and [Audit](#audit) ## System Health Summarizes operational status. Items are grouped into colored sections based on their current state. ### Issues Red section — appears only when there are active problems. | Indicator | Description | | ---------------- | ----------------------------------------------------------------------------------------- | | **Sources** | Source connectors in an error state. Shows count and names of affected sources. | | **Destinations** | Destination connectors in an error state. Shows count and names of affected destinations. | | **Transforms** | Transform jobs that have failed. Shows count of affected transforms. | ### Warnings Amber section — appears only when there are active warnings. | Indicator | Description | | -------------- | ----------------------------------------------------------------------- | | **DLQ** | Dead Letter Queue topics containing undeliverable records. | | **S. Latency** | Topics where source latency exceeds 10 minutes. Shows max latency. | | **D. Lag** | Topics where the destination consumer has fallen behind. Shows max lag. | ### Healthy Green section — shows all indicators that are currently OK. When everything is healthy, all issue and warning indicators appear here. ### Info Shows general operational metrics below the Healthy section: | Indicator | Description | | ------------- | ----------------------------------------------------------------- | | **Snapshots** | Topics currently performing an initial data load from the source. | | **Alerts** | Unread alert notifications for this project. | Health items with active issues are clickable — they scroll to the lineage table and apply the relevant filters automatically (e.g., clicking **Sources** filters the lineage by broken source connectors, clicking **DLQ** applies the DLQ quick filter). ## Lineage Shows how data flows through your pipelines — from sources, through optional transforms, to destinations. ### By Source Groups topics by their source connector (default tab). Lineage — By Source expanded Each source row shows the source name, connector type icon, status, and aggregate metrics. Click a row or use **Expand All** to see individual topics: | Source columns | Transform columns | Destination columns | | ------------------------------------------------------------------- | -------------------------------------- | -------------------------------------- | | Topic name, Status, Latency, Events (events/sec), Snapshot progress | Transform name, Output Topics, Latency | Destination name, Status, Lag (events) | Source, transform, and destination names include external link icons that navigate to their detail pages. Topic names are clickable and navigate to the topic detail view. ### Audit Compares record counts between sources and destinations to verify data completeness. Lineage — Audit tab expanded Each source row shows the source name, connector type icon, and topic count. Expand to see per-topic details: | Source columns | Destination columns | | --------------------------- | ----------------------------------------- | | Topic name, Count (records) | Destination name, Count (records), Diff % | The **Diff %** column shows the percentage difference between source and destination record counts, color-coded by severity: * **Green** (0–1%) — counts match or near-match * **Amber** (2–10%) — minor drift * **Red** (>10%) — significant drift **Audit-specific controls:** * **Run Audit** — triggers record count collection for all sources on the current page * **Diff %** dropdown — filters topics by drift percentage range (All, 0–2%, 2–10%, 10–50%, 50–100%, >100%) Record counts require a completed audit run. Use the **Run Audit** button to collect counts, then expand source groups to see the results. ### Consumer Groups Displays Kafka consumer groups and their health status. Lineage — Consumer Groups Each row shows the **Group ID** (linked to the consumer group detail page), **State** (Stable, Empty, Dead, PreparingRebalance, CompletingRebalance, Unknown), and **Members** count. Expand a row to see individual members with their Member ID, Client ID, and Host. **Consumer Groups filters:** * **Search** — filter by group ID * **Status** — filter by group state For full consumer group management including offset resets and lag monitoring, see the [Consumer Groups](/consumer-groups) page. ## Filters All lineage tabs provide search, filtering, and pagination controls. ### Search * **Source search** — server-side search on source/group names * **Topic search** — filters topics within expanded groups. When active, groups auto-expand to show matching topics. * **Per group** — controls how many topics display per expanded group (5, 10, 20, or 50) ### Entity Filters Filter badges appear below the search bar. Available filters vary by tab: | Filter | By Source | Audit | Consumer Groups | | --------------- | --------- | ----- | --------------- | | **Source** | Yes | Yes | — | | **Destination** | Yes | Yes | — | | **Transform** | Yes | — | — | | **Snapshot** | Yes | — | — | | **S. Latency** | Yes | — | — | | **D. Lag** | Yes | — | — | | **Status** | — | — | Yes | The S. Latency and D. Lag filters support comparison operators (`>`, `>=`, `<`, `<=`, `=`, `!=`) with numeric values. ### Presets The **Presets** dropdown (By Source tab) provides quick filter toggles: * **Has Destination** — topics with at least one destination pipeline * **Has Transform** — topics with at least one transform * **DLQ** — Dead Letter Queue topics * **Orphaned** — topics without destination pipelines ### Pagination and Controls * **Expand All / Collapse All** — toggles all groups at once * **Refresh** — manually re-fetches data (auto-refreshes every 10 seconds) * **Page size** — configurable items per page * **Page navigation** — navigate between pages of results ## Related Documentation * [Alerts](/alerts) — proactive notifications for pipeline issues * [Consumer Groups](/consumer-groups) — consumer group management and offset resets * [Topics](/topics) — Kafka topic metrics and message inspection * [Transforms](/transforms-1) — transform configuration and monitoring * [Logs](/logs) — troubleshoot pipeline and connector issues * [DLQ Operations](/dlq-operations) — manage dead letter queue topics # OpenID connect - IDP setup Source: https://docs.streamkap.com/openid-connect-idp-setup OpenID Connect (OIDC) is an open identity authentication protocol that works on top of the OAuth 2.0 framework. OIDC allows individuals to use SSO to access relying party sites. This guide explains how users can configure their OpenID Connect (OIDC) settings with their identity provider. Below is an example with Okta. Create a new OpenID Connect application with Okta that you can use as the Identity Provider for users. In the Streamkap app, click your avatar in the top navigation bar, select **Account preferences**, then click **SSO** under the Workspace section. Choose Open ID Connect as IDP method and copy the Redirect URI. Use Redirect URI from the Admin portal as Sign-in redirect URIs on Okta: Get the Client ID and secret from your Okta application and insert them in the Admin portal OpenId configuration. OpenID Connect parameters overview: Configuration Description * Issuer URL - This is a URL that is given by the IDP. This URL provides instructions on how to communicate with the IDP. If you are unsure you have the right URL, insert in in the issues URL field and it will be validated automatically. * Client ID - The clientId is given by the IDP. This allows the IDP to identify who is requesting to authenticate. * Secret Key - The secret key allows authentication with the IDP to validate the user who tries to log in. It must correspond to the secret key inserted for the clientId. * Redirect URI - This is a pre-configured value that lets the IDP know where it should return the user after the user is authenticated within the IDP. The redirect URI value must be configured in the IDP itself. After configuring the identity provider, you will need to claim one or more domains for the account. This step is required in order to avoid abuse of a domain. The domain is the same domain you login with. For example, ours would be streamkap.com The domain needs to be claimed by copying the TXT record and applying it to your DNS provider. If you cannot obtain access to your organization's DNS, please contact your application Administrator. You can configure multiple domains for an account. This can be useful if you're using multiple environments for development or multiple production applications on separate domains and need the SSO connection to cover several domains. Select which roles should be assigned to SSO users by default and map IDP groups to specific roles. Roles you assign to users through SSO will apply regardless of whatever additional roles you assign to those users. **Default SSO Roles** Assign default roles to all SSO users by adding one or more Frontegg roles from your list of predefined roles. Mapping groups to roles with OpenID Connect is currently only possible for the application owner via Frontegg APIs or using Frontegg Backoffice. Save the connection and make sure that it is enabled. Now all users with the domain that was configured for the connection will be redirected to their IDP when they try to sign in. # OpenSearch Source: https://docs.streamkap.com/opensearch Stream data from OpenSearch # Prerequisites * Connection details * Streamkap user # Granting Privileges * Create a user and password for Streamkap # Consider Access Restrictions * Visit [Connection Options](/connection-options) to ensure Streamkap can reach your database # Setup OpenSearch Connector in Streamkap * Create an OpenSearch Source * Enter the following information: * A name for your Connector * Hostname * Port (443 for HTTPS) * Authentication Type * For Basic, enter: * Username * Password * Protocol (HTTPS or HTTP) * Index names * Datetime Field name (e.g @timestamp) * Click Save The connector will take approximately 1 minute to start processing data. # Overview Source: https://docs.streamkap.com/oracle Streamkap supports syncing OracleDB in real-time using change data capture for: Streamkap's Oracle Source supports **LogMiner**, **XStream**, and **OpenLogReplicator** as log reading methods. If you don't see your platform listed, please contact us as we're happy to help find and provide the setup documentation you need. # Oracle (Generic) Source: https://docs.streamkap.com/oracle-generic Configure Oracle 12c+ as a Streamkap CDC source: enable LogMiner and archive logs, create the connector user, and grant required privileges and quotas. ## Prerequisites **Standby databases** An Oracle database can be configured with either a physical or a logical standby database for recovery after a production failure. At this time, Streamkap does not support them. * Oracle 12c or above, Standard or Enterprise Edition * Hostname and Port * (single-tenant architecture): Database name * (multi-tenant architecture): Container database name (CDB) and pluggable database name (PDB) * The Oracle database master user credentials or equivalent ## Oracle Setup Streamkap's Oracle Source supports **LogMiner**, **XStream**, and **OpenLogReplicator** as log reading methods. For the Connector to ingest changes from your database it is dependent on Oracle's **redo** logs and **archive** logs. It is important that these redo logs are large enough and, the archive logs are retained for long enough to ensure all changes are captured. ### 1. Grant Database Access * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your database. ### 2. Enable Archive Logs **Reboot required** When `ARCHIVELOG` mode is enabled, your Oracle database will be taken offline. When redo logs fill up, Oracle archives groups of them into archive logs. Archive logs should be retained for at least 24 hours. However, we recommend retaining them for longer. Too short a retention period and changes may not be captured and processed. **Retention periods and database storage** Archive logs are retained on your **database instance** using up its storage capacity. It is important to make sure it has enough space, otherwise, performance issues and outages can occur. In general, the more tables (and columns) there are, the more capacity is required. For the Connector, additional capacity is essential because it depends on Oracle's supplemental logging. To estimate what storage capacity you might need and assuming your Oracle database has supplemental logging enabled, you can look at the last 1 hour of log storage usage and multiply that by the retention period (at least 24 hours) you choose. Here's an example script for that: ```sql SQL theme={null} SELECT SUM(BLOCKS * BLOCK_SIZE) bytes, -- usage SUM(BLOCKS * BLOCK_SIZE) * 72 estimated_bytes -- assuming 72 hours archivelog retention FROM V$ARCHIVED_LOG WHERE FIRST_TIME >= SYSDATE-(1/24) -- last hour AND DEST_ID=1; ``` You should set the [DB\_RECOVERY\_FILE\_DEST\_SIZE](https://docs.oracle.com/database/121/REFRN/GUID-FA2F9735-DD0E-4026-96F0-80AF0E5E6E64.htm#REFRN10235) parameter to a value that is appropriate for your available disk space. ```SQL SQL theme={null} -- Replace the {...} placeholders as required alter system set db_recovery_file_dest_size = {recovery_file_size}; alter system set db_recovery_file_dest = '{recovery_file_location}' scope=spfile; ``` Then, configure Oracle [RMAN](https://docs.oracle.com/cd/B28359_01/backup.111/b28270/rcmconfb.htm#BRADV89439) to retain backups and archive logs for at least 24 hours. We recommend retaining data for longer. ```SQL SQL theme={null} RMAN> CONFIGURE RETENTION POLICY TO RECOVERY WINDOW OF 3 DAYS; ``` To enable `ARCHIVELOG` mode, run this script: ```SQL SQL theme={null} SHUTDOWN IMMEDIATE; STARTUP MOUNT; ALTER DATABASE ARCHIVELOG; ALTER DATABASE OPEN; ``` To confirm if `ARCHIVELOG` mode has been enabled, run this query: ```SQL SQL theme={null} archive log list; ``` If the `Database log mode` is `Archive Mode` then it is enabled. ### 3. Enable LogMiner For the Connector to query the redo and archive logs, it is dependent on the [Oracle LogMiner](https://docs.oracle.com/en/database/oracle/oracle-database/19/sutil/oracle-logminer-utility.html) utility. To enable that, supplemental logging must be enabled. ```SQL SQL theme={null} -- Enable database supplement logging ALTER DATABASE ADD SUPPLEMENTAL LOG DATA; ``` After supplemental logging has been enabled at the database level, you then need to enable table level supplemental logging. ```SQL SQL theme={null} -- To enable all supplemental logging, run the following SQL statement for each table: ALTER TABLE {schema}.{table} ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS ``` To confirm if supplemental logging has been enabled, run this query: ```SQL SQL theme={null} SELECT NAME, SUPPLEMENTAL_LOG_DATA_MIN FROM V$DATABASE; ``` If the `SUPPLEMENTAL_LOG_DATA_MIN` is `YES` it is enabled. #### Resize Redo Logs An Oracle instance typically starts with three, online redo log files, 50MB each. That is too small and too few; more log files are necessary, and the logs should be resized to at least 1024MB or more, especially for production databases. Before making any changes, run this query to check the current log file sizes: ```SQL SQL theme={null} SELECT GROUP#, BYTES/1024/1024 SIZE_MB, STATUS FROM V$LOG ORDER BY 1; ``` | GROUP# | SIZE\_MB | STATUS | | ------ | -------- | -------- | | 1 | 50 | INACTIVE | | 2 | 50 | INACTIVE | | 3 | 50 | CURRENT | Also, we need to determine how many log files per *group* there are and their location. ```SQL SQL theme={null} SELECT GROUP#, LOCATION FROM V$LOGFILE ORDER BY 1, 2; ``` | GROUP# | LOCATION | STATUS | | ------ | -------------------------------------- | -------- | | 1 | /opt/oracle/oradata/ORCLCDB/redo01.log | ACTIVE | | 2 | /opt/oracle/oradata/ORCLCDB/redo02.log | INACTIVE | | 3 | /opt/oracle/oradata/ORCLCDB/redo03.log | UNUSED | Now replace the old log files with new, larger log files. Only `INACTIVE` and `UNUSED` groups can be dropped and recreated. **Log multiplexing** Oracle does support the notion of multiple log files per group known as 'log multiplexing'. If your database uses this, use a comma-delimited list of filenames to register each log file. ```SQL SQL theme={null} -- Execute these statements as many times as required for existing INACTIVE and UNUSED log groups -- Replace {group_number} placeholder and {log_location}, using a comma separated list of filenames if database uses log multiplexing ALTER DATABASE CLEAR LOGFILE GROUP {group_number}; ALTER DATABASE DROP LOGFILE GROUP {group_number}; ALTER DATABASE ADD LOGFILE GROUP {group_number} ('{log_location}') size 1024M REUSE; ``` Now switch the `ACTIVE` log so we can drop it. ```SQL SQL theme={null} ALTER SYSTEM SWITCH LOGFILE; ``` We need to wait for the database to eventually switch the status of the `ACTIVE` group to `INACTIVE`. The switch could take several minutes, so be patient and recheck the size periodically. Once the status reaches `INACTIVE`, replace the last, old log file and confirm all logs are resized: ```SQL SQL theme={null} -- Execute these statements as many times as required for existing INACTIVE and UNUSED log groups -- Replace {group_number} placeholder and {log_location}, using a comma separated list of filenames if database uses log multiplexing ALTER DATABASE CLEAR LOGFILE GROUP {group_number}; ALTER DATABASE DROP LOGFILE GROUP {group_number}; ALTER DATABASE ADD LOGFILE GROUP {group_number} ('{log_location}') size 1024M REUSE; SELECT GROUP#, BYTES/1024/1024 SIZE_MB, STATUS FROM V$LOG ORDER BY 1; ``` | GROUP# | SIZE\_MB | STATUS | | ------ | -------- | ------- | | 1 | 1024 | CURRENT | | 2 | 1024 | UNUSED | | 3 | 1024 | ACTIVE | | ... | ... | ... | Now add additional, new log files as necessary. We suggest starting with 4 additional log files. **Group numbering and log file sizing** The `{group_number}` should follow sequentially. There is no benefit to organising log groups in sequences such as `10,20,30` or mixed sizing such as `128MB, 1024MB, 512MB`. For example, if the highest group number is `3` and log file `/opt/oracle/oradata/ORCLCDB/redo03.log` of size `1024MB`, the next log file added should be group `4` and log file `/opt/oracle/oradata/ORCLCDB/redo04.log` of size `1024MB`. ```SQL SQL theme={null} -- Execute these statements as many times as required -- Replace {group_number} placeholder and {log_location}, using a comma separated list of filenames if database uses log multiplexing ALTER DATABASE ADD LOGFILE GROUP {group_number} ('{log_location}') size 1024M; SELECT GROUP#, BYTES/1024/1024 SIZE_MB, STATUS FROM V$LOG ORDER BY 1; ``` | GROUP# | SIZE\_MB | STATUS | | ------ | -------- | -------- | | 1 | 1024 | CURRENT | | 2 | 1024 | UNUSED | | 3 | 1024 | ACTIVE | | 4 | 1024 | INACTIVE | | 5 | 1024 | INACTIVE | | ... | ... | ... | ### 4. Create Database User Depending on your [database architecture](https://docs.oracle.com/en/database/oracle/oracle-database/19/multi/introduction-to-the-multitenant-architecture.html#GUID-C5B0AF7D-ABE8-4F69-9552-F4DAF40281F1), choose the correct script below to create a database user with privileges for the Connector. ```SQL CDB (Multi-tenant) theme={null} -- Replace {...} placeholders as needed ALTER SESSION SET CONTAINER=CDB$ROOT; CREATE USER C##STREAMKAP_USER IDENTIFIED BY {password}; ALTER USER C##STREAMKAP_USER QUOTA UNLIMITED ON USERS; ALTER SESSION SET CONTAINER={PDB}; CREATE TABLESPACE STREAMKAP_LOGMINER_TBS DATAFILE {filename} SIZE 25M AUTOEXTEND ON MAXSIZE UNLIMITED; ALTER USER C##STREAMKAP_USER DEFAULT TABLESPACE STREAMKAP_LOGMINER_TBS; ALTER USER C##STREAMKAP_USER QUOTA UNLIMITED ON STREAMKAP_LOGMINER_TBS; -- Grant permissions GRANT CREATE SESSION TO C##STREAMKAP_USER CONTAINER=ALL; GRANT SET CONTAINER TO C##STREAMKAP_USER CONTAINER=ALL; -- Allows the Connector to use LogMiner GRANT LOGMINING TO C##STREAMKAP_USER CONTAINER=ALL; -- Flashback queries used for performing initial snapshots of the data GRANT FLASHBACK ANY TABLE TO C##STREAMKAP_USER CONTAINER=ALL; GRANT SELECT ANY TRANSACTION TO C##STREAMKAP_USER CONTAINER=ALL; -- Required for schema history when performing initial snapshots GRANT SELECT_CATALOG_ROLE TO C##STREAMKAP_USER CONTAINER=ALL; GRANT EXECUTE_CATALOG_ROLE TO C##STREAMKAP_USER CONTAINER=ALL; -- Connector creates a table for explicitly managing the flushing of internal log buffers (LGWR) GRANT CREATE TABLE TO C##STREAMKAP_USER CONTAINER=ALL; GRANT CREATE SEQUENCE TO C##STREAMKAP_USER CONTAINER=ALL; -- Grant the Streamkap user permission to read each schema and table you wish to sync ALTER SESSION SET CONTAINER={PDB}; GRANT SELECT ON {schema}.{table} TO C##STREAMKAP_USER CONTAINER=ALL; -- Alternatively, you can grant access to all -- GRANT SELECT ANY TABLE TO C##STREAMKAP_USER CONTAINER=ALL; -- Grant the Streamkap user access to the DBA_EXTENTS, DBA_TABLESPACES, DBA_SEGMENTS, and TRANSACTION system views. GRANT SELECT ON DBA_EXTENTS TO C##STREAMKAP_USER; GRANT SELECT ON DBA_TABLESPACES TO C##STREAMKAP_USER; GRANT SELECT ON DBA_SEGMENTS TO C##STREAMKAP_USER; GRANT SELECT ANY TRANSACTION TO C##STREAMKAP_USER; -- The following grants must be run as SYS or a user with SYSDBA privilege (e.g. sqlplus / as sysdba) -- SYSTEM alone does not have sufficient privileges to grant access to SYS-owned objects ALTER SESSION SET CONTAINER=CDB$ROOT; GRANT SELECT ON SYS.V_$DATABASE TO C##STREAMKAP_USER; GRANT SELECT ON SYS.V_$PARAMETER TO C##STREAMKAP_USER; GRANT SELECT ON SYS.V_$ARCHIVED_LOG TO C##STREAMKAP_USER; GRANT SELECT ON SYS.V_$ARCHIVE_DEST TO C##STREAMKAP_USER; GRANT SELECT ON SYS.V_$LOGMNR_CONTENTS TO C##STREAMKAP_USER; GRANT EXECUTE ON DBMS_LOGMNR TO C##STREAMKAP_USER; GRANT EXECUTE ON DBMS_LOGMNR_D TO C##STREAMKAP_USER; GRANT SELECT ANY TRANSACTION TO C##STREAMKAP_USER; GRANT EXECUTE_CATALOG_ROLE TO C##STREAMKAP_USER; ``` ```SQL Non-CDB (Single-tenant) theme={null} -- Replace {...} placeholders as needed CREATE USER STREAMKAP_USER IDENTIFIED BY {password}; CREATE TABLESPACE STREAMKAP_LOGMINER_TBS DATAFILE {filename} SIZE 25M AUTOEXTEND ON MAXSIZE UNLIMITED; ALTER USER STREAMKAP_USER DEFAULT TABLESPACE STREAMKAP_LOGMINER_TBS; ALTER USER STREAMKAP_USER QUOTA UNLIMITED ON STREAMKAP_LOGMINER_TBS; -- Grant permissions GRANT CREATE SESSION TO STREAMKAP_USER; GRANT SET CONTAINER TO STREAMKAP_USER; -- Allows the Connector to use LogMiner GRANT LOGMINING TO STREAMKAP_USER; -- Flashback queries used for performing initial snapshots of the data GRANT FLASHBACK ANY TABLE TO STREAMKAP_USER; GRANT SELECT ANY TRANSACTION TO STREAMKAP_USER; -- Required for schema history when performing initial snapshots GRANT SELECT_CATALOG_ROLE TO STREAMKAP_USER; GRANT EXECUTE_CATALOG_ROLE TO STREAMKAP_USER; -- Connector creates a table for explicitly managing the flushing of internal log buffers (LGWR) GRANT CREATE TABLE TO STREAMKAP_USER; GRANT CREATE SEQUENCE TO STREAMKAP_USER; -- Grant the Streamkap user permission to read each schema and table you wish to sync GRANT SELECT ON {schema}.{table} TO STREAMKAP_USER; -- Alternatively, you can grant access to all -- GRANT SELECT ANY TABLE TO STREAMKAP_USER; -- The following grants must be run as SYS or a user with SYSDBA privilege (e.g. sqlplus / as sysdba) -- SYSTEM alone does not have sufficient privileges to grant access to SYS-owned objects GRANT SELECT ON SYS.V_$DATABASE TO STREAMKAP_USER; GRANT SELECT ON SYS.V_$PARAMETER TO STREAMKAP_USER; GRANT SELECT ON SYS.V_$ARCHIVED_LOG TO STREAMKAP_USER; GRANT SELECT ON SYS.V_$LOGMNR_CONTENTS TO STREAMKAP_USER; GRANT EXECUTE ON DBMS_LOGMNR TO STREAMKAP_USER; GRANT EXECUTE ON DBMS_LOGMNR_D TO STREAMKAP_USER; GRANT SELECT ANY TRANSACTION TO STREAMKAP_USER; GRANT EXECUTE_CATALOG_ROLE TO STREAMKAP_USER; ``` ### 5. Enable Snapshots To backfill your data, the Connector needs to be able to perform Snapshots (See [Snapshots & Backfilling](/snapshots) for more information). To enable this process, a table must be created for the Connector to use. Depending on your [database architecture](https://docs.oracle.com/en/database/oracle/oracle-database/19/multi/introduction-to-the-multitenant-architecture.html#GUID-C5B0AF7D-ABE8-4F69-9552-F4DAF40281F1), choose the correct script below. The examples below use `STREAMKAP_SIGNAL` as the signal table name, but you can choose any name. During [Streamkap Setup](#3-snapshot-settings), provide the full path to your signal table in `schema.table` format (e.g., `STREAMKAP_USER.STREAMKAP_SIGNAL`). ```SQL CDB (Multi-tenant) theme={null} -- Replace {...} placeholders as needed ALTER SESSION SET CONTAINER={PDB}; -- Create a local (PDB) user CREATE USER STREAMKAP_USER IDENTIFIED BY {password}; GRANT CREATE SESSION TO STREAMKAP_USER; GRANT SET CONTAINER TO STREAMKAP_USER; ALTER USER STREAMKAP_USER DEFAULT TABLESPACE STREAMKAP_LOGMINER_TBS; ALTER USER STREAMKAP_USER QUOTA UNLIMITED ON STREAMKAP_LOGMINER_TBS; -- Create the table CREATE TABLE STREAMKAP_USER.STREAMKAP_SIGNAL ( id VARCHAR2(255) PRIMARY KEY, type VARCHAR2(32) NOT NULL, data VARCHAR2(2000) NULL ); -- Grant necessary privileges on the table to the common user GRANT SELECT, UPDATE, INSERT, DELETE ON STREAMKAP_USER.STREAMKAP_SIGNAL TO C##STREAMKAP_USER; ``` ```SQL Non-CDB (Single-tenant) theme={null} -- Create the table CREATE TABLE STREAMKAP_USER.STREAMKAP_SIGNAL ( id VARCHAR2(255) PRIMARY KEY, type VARCHAR2(32) NOT NULL, data VARCHAR2(2000) NULL ); -- Grant necessary privileges on the table to the user GRANT SELECT, UPDATE, INSERT, DELETE ON STREAMKAP_USER.STREAMKAP_SIGNAL TO STREAMKAP_USER; ``` ### 6. Heartbeats Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. For read-write connections (when **Read only** is **No** during Streamkap Setup), the Connector writes to the heartbeat table directly. ```SQL SQL theme={null} -- Replace {...} placeholders as needed ALTER SESSION SET CONTAINER={PDB}; -- Create the heartbeat table with id, text, and last_update fields CREATE TABLE STREAMKAP_USER.STREAMKAP_HEARTBEAT ( id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY, text VARCHAR2(4000), last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Grant permission to the Streamkap user GRANT SELECT, UPDATE, INSERT, DELETE ON STREAMKAP_USER.STREAMKAP_HEARTBEAT TO STREAMKAP_USER; -- Grant necessary privileges on the table to the common user GRANT SELECT, UPDATE, INSERT, DELETE ON STREAMKAP_USER.STREAMKAP_HEARTBEAT TO C##STREAMKAP_USER; -- Insert the first row into the heartbeat table INSERT INTO STREAMKAP_USER.STREAMKAP_HEARTBEAT (text) VALUES ('test_heartbeat'); ``` *** ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **Oracle**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Hostname**: Specify the database endpoint. * **Port**: Default is `1521`. * **Connect via SSH Tunnel**: The Connector will connect to an SSH server in your network which has access to your database. This is necessary if the Connector cannot connect directly to your database. * See [SSH Tunnel](/ssh-tunnel) for setup instructions. * **Username** (case sensitive): Username to access the database. By default, Streamkap scripts use `STREAMKAP_USER`. * **Password**: Password to access the database. * **Database**: The database name (single-tenant architecture) or container database name (multi-tenant architecture). * **Pluggable Database** (optional, multi-tenant architecture only): The pluggable database name. * **Heartbeats**: * **Heartbeat Table Schema**: Streamkap will use a table in this schema to manage heartbeats. Usually this is the same as the Signal Table. See [Heartbeats](#6-heartbeats) for setup instructions. ### 3. Snapshot Settings * **Signal Table**: Full path to the signal table including schema and table name (e.g., `STREAMKAP_USER.STREAMKAP_SIGNAL`). This table is used for incremental snapshotting. See [Enable Snapshots](#5-enable-snapshots) for setup instructions. ### 4. Advanced Parameters * **Represent binary data as**: Specifies how the data for binary columns should be interpreted. Your destination for this data can impact which option you choose. Default is `bytes`. * **Capture Only Captured Databases DDL**: Specifies whether the connector records schema structures from all logical databases in the database instance or only captured databases. Enabling this when you have many databases in your instance can improve performance and avoid timeouts. Default is `false`. See [Schema History Optimization](/schema-history-optimization) for details. * **Capture Only Captured Tables DDL**: Specifies whether the connector records schema structures from all logical tables in the captured schemas or databases, or only captured tables. Enabling this when you have many tables can improve performance and avoid timeouts. Default is `false`. See [Schema History Optimization](/schema-history-optimization) for details. Click **Next**. ### 5. Schema and Table Capture * **Add Schemas/Tables**: Specify the schema(s) and table(s) for capture. * You can bulk upload here. The format is a simple list of schemas and tables, with each entry on a new row. Save as a `.csv` file without a header. **Adding tables programmatically** -- You can add tables to an existing source via the API or Terraform using the `table.include.list.user.defined` parameter (Terraform: `table_include_list_user_defined`). See [Terraform Resources](/terraform-resources#sources) for Terraform source configuration. **CDC only captures base tables, not Views** Change Data Capture reads Oracle redo logs via LogMiner, which only record changes to physical tables. Database Views are query-time computations with no physical storage—they don't generate redo log entries. **What you cannot capture:** Views, materialized views (capture base tables instead), global temporary tables, external tables, or system tables (DBA\_\*, ALL\_\*, USER\_\*). **Solution:** Specify only the underlying base tables that feed your views. You can recreate the view logic in your destination or transformation layer. Click **Save**. **Have questions?** See the [Oracle Source FAQ](/oracle-source-faq) for answers to common questions about Oracle sources, troubleshooting, and best practices. # Oracle Source FAQ Source: https://docs.streamkap.com/oracle-source-faq ## Oracle Sources FAQ for Streamkap This FAQ focuses on using Oracle Database as a source in Streamkap, including self-hosted setups and cloud variants (AWS RDS Oracle). Streamkap's Oracle connector provides real-time CDC with managed features like automatic scaling, UI setup, and ETL transformations. Streamkap's Oracle Source supports **LogMiner**, **XStream**, and **OpenLogReplicator** as log reading methods. An Oracle source in Streamkap enables real-time Change Data Capture (CDC) from Oracle databases, capturing row-level inserts, updates, and deletes with sub-second latency. It uses LogMiner to read the redo logs and stream changes to destinations, supporting snapshots for initial loads, schema evolution, and heartbeats for low-traffic databases. Streamkap abstracts complexity, offering a serverless setup via UI or API. * Oracle Database 11g+ for basic CDC; 12c+ for enhanced features like multitenant architecture and JSON support; 19c+ for latest optimizations and cloud compatibility. * Compatible with Oracle Database 11.2+ * Oracle RAC (Real Application Clusters) supported Streamkap supports: * Self-hosted (on-prem/VM) * AWS RDS Oracle (Standard Edition, Enterprise Edition) * Oracle Cloud Infrastructure (OCI) * Oracle RAC (Real Application Clusters) Streamkap also supports standalone instances, Data Guard configurations, and Oracle Exadata. * **CDC**: LogMiner-based via redo logs; captures inserts/updates/deletes with SCN (System Change Number) tracking * **Snapshots**: Ad-hoc/initial backfills using signal table methods; locking or non-locking modes * **Schema Evolution**: Automatic handling of DDL changes when enabled; tracks table structure modifications * **Heartbeats**: Enabled by default to prevent redo log buildup in low-traffic databases * **Data Types**: Standard mappings (numerics, temporals, strings, LOBs, RAW as bytes/base64/hex, XML, JSON in 12c+) * **Ingestion Modes**: Inserts (append) or upserts * **Security**: SSL/TLS, Oracle wallet, IP allowlisting * **Monitoring**: Latency, lag, redo log metrics, LogMiner session stats in-app * Streamkap adds transaction metadata and ad-hoc snapshots with filters Streamkap uses Oracle LogMiner to read and parse redo log files, extracting committed change events. LogMiner queries are executed against redo logs to retrieve SQL statements and change data, which are then emitted as change events. The connector tracks progress using SCN (System Change Number). **Requirements:** * Supplemental logging must be enabled (database and table level) * Archive log mode must be enabled * LogMiner privileges required for the Streamkap user Supplemental logging instructs Oracle to include additional information in redo logs beyond what's needed for database recovery. This extra data is essential for CDC. **Types:** * **Database-level**: `ADD SUPPLEMENTAL LOG DATA` * **Table-level**: `ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS` (recommended for complete change records) **Why it matters:** * Without supplemental logging, redo logs may not contain complete before/after images * Affects ability to capture deletes and updates with full column data * Must be enabled before CDC starts capturing **Setting:** ```sql theme={null} -- Database level ALTER DATABASE ADD SUPPLEMENTAL LOG DATA; -- Table level (for complete records) ALTER TABLE schema.table ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS; ``` * Trigger ad-hoc at source/table level * Requires a signal table (`streamkap_signal`) * Methods: Blocking (uses locks) or incremental (chunked by ROWID or primary key) * Modes: `initial` (default), `always`, `initial_only`, `no_data`, `when_needed`, `configuration_based`, `custom` Streamkap simplifies triggering via UI. Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. For read-write connections (when **Read only** is **No** during Streamkap Setup), the Connector writes to the heartbeat table directly. ```SQL SQL theme={null} -- Replace {...} placeholders as needed ALTER SESSION SET CONTAINER={PDB}; -- Create the heartbeat table with id, text, and last_update fields CREATE TABLE STREAMKAP_USER.STREAMKAP_HEARTBEAT ( id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY, text VARCHAR2(4000), last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Grant permission to the Streamkap user GRANT SELECT, UPDATE, INSERT, DELETE ON STREAMKAP_USER.STREAMKAP_HEARTBEAT TO STREAMKAP_USER; -- Grant necessary privileges on the table to the common user GRANT SELECT, UPDATE, INSERT, DELETE ON STREAMKAP_USER.STREAMKAP_HEARTBEAT TO C##STREAMKAP_USER; -- Insert the first row into the heartbeat table INSERT INTO STREAMKAP_USER.STREAMKAP_HEARTBEAT (text) VALUES ('test_heartbeat'); ``` * **Basics**: Numbers (NUMBER, INTEGER, FLOAT), strings (VARCHAR2, CHAR, NVARCHAR2, NCHAR, CLOB), dates/timestamps (DATE, TIMESTAMP, TIMESTAMP WITH TIME ZONE) * **Advanced**: Binary (RAW, LONG RAW, BLOB as bytes/hex), XML (XMLTYPE), JSON (12c+, stored as VARCHAR2/CLOB) * **Custom**: ROWID, UROWID, spatial types (SDO\_GEOMETRY) with limitations * **Unsupported**: BFILE (external file pointers), deprecated LONG types (use CLOB instead), nested tables, VARRAYs without flattening, Oracle object types without conversion * Check archive log generation rate: ```sql theme={null} SELECT name, sequence#, first_time, next_time FROM v$archived_log WHERE first_time > SYSDATE - 1 ORDER BY first_time; ``` * Monitor LogMiner sessions: ```sql theme={null} SELECT * FROM v$logmnr_contents WHERE ROWNUM <= 10; ``` * Check redo log retention and space: ```sql theme={null} SELECT * FROM v$log; ``` **Best Practices**: * Retain archive logs for at least 3-5 days * Alert on archive log destination disk space * Monitor supplemental logging overhead * Track LogMiner query performance **No, CDC cannot capture Views or most virtual database objects.** **Why Views cannot be captured:**\ CDC captures changes by reading the database transaction log (binlog, WAL, oplog, redo log, etc.). Views are query-time computations over base tables—they don't store data or generate transaction log entries. When you query a view, the database engine executes the underlying SELECT statement against the base tables. Since views don't store data, they don't generate transaction log entries. **What cannot be captured:** * **Views**: Virtual tables with no physical storage or redo log entries * **Materialized Views (MVs)**: While they have physical storage, their refresh operations may not generate standard DML redo logs, or may generate bulk operations that are difficult to track incrementally. **Solution**: Capture the base tables that feed the materialized view, then recreate the MV logic downstream. * **Materialized View Logs**: Internal structures for fast refresh; not user data * **Global Temporary Tables (GTT)**: Session or transaction-scoped, redo logging depends on ON COMMIT setting, generally excluded from CDC * **External Tables**: Reference files outside the database, no redo logging * **Index-Organized Tables (IOT)**: Can be captured, but with special considerations for ROWID-based snapshots * **System Tables** (DBA\_\*, ALL\_\*, USER\_\*, V\$\*): Metadata views, not user data * **CTEs (Common Table Expressions)**: Query-time constructs with no persistent storage * **Object Tables**: Tables based on Oracle object types; require special handling **Solution:**\ Configure CDC on the underlying base tables that power your views. The view logic can be recreated in your destination or transformation layer. **Oracle-specific notes:** * **Materialized Views**: Depending on refresh method (COMPLETE, FAST, FORCE), may generate redo logs, but tracking incremental changes is problematic. Always capture the base tables instead. * **Materialized View with ON COMMIT refresh**: Generates redo logs but as part of transaction commit; difficult to associate with source changes * **Partitioned Views**: Capture each base table in the partition view union * **Index-Organized Tables**: Can be captured, but ensure supplemental logging includes the primary key (which is the physical organization key) **Example:**\ If you have a view `V_CUSTOMER_ORDERS` that joins tables `CUSTOMERS` and `ORDERS`, enable supplemental logging and CDC on the `CUSTOMERS` and `ORDERS` base tables, then recreate the join logic in your destination or transformation layer. If you have a materialized view `MV_DAILY_SALES` that aggregates from `SALES` table, capture the `SALES` table and perform the aggregation downstream. * Archive log mode must be enabled (not enabled by default in many deployments) * Supplemental logging overhead on write performance * LogMiner has performance impact on high-transaction databases * Large transactions can cause memory pressure in LogMiner sessions * Redo log space management critical for continuous CDC * Schema changes may require connector restart to refresh table metadata * Long-running transactions can delay change event visibility * Oracle RAC requires all nodes to have archive logs accessible Captures deletes as events with before-images, provided supplemental logging is enabled with ALL COLUMNS. Without proper supplemental logging, delete events may only contain primary key values. Encrypted connections (SSL/TLS), Oracle wallet for credential management, role-based access, IP allowlisting, support for Oracle Network Encryption. Oracle has a background process called the "Log Writer" or LGWR for short. Logs are written to an in-memory buffer first, and then the LGWR writes them to disk. The Connector needs to keep track of the last recorded system change number (SCN) that the LGWR process records for each committed transaction. It's best to persist that SCN somewhere outside of the Connector so it creates a very small, 1 column table named `LOG_MINING_TABLE`. If the Connector fails, it can use the last recorded value from that table to recover. We recommend it because the supplemental logging configuration increases the amount of data in the redo logs, data that's required for tracking changes to your data, including its schema. We would also recommend for databases with low traffic to also enable the heartbeats feature. Connect to the database and run this script to list the available container (CDB) and pluggable database (PDB) names. ```sql theme={null} SELECT NAME, CDB, CON_ID FROM V$DATABASE ORDER BY CON_ID; ``` For the Container database names, the `CDB` column should show `YES`. At this time, no. The Connector makes calls to LogMiner APIs from within the root database. Additionally, it consults several `V$` tables which are not available from within a PDB. If you use any database tools such as Oracle SQL Developer to interact with the database, you'll find them in the connection configuration you have saved in those tools. Alternatively, you can find them in the AWS account: 1. Sign into your AWS account 2. Once signed in, navigate to the **RDS** dashboard by clicking on **Services** in the top left corner, **Databases** and then **RDS** or by typing **RDS** into the top left search box 3. From the AWS RDS Dashboard, click on **DB Instances** or **Databases** in the left side menu 4. Click on the **DB identifier** for the Oracle database you want Streamkap to use. The **Database Details** page should appear 5. Under the **Connectivity & security** section you will find **Endpoint & port** No, they are mostly independent of each other. * `archivelog retention hours` determines how long the logs are retained **locally** in the database storage * AWS automated backups retention period determines how long the logs are retained by AWS **outside** of the database storage When archive logs have existed for longer than the `archivelog retention hours`, they are removed from the database storage and then retained by AWS - outside of the database storage - for the AWS automated backups retention period. The AWS automated backups are there so you can recover your database in the event of a disaster. The retention period doesn't impact the Streamkap Connector, only the `archivelog retention hours` does. * **CDC Not Working**: Verify archive log mode enabled; check supplemental logging at database and table levels * **Redo Log Buildup**: Enable heartbeats; monitor archive log destination space; adjust retention * **Missing Events**: Ensure supplemental logging includes ALL COLUMNS; verify LogMiner privileges * **Performance Issues**: Monitor LogMiner session resource usage; limit captured tables; tune redo log size * **Connection Failures**: Check listener status, TNS configuration, firewall rules * **Schema Changes Not Reflected**: Restart connector to refresh metadata after DDL operations For large database instances with many databases or tables, schema history can impact performance. Learn about optimization settings that can reduce schema history topic size and improve connector startup times. See the [Schema History Optimization](/schema-history-optimization) guide for detailed information on when and how to use these settings. * Use dedicated CDC user with minimal required privileges (LogMiner, SELECT on tables) * Enable supplemental logging with ALL COLUMNS for complete change records * Ensure archive log mode is enabled and monitored * Set appropriate archive log retention (3-5 days minimum) * Limit capture to needed schemas/tables to reduce LogMiner overhead * Monitor redo log generation rate and space * Test schema evolution procedures in staging * For cloud: Use provider monitoring; enable heartbeats for low traffic * Document supplemental logging configuration for team reference * Consider impact of supplemental logging on write-heavy workloads Database version upgrades require careful planning to avoid data loss and minimize downtime for your CDC pipelines. See our [Database Upgrade Guide](/database-upgrade-guide) for step-by-step instructions. # Organisation Security Source: https://docs.streamkap.com/organisation-security Configure organization-wide security policies and settings Organisation Security settings can be found in **Account preferences** under the **Workspace** section. Click your avatar in the top navigation bar and select **Account preferences**, then click **Security**. Organisation Security dashboard showing security score and recommendations ## Security Features The Security dashboard provides: * **Security Score** - An overview of your organization's security posture with recommendations * **MFA Settings** - Enable, disable, or force MFA for all users * **User Lockout** - Configure lockout parameters after failed login attempts * **Password History** - Set password history requirements * **Session Management** - Configure idle session timeout and maximum concurrent sessions * **Re-login Period** - Set time period to force users to re-authenticate * **IP Restrictions** - Limit access to specific IP addresses * **Domain Restrictions** - Limit access to specific email domains ## Security Check Up The Security check up section provides quick access to common security configurations: * **MFA** - View users without MFA enabled * **Sessions** - Manage session settings * **Password** - Configure password policies * **Inactivity** - Monitor inactive users * **Restrictions** - Manage IP and domain restrictions # Performance Tuning Source: https://docs.streamkap.com/performance-tuning Optimize pipeline throughput and latency for your CDC workloads Change Data Capture (CDC) pipeline performance depends on three stages: how fast the source produces change events, how topic partitioning distributes load across parallel consumers, and how quickly the destination writes data. Tuning each stage helps you achieve lower latency and higher throughput across your pipelines. This guide covers the key parameters, source-side optimizations, topic partitioning, destination-side optimizations, monitoring strategies, and common performance issues. ## Key Tuning Parameters The following parameters have the most impact on pipeline throughput. Adjust them incrementally and monitor the results before making further changes. | Parameter | Description | Default | Where to Configure | | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------- | ------------------------------------ | | **Tasks** | Number of parallel tasks for the destination connector. More tasks enable concurrent writes across partitions. | Varies by destination | Destination connector settings | | **Maximum poll records** | Number of records fetched per poll cycle from Kafka. Higher values improve throughput for high-volume topics. | Varies by destination | Destination Advanced settings | | **Topic partition count** | Number of partitions per topic. More partitions enable greater parallelism for both producers and consumers. | Configured at source | [Topics](/topics) page, Settings tab | | **Poll interval** (`poll.interval.ms`) | Time between poll cycles. Lower values reduce latency but increase CPU usage. Higher values batch more records per poll. | Varies by destination | Destination Advanced settings | | **Fetch minimum bytes** (`fetch.min.bytes`) | Minimum amount of data the consumer fetches per request from Kafka. Higher values allow the broker to accumulate more data before responding, improving throughput at the cost of latency. | `1` byte | Destination Advanced settings | | **Fetch maximum wait** (`fetch.max.wait.ms`) | Maximum time the broker waits to accumulate `fetch.min.bytes` before responding. Lower values reduce latency; higher values allow more batching. | `500` ms | Destination Advanced settings | Make performance tuning changes one at a time and monitor the impact for a meaningful period before making additional adjustments. Changing multiple parameters simultaneously makes it difficult to attribute improvements or regressions to a specific change. Setting **Maximum poll records** to very high values (e.g., 100,000+) can increase memory consumption on the connector. If you observe out-of-memory errors or connector instability after increasing this value, reduce it incrementally until stable. ## Source-Side Optimization Source connectors read change events from your database's transaction log (Write-Ahead Log / WAL, binlog, redo log, or change stream). The following factors influence how efficiently the source produces events. ### Snapshot Performance Initial snapshots and backfills are typically the most resource-intensive operations for a source connector. The duration and load depend on several factors: | Factor | Impact | Guidance | | -------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | | **Table size (row count)** | Larger tables take proportionally longer | For tables with hundreds of millions of rows, expect snapshots to run for hours | | **Row width** | Wide rows with large text/blob columns increase per-chunk processing time | Narrow tables snapshot faster than tables with many columns or large payloads | | **Source database load** | High concurrent query load slows snapshot reads | Schedule snapshots during off-peak hours when possible | | **Index availability** | Snapshots read data in primary key order | Ensure primary keys are well-indexed on source tables | | **Number of tables** | Tables are snapshotted sequentially | Prioritize critical tables using table-level snapshots | For very large tables, consider using **Filtered (Partial) snapshots** to backfill data in manageable ranges rather than snapshotting the entire table at once. This reduces load on your source database and gives you more control over the process. See [Snapshots & Backfilling](/snapshots) for details. ### Heartbeat Configuration For databases with low traffic or intermittent activity, configure heartbeats to keep the connector's offset position fresh. Without heartbeats, connectors tracking quiet tables may lose their place in the transaction log when the log rotates or expires. Streamkap provides two layers of heartbeat protection: 1. **Connector heartbeats** (enabled by default) -- The connector periodically emits heartbeat messages to an internal topic, even when no data changes are detected. 2. **Source database heartbeats** (recommended for all deployments) -- Regular updates to a dedicated heartbeat table in the source database simulate activity and maintain log progress. See [Heartbeat Configuration](/heartbeat-configuration) for setup instructions specific to your source type. ### WAL / Binlog Retention Ensure your source database retains its transaction log long enough for the connector to read it. If the log is rotated or purged before the connector processes it, the connector will lose its position and require a new snapshot. * **PostgreSQL**: Set `wal_level = logical` and configure a sufficient `max_slot_wal_keep_size` or replication slot retention. * **MySQL / MariaDB**: Configure `binlog_expire_logs_seconds` (or `expire_logs_days`) to retain binlogs for at least 3-7 days. * **Oracle**: Configure redo log and archive log retention to cover at least several days of changes. If your source connector falls behind and the transaction log is purged, you will need to trigger a new snapshot to recover. Monitor connector lag and source log retention to prevent this situation. ## Topic Partitioning Topic partitions control how data is distributed across parallel consumers. Increasing the partition count is the primary way to scale throughput for both streaming changes and snapshots/backfills. ### How Partitions Affect Throughput Each topic partition can be consumed by one task at a time. The maximum effective parallelism for a destination connector equals the number of partitions in the topic. * **1 partition** = 1 task can consume, all data processed serially * **5 partitions** = up to 5 tasks consuming in parallel * **Tasks beyond the partition count** remain idle When you increase partitions, you must also increase destination **Tasks** to match — otherwise the additional partitions will not be consumed in parallel. ### When to Increase Partitions * **High consumer lag**: The destination cannot keep up with a single partition's throughput * **Scaling destination writes**: Enable multiple tasks to write in parallel * **Faster snapshots and backfills**: More partitions allow the destination to ingest snapshot data in parallel, significantly reducing backfill duration for large tables ### Partition Increase Procedure 1. **Increase topic partitions** on the [Topics](/topics) page Settings tab (e.g., to `5`, `8`, `16`, or `32` depending on throughput needs). Follow the [safe partition increase procedure](/topics#partition-management). 2. **Increase destination tasks** to match the new partition count in the destination connector settings. 3. **Monitor** consumer lag on the [Consumer Groups](/consumer-groups) page to confirm the additional parallelism is effective. Increasing partitions alone is not sufficient — you must also increase the destination **Tasks** to match. Without matching tasks, the additional partitions will not be consumed in parallel. For snapshots specifically, partition and task increases should be made **before** triggering the snapshot. Snapshot read speed is also bounded by the source database — if your source is under heavy load or has slow disk I/O, increasing partitions and tasks on the Streamkap side will not help beyond the source's throughput limit. See [Topics — Partition Management](/topics#partition-management) for the full safe procedure and considerations. ## Destination-Side Optimization Destination connectors consume messages from Kafka and write them to your target system. The primary levers for improving destination throughput are parallelism (tasks), polling efficiency (maximum poll records), and partition count. All destination-side tuning applies to both streaming CDC changes and snapshot/backfill ingestion. **Applies to:** Snowflake, BigQuery, Redshift, Databricks Data warehouses are optimized for batch operations. Key tuning considerations: * **Tasks**: Increase the number of tasks to enable parallel writes across partitions. Each task handles one or more partitions independently. * **Maximum poll records**: Increase to `25000`, `50000`, or `80000` depending on record sizes. Larger poll batches reduce the overhead of frequent, small writes. * **Topic partitions**: Increase partition count to at least `5` for high-throughput topics to enable greater parallelism. **Warehouse-specific considerations:** * **Snowflake (Append mode)**: Uses Snowpipe Streaming for ingestion. Warehouse compute is only needed for Dynamic Tables, QA, and tasks. Set `AUTO_SUSPEND` to 60 seconds or higher for active CDC pipelines to avoid frequent suspend/resume cycles. * **Snowflake (Upsert mode)**: Requires a running warehouse for periodic `MERGE INTO` operations. Size the warehouse to match your ingestion throughput. * **BigQuery**: Ensure the service account has sufficient quota for streaming inserts or load jobs in your target dataset region. * **Redshift**: Match task count to your cluster's write capacity. Monitor WLM queue depth and query throughput. * **Databricks**: Data is staged as Parquet files and loaded via `COPY`. Ensure the `tmp` directory on DBFS has sufficient space and the cluster is sized for your ingestion rate. **Applies to:** PostgreSQL, MySQL, Oracle, SQL Server (as destinations) JDBC destinations write records using standard database operations (INSERT, UPDATE, DELETE). Key tuning considerations: * **Tasks**: Increase to enable parallel writes. Each task maintains its own database connection. * **Maximum poll records**: Increase to batch more records per write operation, reducing round-trip overhead. * **Topic partitions**: Match or exceed the task count so each task has at least one partition to process. **Connection considerations:** * Ensure your destination database has sufficient connection pool capacity to handle the configured number of tasks. * Monitor destination database CPU and I/O to ensure writes are not bottlenecked by the target system. * For upsert operations, ensure primary key indexes are optimized on destination tables. **Applies to:** S3, Azure Blob Storage, Cloudflare R2 Object storage destinations write data as files (Parquet, JSON, CSV). Key tuning considerations: * **Tasks**: Increase to enable parallel file writes across partitions. * **File sizing**: More partitions produce more files in parallel but each file may be smaller. Balance between write parallelism and downstream query performance. * **Compression**: Choose compression type (gzip, snappy, zstd) based on your downstream query patterns. Snappy offers faster compression/decompression; gzip and zstd offer better compression ratios. * **Format**: Parquet is generally recommended for analytical workloads due to columnar storage and efficient compression. **Applies to:** ClickHouse Cloud ClickHouse uses the `ReplacingMergeTree` engine for upsert mode, which deduplicates data during background merges rather than at write time. Key tuning considerations: * **Tasks**: Increase to enable parallel writes. ClickHouse handles concurrent inserts well. * **Maximum poll records**: Increase to `25000`, `50000`, or `80000` depending on record sizes. Larger batches improve insert throughput. * **Topic partitions**: Increase to at least `5` for high-throughput topics. **ClickHouse-specific considerations:** * Background merges handle deduplication automatically, but queries may return duplicates before merges complete. Use the `FINAL` query modifier or set `final = 1` on the querying role to ensure deduplicated results. * Monitor ClickHouse merge activity to ensure background merges keep up with insert volume. * For high-throughput workloads, ensure the ClickHouse service tier provides sufficient CPU and memory for both inserts and merges. **Applies to:** Apache Iceberg (AWS Glue, Generic) Iceberg destinations write data as files to object storage with metadata management. Key tuning considerations: * **Tasks**: Increase to enable parallel writes across partitions. * **File sizing and compaction**: More partitions produce more small files. Plan for periodic compaction to consolidate small files and maintain query performance. * **Topic partitions**: Balance between write parallelism and the number of small files generated. ## Benchmarking with Synthetic Data When you load-test a pipeline with generated data, the **shape of each row matters as much as the number of rows**. Every change event carries a fixed set of Streamkap [metadata columns](/metadata) (`_streamkap_ts_ms`, `_streamkap_source_ts_ms`, `_streamkap_offset`, and so on) plus Kafka record headers. That overhead is roughly constant per record regardless of how much of your own data the row contains. This means a synthetic table with only a couple of narrow columns is dominated by metadata: most of the bytes on the wire are Streamkap's, not yours. The pipeline hits a **records-per-second** ceiling while **MB/second stays low**, which does not reflect how the same pipeline behaves on production rows. Do not benchmark with minimal rows such as `(id, created_at)`. With so little payload, per-record metadata dominates and you measure the overhead, not the throughput. Reported MB/s will be far lower than production, and you may conclude the pipeline is slower than it actually is. ```sql theme={null} -- ❌ Understates throughput — payload is tiny, metadata dominates DO $do$ DECLARE t int; BEGIN FOR t IN 1..270 LOOP EXECUTE format( 'INSERT INTO bench.%I (id, created_at) SELECT g, now() FROM generate_series(70000000, 70099999) g', 'table_' || lpad(t::text, 3, '0')); END LOOP; END $do$; ``` Instead, generate rows whose width and column types resemble your real tables — a few `text`/`varchar` fields and a `jsonb` document are typical of production OLTP workloads. ```sql theme={null} -- ✅ Representative rows — a few text/jsonb columns per record DO $do$ DECLARE t int; BEGIN FOR t IN 1..270 LOOP EXECUTE format($ins$ INSERT INTO bench.%I (id, created_at, status, payload, notes) SELECT g, now(), (ARRAY['active','pending','closed'])[1 + (g %% 3)], jsonb_build_object( 'order_id', g, 'region', (ARRAY['us-east','us-west','eu-central'])[1 + (g %% 3)], 'items', g %% 50, 'amount', round((random() * 1000)::numeric, 2) ), repeat('lorem ipsum dolor sit amet ', 8) FROM generate_series(70000000, 70099999) g $ins$, 'table_' || lpad(t::text, 3, '0')); END LOOP; END $do$; ``` In our own Postgres testing, adding just a few `jsonb`/`string` columns to the synthetic rows raised measured throughput by roughly **5x in MB/second** over the two-column version — same record rate, far more useful data moved per record. Measure **Bytes In Per Topic** on the [Topics](/topics) page, not just record counts, and match the row width, column types, and insert/update mix to what you actually run in production. ### Commit in batches, not one large transaction CDC captures **committed** changes, so this applies to every source: a single giant transaction always arrives at the destination as one burst at commit time, never as the steady stream that production OLTP produces. Production databases almost always commit in **small, frequent transactions** — many short writes per second rather than one bulk load — which is exactly the pattern CDC streams smoothly, so a batched generator is also the more realistic benchmark. The buffering and spill happen in different places per dialect (details below), but the guidance is the same everywhere — **commit frequently**. PostgreSQL logical decoding (`pgoutput`) only emits a transaction's changes **after that transaction commits**. If your generator inserts everything inside a single transaction — and note that a `DO $do$ … $do$` block runs as **one transaction** — Streamkap sees nothing until the very end and then receives the entire load as one commit burst. That is the opposite of a steady CDC stream and makes latency and throughput readings meaningless. Real OLTP workloads are a continuous flow of small, frequently committed transactions. It also forces PostgreSQL to buffer the whole in-progress transaction for decoding. Once a transaction exceeds `logical_decoding_work_mem` (default `64MB`), the reorder buffer **spills to disk**. On a large bulk load this can reach tens of gigabytes — check the slot's spill counters: ```sql theme={null} SELECT slot_name, spill_txns, spill_count, spill_bytes FROM pg_stat_replication_slots WHERE slot_name = 'streamkap_slot'; slot_name | spill_txns | spill_count | spill_bytes ----------------+------------+-------------+------------- streamkap_slot | 646 | 1132 | 60103998520 -- ~60 GB spilled to disk ``` Non-zero, growing `spill_bytes` means decoding is disk-bound on your synthetic load — a benchmarking artifact, not something normal replication hits. The fix is to commit frequently so decoding streams incrementally, the way production does. A `DO` block can `COMMIT` inside the loop, giving each table (or each batch of rows) its own transaction: ```sql theme={null} -- ✅ One transaction per table — pgoutput streams as each COMMIT lands DO $do$ DECLARE t int; BEGIN FOR t IN 1..270 LOOP EXECUTE format( 'INSERT INTO bench.%I (id, created_at) SELECT g, now() FROM generate_series(%s, %s) g', 'table_' || lpad(t::text, 3, '0'), 70000000, 70099999); COMMIT; -- flush this table so Streamkap decodes and streams it immediately END LOOP; END $do$; ``` Apply the same per-table (or per-batch) `COMMIT` to the representative-row example above. Raising `logical_decoding_work_mem` reduces spill for large transactions, but it only shifts the cost to source memory — it does not make a giant single-transaction load look like production. For benchmarking, the right lever is smaller, more frequent commits, not a bigger decoding buffer. **Other source types.** The commit-burst behavior is universal, but the buffering differs: * **MySQL / MariaDB** -- The binlog contains only *committed* transactions, in commit order, so the connector never reads partial transactions. Instead, an open transaction accumulates in the per-session **binlog cache** (`binlog_cache_size`, small by default) and spills to a temporary file on disk once it grows past that, then the whole block is written to the binlog at `COMMIT`. The MySQL/MariaDB equivalent of `spill_bytes` is the status counter `Binlog_cache_disk_use` (versus `Binlog_cache_use`): check `SHOW GLOBAL STATUS LIKE 'Binlog_cache%';` -- a rising `Binlog_cache_disk_use` means your load is spilling on the source. * **Oracle** -- LogMiner exposes uncommitted changes, so the connector buffers each in-progress transaction until commit; a very large transaction drives connector memory pressure rather than source spill. * **SQL Server / MongoDB** -- Changes surface post-commit (CDC change tables / the oplog), so a large transaction lands as a single delayed burst just like the others. In every case the fix is the same: commit in representative batches so changes stream continuously. ## Monitoring Performance Effective performance tuning requires ongoing monitoring. Use the following Streamkap features to track pipeline health and identify bottlenecks. ### Consumer Group Lag Consumer lag is the most important metric for identifying throughput issues. It represents how far behind the destination connector is from the latest message in a topic. * **Zero lag**: The destination is fully caught up with the source. * **Steady, low lag**: Normal operating state for active pipelines. * **Increasing lag**: The destination cannot keep up with the source's event production rate. This is the primary signal that tuning is needed. Monitor consumer lag on the [Consumer Groups](/consumer-groups) page. Check the **Total Lag** metric and drill into partition-level lag to identify uneven distribution. ### Topic Throughput The [Topics](/topics) page displays volume, events, and error metrics for each topic. Use the metrics chart to track throughput over time and identify patterns: * **Volume spikes**: Correlate with snapshot activity or source database bulk operations. * **Error spikes**: Investigate DLQ topics for failed messages. * **Latency increases**: May indicate destination bottlenecks or Kafka broker pressure. ### Pipeline Metrics The [Pipelines](/pipelines) page provides pipeline-level latency and lag metrics: * **Latency**: Time for data to flow from source through the pipeline to the destination. Values under 1 second indicate healthy performance. * **Lag**: Number of records waiting to be processed. Zero means the pipeline is fully caught up. Increased latency and lag are expected when snapshots are running. Backfilling produces a much higher load than normal CDC streaming, but the load is temporary while backfills are in progress. ## Common Performance Issues **Symptoms:** Consumer lag is consistently increasing over time and not reducing. **Possible causes and remediation:** 1. **Destination bottleneck**: The destination cannot write fast enough to keep up with the source. * Increase the number of **Tasks** on the destination connector. * Increase **Maximum poll records** in the destination's Advanced settings (try `25000`, `50000`, or `80000`). * Increase **topic partition count** to at least `5` to enable greater parallelism. 2. **Under-provisioned destination**: The target system lacks sufficient compute or I/O capacity. * For data warehouses, increase warehouse/cluster size. * For JDBC databases, check CPU, memory, and connection pool utilization. * For ClickHouse, verify the service tier provides adequate resources for inserts and merges. 3. **Insufficient parallelism**: The number of tasks is lower than the number of partitions, or there is only one partition. * Increase partition count, then increase tasks to match. The maximum useful number of tasks equals the number of topic partitions. Additional tasks beyond the partition count will remain idle. **Symptoms:** Snapshot is taking much longer than expected to complete. **Possible causes and remediation:** 1. **Large tables**: Tables with hundreds of millions of rows will naturally take hours to snapshot. This is expected behavior. 2. **Source database under heavy load**: Competing queries slow down snapshot reads. * Schedule snapshots during off-peak hours. * Use filtered (partial) snapshots to process smaller data ranges. 3. **Missing or fragmented indexes**: Snapshots read data in primary key order. Poorly indexed tables are slower to read. * Ensure primary keys are well-indexed and consider running `ANALYZE` or `REINDEX` on the source database. 4. **Network latency**: Cross-region connections between Streamkap and the source database add round-trip time per chunk. See [Snapshots & Backfilling](/snapshots) for detailed guidance on planning and estimating snapshot duration. **Symptoms:** Pipeline shows errors or broken status. Logs indicate write timeouts or connection failures to the destination. **Possible causes and remediation:** 1. **Destination overloaded**: The target system cannot handle the write volume. * Reduce the number of tasks temporarily to lower concurrent write pressure. * For data warehouses, increase warehouse/cluster capacity. 2. **Network issues**: Transient connectivity problems between Streamkap and the destination. * Check firewall rules and network policies. * Verify the destination is accessible from [Streamkap IP addresses](/streamkap-ip-addresses). 3. **Large records**: Individual records that are very large (e.g., containing large text or blob fields) may exceed timeout thresholds. * Consider excluding large columns from replication if they are not needed at the destination. **Symptoms:** Some partitions have high lag while others are at zero. Overall throughput is limited by the slowest partition. **Possible causes and remediation:** 1. **Partition count lower than task count**: Some tasks are idle while others are overloaded. * Increase the partition count to match or exceed the task count. Follow the [safe partition increase procedure](/topics#partition-management). 2. **Hot partitions**: Certain keys produce disproportionate traffic, creating uneven load across partitions. * Monitor partition-level lag on the [Consumer Groups](/consumer-groups) page to identify imbalanced partitions. 3. **Recent partition increase**: After increasing partitions, new data goes to new partitions but existing lag remains on old partitions until fully processed. Increasing partitions only affects new data. Existing lag on old partitions will persist until those messages are fully consumed. ## Tuning Workflow Follow this workflow when optimizing pipeline performance: Check [Consumer Groups](/consumer-groups) for lag, [Topics](/topics) for throughput metrics, and [Pipelines](/pipelines) for latency. Determine whether the bottleneck is on the source side, Kafka layer, or destination side. In the destination connector's Advanced settings, increase **Maximum poll records** to `25000`, `50000`, or `80000` depending on your record sizes. This is the lowest-risk change and often provides immediate improvement. On the [Topics](/topics) page, increase the partition count to at least `5` for high-throughput topics. Follow the [safe partition increase procedure](/topics#partition-management) to avoid data consistency issues. In the destination connector settings, increase **Tasks** to match the partition count. More tasks enable more parallel consumers and writers. After each change, monitor consumer lag, latency, and destination health for a meaningful period before making further adjustments. If you have exhausted the tuning options above and performance is still not meeting your requirements, contact [Streamkap support](mailto:support@streamkap.com) for advanced optimization assistance. Include your pipeline ID, current settings, and the metrics you are observing. ## Sizing by Use Case Different workloads have different performance characteristics. Use the recommendations below as starting points and adjust based on monitoring. **Goal:** Maximize the number of records processed per second. * Increase **Maximum poll records** to `50000` or `80000` * Increase **Topic partition count** to `5` or higher * Increase **Tasks** to match the partition count * Set **`fetch.min.bytes`** to `100000` (100 KB) or higher to allow the broker to batch more data per fetch * Set **`fetch.max.wait.ms`** to `1000` ms or higher to give the broker time to accumulate larger batches * Use a destination tier with sufficient write capacity (larger warehouse, higher IOPS database, etc.) **Goal:** Minimize the time between a source change and its arrival at the destination. * Keep **Maximum poll records** moderate (`5000`--`10000`) to avoid waiting for large batches to fill * Set **`fetch.min.bytes`** to `1` (the default) so the broker responds immediately with any available data * Set **`fetch.max.wait.ms`** to `100`--`250` ms to reduce broker-side wait time * Set **Poll interval** (`poll.interval.ms`) to a low value to poll more frequently * Use enough **Tasks** and **partitions** to avoid queuing, but do not over-provision **Goal:** Handle records with large payloads (e.g., text columns, blobs, wide rows) without timeouts or memory issues. * Keep **Maximum poll records** lower (`5000`--`10000`) to reduce per-batch memory consumption * Monitor connector memory usage and reduce poll records further if you observe instability * Consider excluding very large columns from replication (using DropFields) if they are not needed at the destination * Ensure **Topic partition count** is sufficient to spread load across multiple tasks **Goal:** Efficiently process a high volume of small records (e.g., event logs, click streams). * Increase **Maximum poll records** to `50000` or `80000` to aggregate many small records per batch * Set **`fetch.min.bytes`** to `50000` (50 KB) or higher so the broker accumulates multiple small messages before responding * Set **`fetch.max.wait.ms`** to `500`--`1000` ms to balance batch size with acceptable latency * Increase **Topic partition count** and **Tasks** to handle the high event rate in parallel ## Related Documentation * [Topics](/topics) - Manage Kafka topics, partitions, and view throughput metrics * [Consumer Groups](/consumer-groups) - Monitor consumer lag and manage offset positions * [Pipelines](/pipelines) - Monitor pipeline health, latency, and lag * [Snapshots & Backfilling](/snapshots) - Plan and optimize snapshot operations # Personal Privacy & Security Source: https://docs.streamkap.com/personal-privacy-security Manage your personal security settings including password, MFA, passkeys, and sessions Personal Privacy & Security settings can be found in **Account preferences** under the **My Account** section. Click your avatar in the top navigation bar and select **Account preferences**, then click **Privacy & Security**. Privacy & Security settings showing password, passkeys, MFA options, and login sessions ## Features * **Change your password** - Update your account password * **Enrol/Set MFA/2FA** - Configure multi-factor authentication * **Setup Passkeys** - Add passwordless authentication * **View sessions** - See all active login sessions and log out of any or all of them * **Remember 2FA devices** - Option to remember trusted devices for 30 days ## Multi-Factor Authentication (MFA/2FA) 2FA or Multi-Factor Authentication adds an essential layer of security to protect your use of Streamkap. 2FA at Streamkap is mandatory for anyone not logging in via enterprise SSO. ### Supported MFA Methods * **Authenticator apps** - Google Authenticator, Authy, 1Password, etc. * **SMS** - Receive codes via text message * **Built-in authenticators** - Touch ID, Face ID, Windows Hello * **Security Keys** - Hardware keys like YubiKey ### Setting Up MFA 1. Navigate to **Account preferences** → **Privacy & Security** 2. Under "Multi-Factor Authentication", click **Set up** on your preferred method 3. Follow the setup wizard to complete configuration 4. Your chosen method will show as "Enabled" once configured We recommend using an authenticator app as your primary MFA method, as it's more secure than SMS and doesn't require internet connectivity. ## Login Sessions The Login Sessions section shows all devices and browsers where your account is currently logged in: * **Device** - Operating system and browser * **Location** - Geographic location based on IP address * **IP Address** - The IP address used for the session * **Time** - When the session was created ### Managing Sessions * **Log out individual session** - Click the logout button next to any session * **Log out all other sessions** - Click "Log out all other sessions" to revoke all sessions except your current one If you see any sessions you don't recognize, log them out immediately and change your password. ## Related Documentation * [Organisation Security](/organisation-security) - Organization-wide security settings * [Users](/team-management) - Team management and access control * [SSO Setup](/setup-sso) - Single Sign-On configuration # Pinecone Source: https://docs.streamkap.com/pinecone Stream data from Kafka topics into Pinecone vector indexes in real-time ## Overview Use the Pinecone Sink Destination to stream data from your Kafka topics into Pinecone indexes. This connector is useful for building real-time vector search applications, powering recommendation systems, and maintaining synchronized vector embeddings. Since Pinecone requires pre-computed vectors, you must provide embeddings in your Kafka records using the **Field Vector Strategy**. ## Prerequisites * A Pinecone account with an existing index * Pinecone API key * The name of your target Pinecone index (must be pre-created) * Pre-computed embedding vectors in your Kafka records * Understanding of your ID strategy (see below) Unlike some vector databases, Pinecone does **not** generate embeddings automatically. You must provide pre-computed vectors in your Kafka records using the **Field Vector Strategy**. ## Key Concepts ### Indexes & Namespaces Pinecone organizes vectors into **indexes**, and each index can contain multiple **namespaces**. Each Kafka topic is mapped to a Pinecone namespace within your configured index. By default, a Kafka topic named `users` maps to a namespace named `users`. You can customize this mapping using the **Namespace Mapping** setting. ### Document IDs & Upsert Operations Pinecone uses string-based IDs to uniquely identify vectors. The connector supports multiple ID strategies: * **NoIdStrategy** (default) – Generates a new UUID for each record, always creating new vectors (INSERT semantics) * **FieldIdStrategy** – Uses a field from your Kafka record as the vector ID, enabling upserts * **KafkaIdStrategy** – Uses the Kafka message key as the vector ID When using **FieldIdStrategy**, specify the field name (e.g., `id`, `user_id`) in the **Document ID Field** setting. When using **KafkaIdStrategy** or **FieldIdStrategy**, the connector generates a deterministic UUID from the key or field value, enabling idempotent upserts. ### Vectors (Bring Your Own Vectors) Pinecone requires vectors to be provided with each record. Use the **Field Vector Strategy** and specify the field in your Kafka records that contains the embedding vector. The connector supports multiple vector input formats: * Direct float arrays * Lists/collections of numbers * JSON string format (e.g., `"[0.1, 0.2, 0.3]"`) The **No Vector Strategy** is not supported for Pinecone. You must configure **Field Vector Strategy** and specify a vector field. ### Metadata All non-vector fields from your Kafka records are automatically stored as Pinecone metadata. This metadata can be used for filtering during queries. Pinecone metadata is schemaless — any field types are accepted, including nested structures, arrays, and maps. ### Delete Operations If **Delete Enabled** is set to true, records with null values or a `__deleted=true` field are treated as deletes. For example, when a record is deleted in the source and a tombstone record is sent to Kafka, the connector will delete the corresponding vector from Pinecone. Delete operations require **KafkaIdStrategy** to be configured as the Document ID Strategy. ## Pinecone Setup Before configuring the connector, prepare your Pinecone environment: ### Create an Index Pinecone indexes must be created before streaming data. Create an index via the Pinecone console or API: ```python theme={null} from pinecone import Pinecone pc = Pinecone(api_key="your-api-key") # Create a serverless index pc.create_index( name="my-index", dimension=1536, # Must match your embedding dimensions metric="cosine", # Options: cosine, euclidean, dotproduct spec=ServerlessSpec( cloud="aws", region="us-east-1" ) ) ``` The index **dimension** must match the dimensionality of the embedding vectors in your Kafka records. A mismatch will cause upsert failures. ### Gather Connection Details * **API Key:** Available in your Pinecone console under API Keys * **Index Name:** The name of the index you created ### Prepare Embedding Vectors Ensure your Kafka records contain pre-computed embedding vectors. Common embedding sources include: * OpenAI Embeddings API * Cohere Embed API * Sentence Transformers * Custom ML models ## Streamkap Setup 1. Navigate to [Destinations](https://app.streamkap.com/connectors/add?tab=Destinations) and choose **Pinecone**. 2. Fill in the fields: 1. **Name** – A memorable identifier for this Destination. 2. **Pinecone API Key** – Your Pinecone API key for authentication. 3. **Pinecone Index Name** – The name of your pre-created Pinecone index. 4. **Namespace Mapping** – Pattern to map Kafka topics to Pinecone namespaces. Default: `${topic}`. Examples: * `${topic}` – Topic `users` → Namespace `users` * `pinecone_${topic}` – Topic `users` → Namespace `pinecone_users` * `prod_${topic}_v2` – Topic `users` → Namespace `prod_users_v2` 5. **Document ID Strategy** – Choose how to assign IDs to vectors: * **No ID Strategy** – Generate new UUID for each record (always inserts) * **Field ID Strategy** – Use a field from the record as the vector ID * **Kafka ID Strategy** – Use the Kafka message key as the vector ID 6. **Document ID Field** (if Field ID Strategy selected) – The field name containing the ID (e.g., `id`, `user_id`). This field must exist in your Kafka records. 7. **Vector Field Name** – The field in your Kafka records containing the embedding vector (e.g., `embedding`, `vector`). Must be an array of numbers. 8. **Delete Enabled** – If true, null-valued records and records with `__deleted=true` are treated as deletes. Requires **Kafka ID Strategy**. 9. **Schema Evolution** (default: `basic`) – Controls automatic namespace management: * **basic (default)** – Namespaces are automatically created on first upsert. * **none** – Namespaces must exist before the connector starts. Use this for strict control in production. 10. **Batch Size** – Number of vectors to batch before sending to Pinecone (default: 100). Larger batches improve throughput; smaller batches reduce latency. 11. **Max Retries** – Maximum retry attempts on connection/timeout errors (default: 3). 12. **Retry Interval (ms)** – Delay between retries in milliseconds (default: 1000). 3. Click **Save**. ## How It Works 1. **Record Ingestion** – Records from Kafka are received by the connector and grouped by namespace. 2. **Deduplication** – Records are deduplicated by document ID within each batch to avoid redundant upserts. 3. **ID Generation** – Based on the ID strategy, a vector ID is assigned or extracted. 4. **Vector Extraction** – The embedding vector is extracted from the configured vector field. 5. **Metadata Extraction** – All non-vector fields are converted to Pinecone metadata (protobuf Struct format). 6. **Batching** – Vectors are accumulated and flushed when the **Batch Size** is reached or on explicit flush. 7. **Upsert to Pinecone** – The batch is sent to Pinecone for upsert. 8. **Delete Handling** – If enabled, null records or `__deleted=true` records trigger vector deletion by ID. 9. **Error Handling** – Failed batches are retried up to **Max Retries** times with backoff. If error unrolling is enabled, individual records from a failed batch are retried separately and routed to a dead letter queue on failure. ## Limitations & Best Practices ### Limitations * **Index Must Be Pre-Created** – Pinecone indexes cannot be created by the connector. You must create the index in Pinecone before starting the connector. * **Vectors Are Required** – Pinecone does not auto-generate embeddings. Every record must contain a vector field with a pre-computed embedding. * **Vector Dimensions Must Match** – The dimensionality of vectors in your Kafka records must match the index dimension configured in Pinecone. * **Delete Requires KafkaIdStrategy** – Delete operations only work when using **Kafka ID Strategy** as the document ID strategy. * **Single Index per Connector** – Each connector instance writes to a single Pinecone index. Use multiple connectors for multiple indexes. ### Best Practices 1. **Use Field ID or Kafka ID Strategy for Idempotency** – If your source has unique identifiers, use them as vector IDs to enable idempotent upserts and avoid duplicate vectors. 2. **Pre-compute High-Quality Embeddings** – Since Pinecone doesn't generate embeddings, ensure your embedding pipeline produces consistent, high-quality vectors before they reach Kafka. 3. **Match Vector Dimensions** – Double-check that your embedding model output dimensions match your Pinecone index dimensions (e.g., OpenAI `text-embedding-3-small` = 1536 dimensions). 4. **Use Namespaces for Data Isolation** – Leverage namespace mapping to organize data by topic, environment, or tenant within a single index. 5. **Tune Batch Size for Throughput** – Start with the default batch size of 100 and increase for higher throughput workloads. Monitor Pinecone's rate limits. 6. **Enable Deletion for CDC Pipelines** – If your source is a database with CDC, enable **Delete Enabled** with **Kafka ID Strategy** to propagate deletes. 7. **Monitor Pinecone Quotas** – Keep track of your Pinecone plan's vector count and storage limits to avoid hitting quota errors. ## Troubleshooting ### Namespace Not Found Error **Problem:** Connector fails with a namespace validation error. **Solution:** * This occurs when **Schema Evolution** is set to `none` and the namespace doesn't exist yet. Switch to `basic` to allow automatic namespace creation. * Alternatively, upsert at least one vector into the namespace manually via the Pinecone API before starting the connector. ### Vector Dimension Mismatch **Problem:** Upsert fails with a dimension mismatch error. **Solution:** * Verify that the embedding vectors in your Kafka records have the same dimensionality as your Pinecone index. * Check your embedding model configuration — different models produce different dimensions (e.g., OpenAI `text-embedding-3-small` = 1536, `text-embedding-3-large` = 3072). * Ensure the **Vector Field Name** points to the correct field in your records. ### Authentication Failures **Problem:** "Unauthorized" or "Invalid API key" errors. **Solution:** * Verify your Pinecone API key is correct and active. * Ensure the API key has permissions to write to the target index. * Check that the API key matches the correct Pinecone project and environment. ### Missing Vectors Error **Problem:** Connector fails with an error about missing or null vectors. **Solution:** * Pinecone requires every record to have a vector. Ensure the **Vector Field Name** is correctly configured. * Verify that your Kafka records contain the vector field and it is not null. * Check the vector format — it must be an array of numbers, a list of numbers, or a JSON string like `"[0.1, 0.2, 0.3]"`. ### High Latency or Rate Limiting **Problem:** Connector is slow or receiving rate limit errors from Pinecone. **Solution:** * Reduce **Batch Size** if you're hitting Pinecone rate limits. * Increase **Retry Interval** to allow more time between retries. * Check your Pinecone plan's rate limits and upgrade if needed. * Monitor the Pinecone dashboard for throttling indicators. ### Delete Operations Not Working **Problem:** Deleted source records are not being removed from Pinecone. **Solution:** * Ensure **Delete Enabled** is set to true. * Delete operations require **Kafka ID Strategy** — verify this is configured as your Document ID Strategy. * Confirm that tombstone records (null values) are being produced to Kafka by your source connector. ## Security Notes * **API Keys** – Stored encrypted. Never share them in logs or config files shared publicly. * **HTTPS** – All communication with Pinecone uses HTTPS by default. * **Metadata** – Be mindful of sensitive data stored as Pinecone metadata, as it is accessible via query results. ## Next Steps 1. Create a Pinecone index matching your embedding dimensions 2. Ensure your source pipeline produces records with embedding vectors 3. Test the connector with a small Kafka topic first 4. Monitor upsert throughput and error rates in the Pinecone dashboard 5. Adjust batch size based on observed performance and rate limits 6. Set up alerts for connector task failures # Pipeline Recovery Procedures Source: https://docs.streamkap.com/pipeline-recovery Troubleshoot and recover Streamkap pipelines using structured decision trees, step-by-step procedures, and scenario-based guidance. This guide helps you diagnose and resolve pipeline issues in Streamkap. Use it when a pipeline is in a Broken state, data has stopped flowing, or you observe data discrepancies between source and destination. For routine pipeline management, see [Pipelines](/pipelines). For proactive monitoring, see [Alerts](/alerts). ## Recovery Decision Tree Use the following flow to determine the correct recovery action for your situation. ### Pipeline is in Broken status 1. Go to the [Logs](/logs) page and filter by the affected connector at the **ERROR** level 2. Identify the error type: * **Connection error** (network timeout, authentication failure) — fix connectivity, then restart the connector via the pipeline actions menu * **Schema error** (type mismatch, missing column) — fix the schema at source or destination, then monitor for recovery * **Permission error** (access denied, insufficient privileges) — grant required permissions, then restart the connector * **Resource error** (disk full, memory exhaustion) — free resources or scale infrastructure, then restart 3. If the error persists after restarting, consider a stop and resume cycle or contact [Streamkap support](mailto:support@streamkap.com) ### Pipeline is running but data is not flowing 1. Check the pipeline's **Lag** metric — if lag is increasing, the source is producing but the destination is not consuming 2. Check the [Consumer Groups](/consumer-groups) page for the destination's consumer group: * Is the consumer group in `STABLE` state with active members? * Is consumer lag growing or static? 3. Check the source connector status — is it Active or Broken? 4. Check the [Logs](/logs) page for WARN or ERROR messages from either connector 5. If the source connector is healthy but the destination is not consuming, stop and resume the destination connector ### Data at destination does not match source 1. Check the [DLQ (Dead Letter Queue)](/dlq-operations) for failed messages — schema mismatches or constraint violations can cause records to be diverted 2. Review the [Logs](/logs) for any processing errors or warnings 3. If specific records are missing, consider a [filtered (partial) snapshot](/snapshots#snapshot-options) for the affected time range 4. If broad data discrepancy is detected, consider a [full snapshot](/snapshots) of the affected tables 5. For Snowflake destinations in append mode, verify that both consumer group offsets and Snowpipe Streaming channel offsets are aligned — see [Snowflake Offset Management](/snowflake#offset-management-append-mode) ## Recovery Actions The following table summarizes the available recovery actions, when to use each, and their impact on data. | Action | What It Does | When to Use | Data Impact | | -------------------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ | | **Restart connector** | Stops and immediately restarts the source or destination connector | Transient errors, connection timeouts, minor configuration changes | None — resumes from last committed offset | | **Stop + Resume** | Manually stops a connector, then resumes it after a pause | Persistent errors that need time to resolve (e.g., waiting for infrastructure fixes) | None — resumes from last committed offset | | **Reset consumer group offsets** | Changes the consumer group's offset position (earliest, latest, specific timestamp, or specific offset) | Need to replay messages, skip problematic records, or recover from a known-good position | May cause data duplication (if reset to earlier offset) or data loss (if reset to later offset) | | **Snapshot** | Triggers a full or filtered snapshot to backfill historical data from source | Missing data at destination, post-schema-change backfill, initial data load for new tables | Destination receives snapshot data in addition to ongoing CDC; no data loss but may temporarily increase latency and lag | Resetting consumer group offsets and snapshotting are powerful operations. Always verify the impact on your destination before proceeding, especially in production environments. ## Recovery Scenarios **Symptoms:** * Pipeline status shows **Broken** (red badge) in the Pipelines list * An info icon appears next to the status with error details **Diagnosis:** 1. Click the info icon next to the Broken status to see the error summary 2. Navigate to the [Logs](/logs) page, filter by the affected connector, and set the log level to **ERROR** 3. Expand error messages to view stack traces and identify the root cause **Common causes and resolutions:** | Cause | Resolution | | -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | | Source database connection lost | Verify network connectivity, firewall rules, and SSH tunnel/VPN status. Restart the source connector once connectivity is restored. | | Destination authentication failure | Check that destination credentials are valid and have not been rotated. Update connector settings if needed. | | Schema incompatibility | Compare source and destination schemas. Fix mismatches and restart the connector. See [DLQ](/dlq-operations) for failed messages. | | Resource exhaustion on source or destination | Free disk space, increase memory, or scale the database. Restart the connector after resources are available. | **Recovery:** * Fix the underlying issue, then use **Resume source** or **Resume destination** from the pipeline's [row actions menu](/pipelines#row-actions-menu) * If the connector does not recover after resuming, stop it, wait 30 seconds, and resume again **Symptoms:** * Pipeline status shows **Active** but lag is not decreasing or latency is not updating * Destination tables are not receiving new records * Consumer group lag is static or growing **Diagnosis:** 1. Check the pipeline's **Lag** and **Latency** metrics on the [Pipelines](/pipelines) page 2. Navigate to [Consumer Groups](/consumer-groups) and locate the destination's consumer group * Verify the group is in `STABLE` state * Check the **Total Lag** metric and per-partition consumer lag 3. Check the source connector status — is it still streaming? Look for recent INFO-level log messages 4. Review [Logs](/logs) for WARN or ERROR messages from both source and destination connectors **Common causes and resolutions:** | Cause | Resolution | | ------------------------------------ | ----------------------------------------------------------------------------------------------------------- | | Source connector stopped or paused | Resume the source connector from the pipeline actions menu | | Destination connector stuck | Stop and resume the destination connector | | Consumer group has no active members | Verify the destination connector is running; restart if the consumer group shows `EMPTY` state | | Network issue between components | Check network connectivity; review firewall and security group rules | | Source database has no new changes | Confirm that changes are being made to the source tables; this may be expected behavior during idle periods | **Symptoms:** * Row counts differ between source and destination * Specific records are missing or outdated at the destination * Column values differ between source and destination **Diagnosis:** 1. Check the [DLQ](/dlq-operations) for messages that failed delivery — these records were diverted instead of being written to the destination 2. Review the [Logs](/logs) page for schema errors, type conversion warnings, or constraint violations 3. Verify that the destination schema matches the source schema (column types, nullable constraints, primary keys) 4. Check if a snapshot was recently cancelled or failed — incomplete snapshots can leave gaps in historical data **Common causes and resolutions:** | Cause | Resolution | | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Messages in the DLQ | Fix the root cause (schema mismatch, permission error, size limit), then snapshot affected tables to backfill missing records. See [DLQ Recovery](/dlq-operations#recovery). | | Incomplete or failed snapshot | Re-trigger a snapshot for the affected tables. See [Failed Snapshot Recovery](/snapshots#failed-snapshot-recovery). | | Schema change without snapshot | Trigger a snapshot to backfill historical rows with the new schema. See [Snapshotting After Schema Changes](/snapshots#snapshotting-after-schema-changes). | | Consumer group offset desynchronized | Reset consumer group offsets to replay missed messages. Follow the [offset reset procedure](#how-to-reset-consumer-group-offsets) below. | **Symptoms:** * Pipeline latency is significantly higher than normal * Consumer lag is growing steadily * Data arrives at the destination with increasing delay **Diagnosis:** 1. Check the pipeline's **Lag** and **Latency** on the [Pipelines](/pipelines) page 2. Navigate to [Consumer Groups](/consumer-groups) and check per-partition lag to identify bottlenecks 3. Review [Logs](/logs) for slow query warnings or timeout messages 4. Check if a snapshot is currently running — snapshots increase load and may temporarily degrade performance **Common causes and resolutions:** | Cause | Resolution | | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Active snapshot in progress | This is expected. Wait for the snapshot to complete. Lag and latency will normalize after backfilling finishes. | | Insufficient consumer parallelism | Increase the **Tasks** setting on the destination connector to add more parallel consumers. See [Consumer Groups - Performance Tuning](/consumer-groups#performance-tuning). | | Destination write bottleneck | Check destination database performance. Consider scaling destination resources or optimizing table indexes. | | High source database load | Schedule snapshots during off-peak hours. Review source database query performance. | | Partition count too low | Increase topic partition count to enable higher parallelism. See [Topics](/topics). | Increased latency and lag may be expected when snapshots are running. Backfilling produces higher load than normal CDC streaming, but the load is temporary. **Symptoms:** * Pipeline shows Broken status with `ChangeStreamFatalError` in logs * Connector cannot resume from its last position * Error 280 appears in connector logs **Diagnosis:** 1. Check the [Logs](/logs) page for `ChangeStreamFatalError` messages 2. Verify the MongoDB oplog retention period — if the connector was offline longer than the oplog retention, the resume token has expired **Resolution:** 1. Contact [Streamkap support](mailto:support@streamkap.com) for an offset reset 2. After the offset reset, trigger a [snapshot](/snapshots) to backfill any data missed during the outage 3. Increase oplog retention to 48 hours or more to prevent recurrence: ```javascript theme={null} db.adminCommand({ replSetResizeOplog: 1, minRetentionHours: 48 }) ``` **Related:** [Error Reference — MongoDB ChangeStreamFatalError](/error-reference#replication) | [MongoDB Source FAQ](/mongodb-source-faq) **Symptoms:** * Pipeline shows Broken status with schema-related errors * Error message: "Schema isn't known to this connector" * Typically affects databases with 1000+ tables **Diagnosis:** 1. Check the [Logs](/logs) page for schema-related error messages 2. Verify the number of tables in the source database — this issue is more common with large database instances **Resolution:** 1. Contact [Streamkap support](mailto:support@streamkap.com) for schema history recovery 2. After recovery, trigger a [snapshot](/snapshots) of affected tables to ensure data consistency 3. Consider enabling **Capture Only Captured Tables DDL** in the source's Advanced settings to prevent recurrence **Related:** [Error Reference — MySQL Schema Error](/error-reference#schema) | [Schema History Optimization](/schema-history-optimization) **Symptoms:** * Pipeline was working before a source database restart, failover, or maintenance window * Pipeline shows Broken status or is running but no new data is arriving * Logs show connection errors or "connection refused" messages **Diagnosis:** 1. Confirm that the source database is back online and accepting connections 2. Verify that the database user credentials and network configuration have not changed 3. Check [Logs](/logs) for connection error messages from the source connector 4. For PostgreSQL sources, verify that the replication slot still exists and has not been dropped during maintenance 5. For MySQL sources, verify that binary logging is still enabled and the binlog has not been purged past the connector's position **Resolution:** 1. Verify source database connectivity and credentials 2. Resume the source connector from the pipeline actions menu 3. If the source connector does not recover: * Stop the source connector * Wait for the source database to be fully available * Resume the source connector 4. If the connector reports that its position (replication slot, binlog position) is no longer valid: * The source connector may need to be reconfigured * Trigger a snapshot to re-establish the data baseline * Contact [Streamkap support](mailto:support@streamkap.com) if the issue persists For PostgreSQL sources, if the replication slot was dropped during maintenance, the connector cannot resume from its previous position. A snapshot will be required to re-establish data consistency. ## Step-by-Step Recovery Procedures ### How to restart a pipeline connector Go to the **Pipelines** page from the sidebar and locate the affected pipeline. Click the actions menu (three dots) on the pipeline row. Select **Stop source** or **Stop destination** depending on which connector needs to be restarted. Wait approximately 10-30 seconds for the connector to fully stop. You can verify by checking the connector status on the pipeline detail page. Open the actions menu again and select **Resume source** or **Resume destination**. Monitor the pipeline's **Status**, **Lag**, and **Latency** metrics to confirm the connector has recovered. Check the [Logs](/logs) page for any new error messages. **Is restart different from stop + resume?** In Streamkap, restarting a connector is effectively a stop followed by a resume. Both operations preserve the connector's last committed offset position, so no data is lost. The distinction matters primarily when you need to make changes (fix permissions, update credentials, wait for infrastructure) between stopping and resuming. ### How to reset consumer group offsets From the pipeline's [row actions menu](/pipelines#row-actions-menu), select **Stop destination**. All consumers in the group must be stopped before offsets can be reset. Go to the [Consumer Groups](/consumer-groups) page and find the consumer group associated with your destination connector. In the **Topic Partitions** table, check the boxes for the partitions you want to reset. You can select individual partitions or all partitions for a topic. Click the **Reset Offsets** button. A dialog will appear with reset options. Select the appropriate strategy: * **Earliest** — replay all available messages from the beginning of the retention window * **Latest** — skip to the end and only process new messages going forward * **Specific Timestamp** — reset to the first offset after a given timestamp * **Specific Offset** — set a precise offset position Review your selections and click **Apply**. For Snowflake destinations in append mode, you must also reset the Snowpipe Streaming channel offsets to `-1`. See [Snowflake Offset Management](/snowflake#offset-management-append-mode) for the required SQL commands. Go back to the pipeline's actions menu and select **Resume destination**. The consumers will start processing from the new offset positions. Resetting offsets to **Earliest** on large topics will cause re-processing of all retained messages, which may take considerable time and could result in duplicate data at the destination. Streamkap retains topic data based on your project's retention policy. Only messages within the retention window can be replayed — check your project settings for the configured retention period. ### How to trigger a snapshot Go to the source connector detail page from the pipeline detail view or the Sources page. Decide whether to snapshot all tables (source-level snapshot) or specific tables (table-level snapshot): * **Source level:** Use the source connector's actions menu and select **Snapshot** * **Table level:** Find the specific topic in the Topics list and use its actions menu to select **Snapshot** Choose between: * **Full (Complete)** — captures all rows from the selected tables * **Filtered (Partial)** — captures only rows matching a filter condition (useful for backfilling specific time ranges) Type "snapshot" in the confirmation dialog to begin the operation. The source connector status will update to reflect the snapshot operation. Monitor progress on the source connector detail page and in the Topics list. Once the snapshot completes, verify that the expected data is present at the destination. Check the pipeline's Lag metric — it should decrease as the snapshot data is processed. Snapshots run in parallel with ongoing CDC streaming. Your real-time data flow continues uninterrupted while historical data is being backfilled. Expect temporarily increased latency and lag during snapshot operations. Recovery operation timing varies based on data volume, source database performance, and destination write capacity. Small table snapshots typically complete in minutes; large tables (100M+ rows) may take several hours. Offset resets take effect immediately, but reprocessing time depends on the volume of messages being replayed. ## Related Documentation * [Pipelines](/pipelines) - Monitor pipeline health, manage connectors, and view performance metrics * [Consumer Groups](/consumer-groups) - Monitor consumer lag, inspect members, and reset offsets * [Snapshots & Backfilling](/snapshots) - Trigger and manage snapshots for data backfilling * [Dead Letter Queue (DLQ)](/dlq-operations) - Inspect and resolve messages that failed processing # Pipelines Source: https://docs.streamkap.com/pipelines Create and manage data pipelines connecting sources to destinations Pipelines in Streamkap represent the data flow from a source connector (database) to a destination connector (data warehouse or lake). Each pipeline can include transforms and topic configurations to shape and route your data. ## Overview The Pipelines page provides a centralized interface to: * View all pipelines with their status, performance metrics, and configurations * Create new pipelines connecting sources to destinations * Monitor pipeline health, latency, and lag * Filter pipelines by connector, status, or tags * Configure pipeline settings including schema selection and transforms * Access source and destination connector details ## Table Columns The Pipelines table displays the following information: * **Name**: The pipeline name with checkbox for bulk selection * **Tags**: Custom tags for organizing pipelines (e.g., Production, Development) * **Status**: Current pipeline state with visual indicators: * **Active** (green): Pipeline is running normally * **Stopped** (gray): Pipeline is paused * **Broken** (red with info icon): Pipeline has errors * **Latency**: Time delay for data to flow through the pipeline * **Lag**: Number of records behind the source * **Topics**: Number of Kafka topics associated with the pipeline * **Source**: Source connector type and name (clickable to view details) * **Destination**: Destination connector type and name (clickable to view details) * **Actions**: Actions menu (⋮) for quick actions ## Pipeline Statuses Pipelines can be in one of the following states: ### Active The pipeline is running and actively replicating data from source to destination. Indicated by green status badge. ### Stopped The pipeline has been manually paused or stopped. No data is flowing. Indicated by gray status badge. ### Broken The pipeline has encountered an error and is not functioning. Click the info icon next to the status to see error details. Indicated by red status badge with an information icon. When a pipeline shows a Broken status, check the Alerts and Logs pages for detailed error information to troubleshoot the issue. ## Filtering and Search Use the filtering options at the top of the page to quickly find specific pipelines: ### Search Type in the search box to filter pipelines by name. The search filters in real-time as you type. ### Filter by Connectors Click the **Connectors** dropdown to filter pipelines by: * Source connector type (MySQL, PostgreSQL, MongoDB, etc.) * Destination connector type (Snowflake, BigQuery, Databricks, etc.) ### Filter by Status Click the **Status** dropdown to show only pipelines with specific statuses: * Active * Stopped * Broken ### Filter by Tags Click the **Tags** dropdown to filter pipelines by assigned tags (Production, Development, custom tags). ## Row Actions Menu Click the actions menu (⋮) on any pipeline row to access the following options: * **View**: Open the pipeline detail page * **Edit**: Open the pipeline settings for editing * **Copy ID**: Copy the pipeline UUID to clipboard (useful for API calls and support tickets) * **Snapshot**: Trigger a new data snapshot for the pipeline's source * **Cancel Snapshot**: Stop an in-progress snapshot operation * **Resume source**: Resume the source connector if it was stopped * **Stop source**: Stop the source connector from streaming data * **Resume destination**: Resume the destination connector if it was stopped * **Stop destination**: Stop the destination connector from consuming data * **Logs**: View logs for this pipeline * **Update Tags**: Add or modify tags assigned to this pipeline * **Delete**: Remove the pipeline (requires confirmation) Row actions menu showing View, Edit, Copy ID, Snapshot, Cancel Snapshot, Resume source, Stop source, Resume destination, Stop destination, Logs, Update Tags, and Delete options Deleting a pipeline is permanent and will stop all data replication. The underlying source and destination connectors will remain unchanged. ## Creating a Pipeline To create a new pipeline: Click the **Create** button at the top left of the Pipelines page. This launches the three-step create wizard. Pick an existing source on the left and a compatible destination on the right (or use **Add a new source** / **Add a new destination** to create one inline). Only destinations compatible with the chosen source are enabled. Pipeline create wizard step 1: select a source connector and a compatible destination connector Choose which databases and tables from the source to replicate, and optionally select transform topics to include in the pipeline. Pipeline create wizard step 2: choose source schema tables and optional transform topics Set the pipeline name, tags, and decide whether to trigger an incremental snapshot of the selected topics on creation. Click **Create** to save and start the pipeline. Pipeline create wizard step 3: set pipeline name, tags, and incremental snapshot option ## Pipeline Detail View Click on any pipeline name to view its detail page with comprehensive information and configuration options. ### Status Tab The Status tab provides an overview of the pipeline's current state and performance: **Tags Section** * Displays all tags assigned to the pipeline * Tags help organize and categorize pipelines **Stats Section** * **Status**: Current pipeline state (Active, Stopped, or Broken) * **Topics**: Number of Kafka topics in the pipeline * **Latency**: Current latency measurement * **Lag**: Current lag count **Source and Destination Section** * Shows connected source connector with type, name, and status * Shows connected destination connector with type, name, and status * Click on connector cards to navigate to their detail pages **Topics Table** Lists all topics flowing through the pipeline: * **Name**: Topic name (clickable to view topic details) * **Type**: * **Topic**: Standard Kafka topic from source tables * **Transform**: Topic created by a transform operation * **Actions**: Actions menu (⋮) for each topic ### Settings Tab The Settings tab allows you to configure pipeline behavior and data selection: **Name** * Edit the pipeline name **Tags** * Add or remove tags * Create new tags or select from existing ones * Tags support filtering and organization **Audit Config** * Toggle to enable periodic auditing of data consistency * Select the table that will be used for auditing * Set the interval for how often the audit runs (e.g., 30 minutes) * Select the timestamp column to track changes When the periodic auditing is enabled, it will helps keep your destination in sync with the source by automatically correcting mismatches: * Records only in destination → audit removes them * Records only in source → audit adds them * Records that differ → audit updates them Now the audit config only support the pipelines with source and destination connectors as below: * MySQL → Snowflake * PostgreSQL → ClickHouse **Source Schema** * Select which databases and tables to replicate * Search functionality to filter databases and tables * Expandable tree view showing: * Database level with checkbox to select all tables * Individual tables with individual checkboxes * "Select all" option to include all databases and tables **Transforms** * Select specific transformed topics to include in the pipeline * Search functionality to filter transform topics * “Select all” option to include all transform topics * Check "Enable auto-discovery of new topics" and input the regex pattern to automatically include new transform topics created in the future that match the pattern "Enable auto-discovery of new topics" option: * When checked, the user can input a regex pattern * Any new transform topics created that match the regex will be automatically added to the pipeline * This is useful for dynamic environments where new transforms may be added frequently. Changes in the Settings tab require saving before they take effect. Click the **Save** button at the bottom right to apply your changes. ### Source and Destination Links The pipeline detail page includes direct links to: * **Source** tab/link: Opens the source connector detail page in a new context * **Destination** tab/link: Opens the destination connector detail page in a new context These links appear as tabs with external link icons, indicating they navigate away from the pipeline view. ## Pagination At the bottom of the pipelines table: * View the range of pipelines currently displayed (e.g., "Showing pipelines 1 to 10 of 20") * Change the number of pipelines per page (10, 25, 50, 100) * Navigate between pages using Previous/Next buttons or page numbers ## Performance Metrics ### Latency Latency measures how long it takes for data to flow from the source through the pipeline to the destination. Lower latency means near real-time replication. * **0ms - 100ms**: Excellent, near real-time * **100ms - 1s**: Good, minimal delay * **>1s**: May indicate performance issues or heavy load ### Lag Lag represents the number of records or events that are waiting to be processed. Zero lag means the pipeline is fully caught up with the source. * **0**: Fully caught up * **>0**: Pipeline is behind, processing backlog Monitor latency and lag regularly. Increasing values may indicate: * Source database load is too high * Destination connector is slow to write * Network issues between components * Need for performance optimization Increased latency and/or lag may be expected when snapshots are running. Backfilling usually produces a much bigger load than normal CDC streaming, but the load is temporary while backfills are running. ## Bulk Actions Select multiple pipelines using the checkboxes in the Name column to perform actions on multiple pipelines at once. When pipelines are selected, the bulk actions bar appears at the top of the table. Available bulk action: * **Update Tags**: Add or remove tags for all selected pipelines simultaneously You can select individual pipelines or use "Select all X matching pipeline" to select all pipelines in the current filtered view. Use tags to organize pipelines by environment (Production, Development), team, project, or any custom categorization that helps you manage your data infrastructure. # PlanetScale (Vitess) Source: https://docs.streamkap.com/planetscale Stream change data from PlanetScale's Vitess-based MySQL platform to Streamkap via the VTGate VStream service, covering keyspace, tablet type, and setup. ## Prerequisites * A PlanetScale account with a database set up. ## PlanetScale Setup PlanetScale is built on Vitess, a database clustering system for horizontal scaling of MySQL. PlanetScale is using Vitess connector under the hood, enabling change data capture (CDC) for PlanetScale databases. A user can subscribe to multiple shards in a keyspace, making it a convenient tool to feed downstream CDC processes. To read and process database changes, the Connector subscribes to VTGate's VStream gRPC service. VTGate is a lightweight, stateless gRPC server, which is part of the Vitess cluster setup. The Connector gives you the flexibility to choose to subscribe to the `MASTER` nodes, or to the `REPLICA` nodes for change events. ### 1. Granting Privileges Because the Connector reads change events from the VTGate VStream gRPC server, it does not need to connect directly to MySQL instances. Therefore, no special database user and permissions are needed. ### 2. Enable Change Data Capture The Connector does not require any specific configuration for use with PlanetScale. However, make sure that these are accessible to Streamkap: * PlanetScale host and its port (443) **Snapshot** Current version of the connector does not support incremental snapshotting. By default, connector will setup with `initial` snapshotting mode. This means that the connector will read all the data from the specified keyspace and tables when it starts for the first time. This mode is blocking and will not allow the connector to stream changes until the initial snapshot is completed. ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **PlanetScale**. ### 2. Connection Settings * Name for your Connector * Hostname - IP address or hostname of the PlanetScale database server. * Port (default: `443`) - port number of the PlanetScale database server. * Username - The username of the PlanetScale database. * Password - The password of the PlanetScale database. * Keyspace Name - The name of the keyspace from which to stream the changes. * Tablet Type - The type of Tablet (hence MySQL) from which to stream the changes. * **Connect via SSH Tunnel**: The Connector will connect to an SSH server in your network which has access to your database. This is necessary if the Connector cannot connect directly to your database. * See [SSH Tunnel](/ssh-tunnel) for setup instructions. Click **Next**. ### 3. Keyspace and Table Capture * **Add Keyspace/Tables**: Specify the Keyspace(s) and table(s) for capture * You can bulk upload here. The format is a simple list of keyspaces and tables, with each entry on a new row. Save as a `.csv` file without a header. **CDC only captures base tables, not Views** Change Data Capture reads the MySQL binary log (binlog) via Vitess VStream, which only records changes to physical tables. Database Views are query-time computations with no physical storage—they don't generate binlog entries. **What you cannot capture:** Views, temporary tables, MEMORY tables, BLACKHOLE tables, or system tables (information\_schema, performance\_schema). **Solution:** Specify only the underlying base tables that feed your views. You can recreate the view logic in your destination or transformation layer. **VIRTUAL generated columns are not streamed** MySQL [VIRTUAL generated columns](https://dev.mysql.com/doc/refman/8.0/en/create-table-generated-columns.html) are computed when rows are read and are **not** written to the binary log, so live streaming cannot capture or update their values. (STORED generated columns are written to the binary log and stream normally.) Snapshots read the table directly, so they **do** capture a value for VIRTUAL columns — but because streaming cannot keep it current, that value will become stale. **We recommend excluding VIRTUAL generated columns from capture:** in the Source's **Settings → Advanced** section, set **Column Selection Mode** to **Exclusion** and add the column to the **Column Exclusion List** (fully-qualified, e.g. `database.table.column`). If you need the value at your destination, calculate it there — or with a [transform](/transforms) — from the columns it is derived from. Click **Save**. # Overview Source: https://docs.streamkap.com/postgresql Streamkap sources for PostgreSQL change data capture across Amazon RDS, Aurora, Google Cloud SQL, Azure, Neon, Crunchy Bridge, and self-hosted deployments. If you don't see your platform listed, please contact us as we're happy to help find and provide the setup documentation you need. # Amazon RDS PostgreSQL Source: https://docs.streamkap.com/postgresql-on-amazon-rds Configure PostgreSQL CDC on Amazon RDS with Streamkap, including parameter groups, replication role permissions, publications, and heartbeat options. ## Prerequisites * PostgreSQL version ≥ 10 * A database user with sufficient privileges to configure the database, including enabling logical replication and creating users ## PostgreSQL Setup ### 1. Grant Database Access * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your database. ### 2. Enable Logical Replication Logical replication is a method of replicating data objects and their changes, based upon their replication identity (usually a primary key). The Connector relies on PostgreSQLs implementation of this. * Open the Amazon RDS console at [https://console.aws.amazon.com/rds/](https://console.aws.amazon.com/rds/). * In the navigation pane, choose **Parameter groups**. **Default parameter groups can't be modified** If the DB instance is using a default parameter group, create a new one: * Choose **Create parameter group**. * Enter a **Parameter group name** and **Description**. * For **Engine type**, choose your database engine. * For **Parameter group family**, choose a DB parameter group family. * (PostgreSQL 13 and newer) For **Type**, choose **DB Parameter Group**. * Choose **Create**. * Select the parameter group to edit. * Choose **Edit** from **Actions**. * Set `rds.logical_replication` to `1`. * Set `wal_sender_timeout` to `0`. A nonzero value may cause disconnects in low/intermittent traffic databases. Enable [Heartbeats](#6-heartbeats) or set an appropriate value if needed. * Choose **Save changes**. If you created a new parameter group, associate it with your Aurora DB cluster: * In the navigation pane, choose **Databases** and select the target DB cluster. * Choose **Modify**. * Change the **DB parameter group** setting. * Choose **Continue** and review modifications. * The change is applied immediately, regardless of the **Scheduling of modifications** setting. * On the confirmation page, choose **Modify cluster**. A reboot is required to apply the changes. **WAL and database storage** The write-ahead logs take up disk space on your database. If you run out of disk space, you may face an interruption to your database. To mitigate this: * Ensure that your database server has ample free space. * Enable sufficient space with auto growth on the database. * Monitor the WAL growth and configure alerts. * Configure the publication (See [Create Publication & Slot](/postgresql-on-amazon-rds#5-create-publication--slot)) to contain only tables you need. ### 3. Create Database User It's recommended to create a separate user and role for the Connector to access your PostgreSQL database. Below is an example script that does that. ```SQL SQL theme={null} -- Replace { ... } placeholders as required CREATE USER streamkap_user PASSWORD '{password}'; -- Create a role for Streamkap CREATE ROLE streamkap_role; GRANT streamkap_role TO streamkap_user; GRANT rds_replication TO streamkap_role; -- Grant Streamkap permissions on the database, schema and all tables to capture GRANT CONNECT ON DATABASE "{database}" TO streamkap_role; GRANT CREATE, USAGE ON SCHEMA "{schema}" TO streamkap_role; GRANT SELECT ON ALL TABLES IN SCHEMA "{schema}" TO streamkap_role; ALTER DEFAULT PRIVILEGES IN SCHEMA "{schema}" GRANT SELECT ON TABLES TO streamkap_role; ``` ### 4. Enable Snapshots To backfill your data, the Connector needs to be able to perform snapshots. See [Snapshots & Backfilling](/snapshots) for more information. To enable this feature, there are 2 methods available: #### Method 1: Enable read only connection Requires PostgreSQL version 13 or higher. This method is recommended if you cannot create a table in the source database and grant the Connector read/write privileges to that. * Set **Read only** to **Yes** during Streamkap Setup. No other configuration should be necessary. #### Method 2: Create a table in the source database Not supported on read replicas. Please use method 1 instead. You will need to create the table and give necessary permissions to the `streamkap_user`. The Connector will use this collection for managing snapshots. Below is an example script that does that. The examples below use `streamkap_signal` as the signal table name, but you can choose any name. During [Streamkap Setup](#3-snapshot-settings), provide the full path to your signal table in `schema.table` format (e.g., `streamkap.streamkap_signal`). ```SQL SQL theme={null} -- Create the schema CREATE SCHEMA streamkap; -- Switch to the newly created schema SET search_path TO streamkap; -- Create the table CREATE TABLE streamkap_signal ( id VARCHAR(255) PRIMARY KEY, type VARCHAR(32) NOT NULL, data VARCHAR(2000) NULL ); -- Grant necessary privileges on the table to the role GRANT CREATE, USAGE ON SCHEMA streamkap TO streamkap_role; GRANT SELECT ON ALL TABLES IN SCHEMA streamkap TO streamkap_role; GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE streamkap_signal TO streamkap_role; ``` **Publications and signal tables** When you create the PostgreSQL publication in the next step, if you choose to specify tables for capture instead of all tables, you **must** include your signal table. ### 5. Heartbeats Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. For read-write connections (when **Read only** is **No** during Streamkap Setup), the Connector writes to the heartbeat table directly. ```SQL SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Switch to the streamkap schema SET search_path TO streamkap; -- Create the heartbeat table with id, text, and last_update fields CREATE TABLE streamkap_heartbeat ( id SERIAL PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Grant permission to the Streamkap user GRANT USAGE ON SCHEMA streamkap TO streamkap_user; GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE streamkap_heartbeat TO streamkap_user; -- Insert the first row into the heartbeat table INSERT INTO streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` **Heartbeat tables and PostgreSQL publications** If the `streamkap_pub` publication created during PostgreSQL Setup was for specific tables e.g. `CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, table3, ...;` instead of `FOR ALL TABLES;`, you **must** add the heartbeat table to the publication: `ALTER PUBLICATION streamkap_pub ADD TABLE streamkap.streamkap_heartbeat;`. For read-only connections (when **Read only** is **Yes** during Streamkap Setup), the Connector cannot write to the heartbeat table directly. Instead, you must configure a scheduled job on the **primary** database to generate artificial traffic. These changes will replicate to the read replica, which the Connector then consumes. **Run these commands on the primary database, not the read replica.** The heartbeat table and scheduled job must be created on the primary database. The changes will automatically replicate to the read replica. The `pg_cron` extension must be allowed in your database's configuration. See your provider's documentation for enabling extensions: Scheduling maintenance with pg\_cron Scheduling maintenance with pg\_cron PostgreSQL extensions PostgreSQL extensions pg\_cron extension pg\_cron extension Supported extensions Once allowed, create the extension: ```sql SQL theme={null} CREATE EXTENSION IF NOT EXISTS pg_cron; ``` ```sql SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Create the heartbeat table CREATE TABLE streamkap.streamkap_heartbeat ( id SERIAL PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Insert the initial row INSERT INTO streamkap.streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` ```sql SQL theme={null} -- Schedule a job to update the heartbeat table every minute SELECT cron.schedule( 'streamkap_heartbeat_job', '*/1 * * * *', $$UPDATE streamkap.streamkap_heartbeat SET text = 'updated_heartbeat', last_update = CURRENT_TIMESTAMP WHERE id = 1;$$ ); ``` Whichever database user is used to create and run the cron jobs (often the `postgres` user or a dedicated cron user) needs appropriate permissions on the heartbeat table. Additionally, the Streamkap user also needs permissions to monitor the heartbeat table. ```sql SQL theme={null} GRANT USAGE ON SCHEMA streamkap TO {cron user}; GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO {cron user}; -- Grant permissions to the Streamkap user for monitoring and diagnostics GRANT USAGE ON SCHEMA streamkap TO streamkap_user; GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO streamkap_user; ``` The `pg_cron` extension stores job execution history in the table `cron.job_run_details`. To prevent this table from growing indefinitely: ```sql SQL theme={null} SELECT cron.schedule( 'streamkap_cron_cleanup', '0 0 * * *', $$DELETE FROM cron.job_run_details WHERE end_time < now() - interval '7 days';$$ ); ``` ```sql SQL theme={null} -- View all scheduled jobs SELECT * FROM cron.job; -- View recent job execution history SELECT * FROM cron.job_run_details ORDER BY start_time DESC LIMIT 10; -- Unschedule a job (replace {jobid} with the actual job ID) SELECT cron.unschedule({jobid}); ``` **Heartbeat tables and PostgreSQL publications** If the `streamkap_pub` publication was created for specific tables (e.g., `CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, ...;`) instead of `FOR ALL TABLES`, you **must** add the heartbeat table to the publication: ```sql SQL theme={null} ALTER PUBLICATION streamkap_pub ADD TABLE streamkap.streamkap_heartbeat; ``` **Logical-message heartbeats** are an alternative to a heartbeat table. The Connector itself calls `pg_logical_emit_message()` on each beat, which appends a transactional record to the write-ahead log (WAL). PostgreSQL treats that record like any other committed change, so the replication slot advances and WAL files are released—without any new table, scheduled job, or write privileges on the source database. **When to choose this over a heartbeat table** Use **logical-message heartbeats** when you want zero footprint in the source database itself—no schemas, tables, scheduled jobs, or write grants to maintain. Use a **heartbeat table** when you want the heartbeat to be visible as an ordinary row that operators can `SELECT` to confirm liveness, or when the requirements below rule logical-message out. **Requirements** * **Streamkap must be connected directly to a primary**, not to a read replica or other hot standby. The Connector executes `pg_logical_emit_message()` against the database it's connected to, and that function writes to the WAL—hot standbys are read-only and will reject the call with an error like `cannot execute ... during recovery`. If your Streamkap source is configured against a replica, use the **Read-only connections** tab instead. * PostgreSQL **14 or later** on the primary. Earlier versions are not supported because the built-in `pgoutput` replication plugin only began streaming logical-decoding messages to the Connector in PostgreSQL 14. On PostgreSQL 13 and earlier, `pg_logical_emit_message()` still appends records to the WAL, but the Connector cannot read them and the replication slot will not advance. * The Streamkap user needs `SELECT` privileges only—no `INSERT`, `UPDATE`, or `DELETE` on any heartbeat table. * Logical replication must already be enabled for the Connector itself (this is a prerequisite for any PostgreSQL source, regardless of heartbeat mode). During [Connection Settings](#2-connection-settings): * Set **Heartbeats** to **Yes**. * Leave **Heartbeat Table Schema** blank. * Set **Use Logical-Message Heartbeat** to **Yes**. No further setup is required in the source database. The Connector will issue the heartbeat query once per minute using the standard Streamkap heartbeat interval. After enabling, you can confirm that the replication slot is advancing against an otherwise idle database. Run on the primary: ```sql SQL theme={null} SELECT slot_name, confirmed_flush_lsn, restart_lsn FROM pg_replication_slots WHERE slot_name = 'streamkap_pgoutput_slot'; ``` Re-run the query after one or two minutes. Both `confirmed_flush_lsn` and `restart_lsn` should move forward. **What the Connector sends** When this mode is enabled, the Connector issues the following query on each beat: ```sql SQL theme={null} SELECT pg_logical_emit_message(true, '__debezium-heartbeat', now()::varchar); ``` The first argument (`true`) makes the emit **transactional**. Non-transactional emits advance the replication slot's `confirmed_flush_lsn` but do not recompute global WAL retention across all slots, so WAL files can still accumulate on an idle source database—transactional emits avoid that. The transactional cost is negligible (one extra transaction ID per beat, well below PostgreSQL's wraparound horizon at the default one-minute interval). The heartbeat record is filtered out before reaching Kafka, so it does not appear as a record in any topic in your pipeline. ### 6. Create Publication & Slot Publications contain a set of change events for the tables you want the Connector to capture. * Create a publication for your tables. You can create a publication for all tables or selected tables. ```SQL SQL - All Tables theme={null} -- Create a publication for all tables to capture CREATE PUBLICATION streamkap_pub FOR ALL TABLES; ``` ```SQL SQL - Selected Tables theme={null} -- Create a publication for specific tables to capture CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, table3, ...; -- Verify the tables to capture were added to the publication SELECT * FROM pg_publication_tables where pubname = 'streamkap_pub'; ``` **Altering publications** You cannot alter `FOR ALL TABLES` publications to include/exclude tables. If you set up a `FOR ALL TABLES` publication and later decide to change that, you have to drop the publication and create another to include specific tables e.g. `CREATE PUBLICATION ... TABLE table1, table2, table3, ...`. However, any change events that occur before the new publication's created will not be included in it, so a snapshot's required to ensure they are not missed by your Streamkap pipelines. You should also stop the [Source](https://app.streamkap.com/connectors/sources) *before* changing the publication. A replication slot represents a stream of change events the Connector reads from. * Create a replication slot. ```SQL SQL theme={null} -- Create a logical replication slot SELECT pg_create_logical_replication_slot('streamkap_pgoutput_slot', 'pgoutput'); -- Verify the replication slot is working (this may take a few moments to return the count) SELECT count(*) FROM pg_logical_slot_peek_binary_changes('streamkap_pgoutput_slot', null, null, 'proto_version', '1', 'publication_names', 'streamkap_pub'); ``` **Replication slot auto-creation** If the Connector cannot find the replication slot named in [Replication Settings](#4-replication-settings) during Streamkap Setup, it will attempt to create one automatically as it cannot read the database transaction logs without one. This is assuming the database user has been granted the `rds_replication` role as per [Create Database User](#3-create-database-user). #### Excluding columns from replication By default a publication streams every column of each captured table. On **PostgreSQL 15+** you can attach a *column list* to a table in the publication so that only the listed columns are replicated — any column you leave out is excluded at the source and never enters the replication stream. ```SQL SQL - Selected Columns theme={null} -- Only the listed columns are published; every other column is excluded. -- Each table's list MUST include its replica identity (primary key), -- otherwise UPDATE/DELETE for that table cannot be replicated. CREATE PUBLICATION streamkap_pub FOR TABLE public.users (id, email, created_at, updated_at), public.orders (id, user_id, status, total), public.products (id, sku, name, price); -- Add or change a table's column list on an existing publication ALTER PUBLICATION streamkap_pub SET TABLE public.users (id, email, updated_at); -- Verify the published columns per table SELECT pubname, schemaname, tablename, attnames FROM pg_publication_tables WHERE pubname = 'streamkap_pub'; ``` * Requires **PostgreSQL 15 or later**. On earlier versions a publication always streams all columns. * Each table's column list **must include the table's replica identity** — its primary key, or the columns from `REPLICA IDENTITY USING INDEX` / `FULL`. Otherwise `UPDATE` and `DELETE` events for that table are not replicated. * Column lists are only valid with `FOR TABLE`, not `FOR ALL TABLES`. A table listed without parentheses publishes all of its columns, so you can mix restricted and unrestricted tables in one publication. A publication column list only governs **streamed (CDC) changes**. The initial snapshot reads columns directly from the table, so an excluded column can still appear in snapshot records. If a column must never be captured, exclude it from the snapshot as well, not only from the publication. *** ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **PostgreSQL**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Hostname**: Specify the hostname. * **Port**: Default is `5432`. * **Connect via SSH Tunnel**: The Connector will connect to an SSH server in your network which has access to your database. This is necessary if the Connector cannot connect directly to your database. * See [SSH Tunnel](/ssh-tunnel) for setup instructions. * **Username**: Username to access the database. By default, Streamkap scripts use `streamkap_user`. * **Password**: Password to access the database. * **Database**: Specify the database to stream data from. * **Read only**: Whether or not to use a read-only connection. Requires PostgreSQL version 13 or higher. * When connecting to a read replica, set this to **Yes** to support Streamkap snapshots. * **Heartbeats**: Enabled by default. Choose one of three modes—see [Heartbeats](#5-heartbeats) for setup instructions: * **Read-write** connections: configure a heartbeat table in the source database and set **Heartbeat Table Schema**. * **Read-only** connections: configure a scheduled heartbeat job on the primary database using `pg_cron`, and include the heartbeat table in [Schema and Table Capture](#6-schema-and-table-capture). * **Logical-message** (PostgreSQL 14+, works under read-only): set **Use Logical-Message Heartbeat** to **Yes** and leave **Heartbeat Table Schema** blank. No table or write privileges required on the source. ### 3. Snapshot Settings If you set Read only to **No**, you will need to create a snapshot signal table and give permissions to the `streamkap_user`. See [Enable Snapshots](#4-enable-snapshots) for setup instructions. * **Signal Table**: Full path to the signal table including schema and table name (e.g., `streamkap.streamkap_signal`). This table is used for incremental snapshotting. See [Enable Snapshots](#4-enable-snapshots) for setup instructions. ### 4. Replication Settings * **Replication Slot Name**: The name of the replication slot for the connector to use. Default is `streamkap_pgoutput_slot`. * **Publication Name**: The name of the publication for the connector to use. Default is `streamkap_pub`. ### 5. Advanced Parameters * **SSL mode**: Whether to use an encrypted connection to the PostgreSQL server. By default, it's required. * **Prefix with Database Name?**: Changes the format of topics to `DatabaseName_TopicName` * **Represent binary data as**: Specifies how the data for binary columns e.g. `blob`, `binary`, `varbinary` should be interpreted. Your destination for this data can impact which option you choose. Default is `bytes`. Click **Next**. ### 6. Schema and Table Capture * **Add Schemas/Tables**: Specify the schema(s) and table(s) for capture * You can bulk upload here. The format is a simple list of schemas and tables, with each entry on a new row. Save as a `.csv` file without a header. **CDC only captures base tables, not Views** Change Data Capture reads the PostgreSQL write-ahead log (WAL), which only records changes to physical tables. Database Views are query-time computations with no physical storage—they don't generate WAL entries. **What you cannot capture:** Views, temporary tables, unlogged tables, foreign tables (FDW), or system tables (information\_schema, pg\_catalog). **Solution:** Specify only the underlying base tables that feed your views. You can recreate the view logic in your destination or transformation layer. **Generated columns are not kept up to date** PostgreSQL [generated columns](https://www.postgresql.org/docs/current/ddl-generated-columns.html) (defined with `GENERATED ALWAYS AS (...) STORED`) are calculated by the database from other columns. PostgreSQL does **not** include them in the replication stream Streamkap reads, so live streaming cannot keep their values current. **We recommend excluding generated columns from capture.** In the Source's **Settings → Advanced** section, set **Column Selection Mode** to **Exclusion** and add the column to the **Column Exclusion List** (fully-qualified, e.g. `schema.table.column`). If you need the value at your destination, calculate it there — or with a [transform](/transforms) — from the columns it is derived from. **Snapshots:** An incremental snapshot (the watermarked **Filtered** and **Full** options) of a table that contains a generated column will **fail** unless the column is excluded. **Blocking** and **Parallel** snapshots are not affected, though any value they capture will become stale because streaming cannot update it. See [Snapshots & Backfilling](/snapshots). PostgreSQL 18 and later can optionally stream stored generated columns when the publication is created with `publish_generated_columns = stored`; earlier versions cannot. Click **Save**. ## Troubleshooting When upgrading the PostgreSQL database used by Streamkap, there are specific steps to prevent data loss and ensure continued operation. Streamkap handles network failures and outages well. If a monitored database stops, the connector resumes from the last recorded log sequence number (LSN) once communication is restored. It retrieves this offset and queries PostgreSQL for a matching LSN in the replication slot. A replication slot is required for change capture, but PostgreSQL removes slots during upgrades and doesn’t restore them. When the connector restarts, it requests the last known offset, but PostgreSQL cannot return it. Creating a new replication slot isn't enough to prevent data loss. New slots only track changes from their creation point and lack earlier offsets. The connector fetches its last known offset from Kafka but can't retrieve corresponding data from the new slot. It skips older change events and resumes from the latest log position, causing silent data loss with no warnings.
## Procedure Follow these steps to minimize data loss. Note that a few steps may require support from Streamkap. We recommend notifying us about your database upgrade ahead of time to ensure you have the necessary support. * Using your database's upgrade procedure, ensure writes to it have stopped. * Allow the connector to capture all change events before starting the upgrade procedure. Ask Streamkap to confirm this for you. **Interrupting change data capture** If all the events were not captured before stopping the Source and upgrading the database, you can perform a snapshot (after the procedure is completed) in Streamkap for that Source to ensure no change events were missed. * Assuming all events are captured, stop the Source in the Streamkap app. This flushes the last records and saves the last offset. * Stop the database and upgrade it using your upgrade procedure. Once the database is upgraded, and before allowing writes again: * Recreate the logical replication slot, otherwise, Streamkap will miss changes. See [Create Publication & Slot](/postgresql-on-amazon-rds#5-create-publication--slot). * Verify the publication for Streamkap exists; recreate it if necessary: [Create Publication & Slot](/postgresql-on-amazon-rds#5-create-publication--slot). **Update the Source in the Streamkap app** If you have chosen different names for the logical replication slot or publication, make sure to update them in the Streamkap setup page for the relevant PostgreSQL Source. In this case, contact Streamkap, as they may need to reset your Connector's offsets in this scenario. * Restore write access to the database. * Resume or restart the Source in the Streamkap app.
Introduced in PostgreSQL 13, the `REPLICA IDENTITY` table setting controls what data is logged for row updates and deletes. By default, only the primary key and Streamkap metadata column values are retained for deleted records. All other columns will be empty. This leaves you with an incomplete record. If you require - for auditing and historical tracking purposes - all column values for deleted records, or if your deletion strategy for your destination is 'soft deletes' (retain the deleted record with a deletion flag), you should set the `REPLICA IDENTITY` to `FULL` for all capture tables. ```SQL SQL theme={null} ALTER TABLE {table} REPLICA IDENTITY FULL; ``` This ensures complete data retention. For capturing partitioned tables, it's essential to enable `publish_via_partition_root` on the publication. By default, changes to partitions are published from the partition itself. The connector expects changes to come from the root table. To ensure compatibility and consistent replication, enable this setting: `ALTER PUBLICATION streamkap_pub SET (publish_via_partition_root = true);`
# Azure PostgreSQL Source: https://docs.streamkap.com/postgresql-on-azure-database Set up PostgreSQL change data capture from Azure Database for PostgreSQL to Streamkap, covering server parameters, replication roles, and heartbeats. ## Prerequisites * PostgreSQL version ≥ 10 * A database user with sufficient privileges to configure the database, including enabling logical replication and creating users ## PostgreSQL Setup ### 1. Grant Database Access * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your database. ### 2. Enable Logical Replication Logical replication is a method of replicating data objects and their changes, based upon their replication identity (usually a primary key). The Connector relies on PostgreSQLs implementation of this. * Go to your Azure database's server parameters page on the Azure [portal](https://portal.azure.com/) * Set the parameter `wal_level` to `LOGICAL` * Search for `shared_preload_libraries`, and `azure.extensions` parameters, and select `PGLOGICAL` from the dropdown list box * Update `max_worker_processes` parameter value to at least `16`. Otherwise, you might encounter issues like `WARNING: out of background worker slots` * Save the changes and restart the server to apply them **WAL and database storage** The write-ahead logs take up disk space on your database. If you run out of disk space, you may face an interruption to your database. To mitigate this: * Ensure that your database server has ample free space. * Enable sufficient space with auto growth on the database. * Monitor the WAL growth and configure alerts. * Configure the publication (See [Create Publication & Slot](/postgresql-on-azure-database#5-create-publication--slot)) to contain only tables you need. ### 3. Create Database User It's recommended to create a separate user and role for the Connector to access your PostgreSQL database. Below is an example script that does that. ```SQL SQL theme={null} -- Replace { ... } placeholders as required CREATE USER streamkap_user PASSWORD '{password}'; -- Grant the user permissions to manage replication slots and publications ALTER USER streamkap_user WITH REPLICATION; -- Grant Streamkap permissions on the database, schema and all tables to capture GRANT CONNECT ON DATABASE "{database}" TO streamkap_role; GRANT CREATE, USAGE ON SCHEMA "{schema}" TO streamkap_role; GRANT SELECT ON ALL TABLES IN SCHEMA "{schema}" TO streamkap_role; ALTER DEFAULT PRIVILEGES IN SCHEMA "{schema}" GRANT SELECT ON TABLES TO streamkap_role; ``` ### 4. Enable Snapshots To backfill your data, the Connector needs to be able to perform snapshots. See [Snapshots & Backfilling](/snapshots) for more information. To enable this feature, there are 2 methods available: #### Method 1: Enable read only connection Requires PostgreSQL version 13 or higher. This method is recommended if you cannot create a table in the source database and grant the Connector read/write privileges to that. * Set **Read only** to **Yes** during Streamkap Setup. No other configuration should be necessary. #### Method 2: Create a table in the source database Not supported on read replicas. Please use method 1 instead. You will need to create the table and give necessary permissions to the `streamkap_user`. The Connector will use this collection for managing snapshots. Below is an example script that does that. The examples below use `streamkap_signal` as the signal table name, but you can choose any name. During [Streamkap Setup](#3-snapshot-settings), provide the full path to your signal table in `schema.table` format (e.g., `streamkap.streamkap_signal`). ```SQL SQL theme={null} -- Create the schema CREATE SCHEMA streamkap; -- Switch to the newly created schema SET search_path TO streamkap; -- Create the table CREATE TABLE streamkap_signal ( id VARCHAR(255) PRIMARY KEY, type VARCHAR(32) NOT NULL, data VARCHAR(2000) NULL ); -- Grant necessary privileges on the table to the role GRANT CREATE, USAGE ON SCHEMA streamkap TO streamkap_role; GRANT SELECT ON ALL TABLES IN SCHEMA streamkap TO streamkap_role; GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE streamkap_signal TO streamkap_role; ``` **Publications and signal tables** When you create the PostgreSQL publication in the next step, if you choose to specify tables for capture instead of all tables, you **must** include your signal table. ### 5. Heartbeats Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. For read-write connections (when **Read only** is **No** during Streamkap Setup), the Connector writes to the heartbeat table directly. ```SQL SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Switch to the streamkap schema SET search_path TO streamkap; -- Create the heartbeat table with id, text, and last_update fields CREATE TABLE streamkap_heartbeat ( id SERIAL PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Grant permission to the Streamkap user GRANT USAGE ON SCHEMA streamkap TO streamkap_user; GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE streamkap_heartbeat TO streamkap_user; -- Insert the first row into the heartbeat table INSERT INTO streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` **Heartbeat tables and PostgreSQL publications** If the `streamkap_pub` publication created during PostgreSQL Setup was for specific tables e.g. `CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, table3, ...;` instead of `FOR ALL TABLES;`, you **must** add the heartbeat table to the publication: `ALTER PUBLICATION streamkap_pub ADD TABLE streamkap.streamkap_heartbeat;`. For read-only connections (when **Read only** is **Yes** during Streamkap Setup), the Connector cannot write to the heartbeat table directly. Instead, you must configure a scheduled job on the **primary** database to generate artificial traffic. These changes will replicate to the read replica, which the Connector then consumes. **Run these commands on the primary database, not the read replica.** The heartbeat table and scheduled job must be created on the primary database. The changes will automatically replicate to the read replica. The `pg_cron` extension must be allowed in your database's configuration. See your provider's documentation for enabling extensions: Scheduling maintenance with pg\_cron Scheduling maintenance with pg\_cron PostgreSQL extensions PostgreSQL extensions pg\_cron extension pg\_cron extension Supported extensions Once allowed, create the extension: ```sql SQL theme={null} CREATE EXTENSION IF NOT EXISTS pg_cron; ``` ```sql SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Create the heartbeat table CREATE TABLE streamkap.streamkap_heartbeat ( id SERIAL PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Insert the initial row INSERT INTO streamkap.streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` ```sql SQL theme={null} -- Schedule a job to update the heartbeat table every minute SELECT cron.schedule( 'streamkap_heartbeat_job', '*/1 * * * *', $$UPDATE streamkap.streamkap_heartbeat SET text = 'updated_heartbeat', last_update = CURRENT_TIMESTAMP WHERE id = 1;$$ ); ``` Whichever database user is used to create and run the cron jobs (often the `postgres` user or a dedicated cron user) needs appropriate permissions on the heartbeat table. Additionally, the Streamkap user also needs permissions to monitor the heartbeat table. ```sql SQL theme={null} GRANT USAGE ON SCHEMA streamkap TO {cron user}; GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO {cron user}; -- Grant permissions to the Streamkap user for monitoring and diagnostics GRANT USAGE ON SCHEMA streamkap TO streamkap_user; GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO streamkap_user; ``` The `pg_cron` extension stores job execution history in the table `cron.job_run_details`. To prevent this table from growing indefinitely: ```sql SQL theme={null} SELECT cron.schedule( 'streamkap_cron_cleanup', '0 0 * * *', $$DELETE FROM cron.job_run_details WHERE end_time < now() - interval '7 days';$$ ); ``` ```sql SQL theme={null} -- View all scheduled jobs SELECT * FROM cron.job; -- View recent job execution history SELECT * FROM cron.job_run_details ORDER BY start_time DESC LIMIT 10; -- Unschedule a job (replace {jobid} with the actual job ID) SELECT cron.unschedule({jobid}); ``` **Heartbeat tables and PostgreSQL publications** If the `streamkap_pub` publication was created for specific tables (e.g., `CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, ...;`) instead of `FOR ALL TABLES`, you **must** add the heartbeat table to the publication: ```sql SQL theme={null} ALTER PUBLICATION streamkap_pub ADD TABLE streamkap.streamkap_heartbeat; ``` **Logical-message heartbeats** are an alternative to a heartbeat table. The Connector itself calls `pg_logical_emit_message()` on each beat, which appends a transactional record to the write-ahead log (WAL). PostgreSQL treats that record like any other committed change, so the replication slot advances and WAL files are released—without any new table, scheduled job, or write privileges on the source database. **When to choose this over a heartbeat table** Use **logical-message heartbeats** when you want zero footprint in the source database itself—no schemas, tables, scheduled jobs, or write grants to maintain. Use a **heartbeat table** when you want the heartbeat to be visible as an ordinary row that operators can `SELECT` to confirm liveness, or when the requirements below rule logical-message out. **Requirements** * **Streamkap must be connected directly to a primary**, not to a read replica or other hot standby. The Connector executes `pg_logical_emit_message()` against the database it's connected to, and that function writes to the WAL—hot standbys are read-only and will reject the call with an error like `cannot execute ... during recovery`. If your Streamkap source is configured against a replica, use the **Read-only connections** tab instead. * PostgreSQL **14 or later** on the primary. Earlier versions are not supported because the built-in `pgoutput` replication plugin only began streaming logical-decoding messages to the Connector in PostgreSQL 14. On PostgreSQL 13 and earlier, `pg_logical_emit_message()` still appends records to the WAL, but the Connector cannot read them and the replication slot will not advance. * The Streamkap user needs `SELECT` privileges only—no `INSERT`, `UPDATE`, or `DELETE` on any heartbeat table. * Logical replication must already be enabled for the Connector itself (this is a prerequisite for any PostgreSQL source, regardless of heartbeat mode). During [Connection Settings](#2-connection-settings): * Set **Heartbeats** to **Yes**. * Leave **Heartbeat Table Schema** blank. * Set **Use Logical-Message Heartbeat** to **Yes**. No further setup is required in the source database. The Connector will issue the heartbeat query once per minute using the standard Streamkap heartbeat interval. After enabling, you can confirm that the replication slot is advancing against an otherwise idle database. Run on the primary: ```sql SQL theme={null} SELECT slot_name, confirmed_flush_lsn, restart_lsn FROM pg_replication_slots WHERE slot_name = 'streamkap_pgoutput_slot'; ``` Re-run the query after one or two minutes. Both `confirmed_flush_lsn` and `restart_lsn` should move forward. **What the Connector sends** When this mode is enabled, the Connector issues the following query on each beat: ```sql SQL theme={null} SELECT pg_logical_emit_message(true, '__debezium-heartbeat', now()::varchar); ``` The first argument (`true`) makes the emit **transactional**. Non-transactional emits advance the replication slot's `confirmed_flush_lsn` but do not recompute global WAL retention across all slots, so WAL files can still accumulate on an idle source database—transactional emits avoid that. The transactional cost is negligible (one extra transaction ID per beat, well below PostgreSQL's wraparound horizon at the default one-minute interval). The heartbeat record is filtered out before reaching Kafka, so it does not appear as a record in any topic in your pipeline. ### 6. Create Publication & Slot Publications contain a set of change events for the tables you want the Connector to capture. * Create a publication for your tables. You can create a publication for all tables or selected tables. ```SQL SQL - All Tables theme={null} -- Create a publication for all tables to capture CREATE PUBLICATION streamkap_pub FOR ALL TABLES; ``` ```SQL SQL - Selected Tables theme={null} -- Create a publication for specific tables to capture CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, table3, ...; -- Verify the tables to capture were added to the publication SELECT * FROM pg_publication_tables where pubname = 'streamkap_pub'; ``` **Altering publications** You cannot alter `FOR ALL TABLES` publications to include/exclude tables. If you set up a `FOR ALL TABLES` publication and later decide to change that, you have to drop the publication and create another to include specific tables e.g. `CREATE PUBLICATION ... TABLE table1, table2, table3, ...`. However, any change events that occur before the new publication's created will not be included in it, so a snapshot's required to ensure they are not missed by your Streamkap pipelines. You should also stop the [Source](https://app.streamkap.com/connectors/sources) *before* changing the publication. A replication slot represents a stream of change events the Connector reads from. * Create a replication slot. ```SQL SQL theme={null} -- Create a logical replication slot SELECT pg_create_logical_replication_slot('streamkap_pgoutput_slot', 'pgoutput'); -- Verify the replication slot is working (this may take a few moments to return the count) SELECT count(*) FROM pg_logical_slot_peek_binary_changes('streamkap_pgoutput_slot', null, null, 'proto_version', '1', 'publication_names', 'streamkap_pub'); ``` **Replication slot auto-creation** If the Connector cannot find the replication slot named in [Replication Settings](#4-replication-settings) during Streamkap Setup, it will attempt to create one automatically as it cannot read the database transaction logs without one. This is assuming the database user has been granted the `REPLICATION` privileges as per [Create Database User](#3-create-database-user). #### Excluding columns from replication By default a publication streams every column of each captured table. On **PostgreSQL 15+** you can attach a *column list* to a table in the publication so that only the listed columns are replicated — any column you leave out is excluded at the source and never enters the replication stream. ```SQL SQL - Selected Columns theme={null} -- Only the listed columns are published; every other column is excluded. -- Each table's list MUST include its replica identity (primary key), -- otherwise UPDATE/DELETE for that table cannot be replicated. CREATE PUBLICATION streamkap_pub FOR TABLE public.users (id, email, created_at, updated_at), public.orders (id, user_id, status, total), public.products (id, sku, name, price); -- Add or change a table's column list on an existing publication ALTER PUBLICATION streamkap_pub SET TABLE public.users (id, email, updated_at); -- Verify the published columns per table SELECT pubname, schemaname, tablename, attnames FROM pg_publication_tables WHERE pubname = 'streamkap_pub'; ``` * Requires **PostgreSQL 15 or later**. On earlier versions a publication always streams all columns. * Each table's column list **must include the table's replica identity** — its primary key, or the columns from `REPLICA IDENTITY USING INDEX` / `FULL`. Otherwise `UPDATE` and `DELETE` events for that table are not replicated. * Column lists are only valid with `FOR TABLE`, not `FOR ALL TABLES`. A table listed without parentheses publishes all of its columns, so you can mix restricted and unrestricted tables in one publication. A publication column list only governs **streamed (CDC) changes**. The initial snapshot reads columns directly from the table, so an excluded column can still appear in snapshot records. If a column must never be captured, exclude it from the snapshot as well, not only from the publication. *** ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **PostgreSQL**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Hostname**: Specify the hostname. * **Port**: Default is `5432`. * **Connect via SSH Tunnel**: The Connector will connect to an SSH server in your network which has access to your database. This is necessary if the Connector cannot connect directly to your database. * See [SSH Tunnel](/ssh-tunnel) for setup instructions. * **Username**: Username to access the database. By default, Streamkap scripts use `streamkap_user`. * **Password**: Password to access the database. * **Database**: Specify the database to stream data from. * **Read only**: Whether or not to use a read-only connection. Requires PostgreSQL version 13 or higher. * **Heartbeats**: Enabled by default. Choose one of three modes—see [Heartbeats](#5-heartbeats) for setup instructions: * **Read-write** connections: configure a heartbeat table in the source database and set **Heartbeat Table Schema**. * **Read-only** connections: configure a scheduled heartbeat job on the primary database using `pg_cron`, and include the heartbeat table in [Schema and Table Capture](#6-schema-and-table-capture). * **Logical-message** (PostgreSQL 14+, works under read-only): set **Use Logical-Message Heartbeat** to **Yes** and leave **Heartbeat Table Schema** blank. No table or write privileges required on the source. ### 3. Snapshot Settings If you set Read only to **No**, you will need to create a snapshot signal table and give permissions to the `streamkap_user`. See [Enable Snapshots](/postgresql-on-azure-database#4-enable-snapshots) for setup instructions. * **Signal Table**: Full path to the signal table including schema and table name (e.g., `streamkap.streamkap_signal`). This table is used for incremental snapshotting. See [Enable Snapshots](#4-enable-snapshots) for setup instructions. ### 4. Replication Settings * **Replication Slot Name**: The name of the replication slot for the connector to use. Default is `streamkap_pgoutput_slot`. * **Publication Name**: The name of the publication for the connector to use. Default is `streamkap_pub`. ### 5. Advanced Parameters * **SSL mode**: Whether to use an encrypted connection to the PostgreSQL server. By default, it's required. * **Prefix with Database Name?**: Changes the format of topics to `DatabaseName_TopicName` * **Represent binary data as**: Specifies how the data for binary columns e.g. `blob`, `binary`, `varbinary` should be interpreted. Your destination for this data can impact which option you choose. Default is `bytes`. Click **Next**. ### 6. Schema and Table Capture * **Add Schemas/Tables**: Specify the schema(s) and table(s) for capture * You can bulk upload here. The format is a simple list of schemas and tables, with each entry on a new row. Save as a `.csv` file without a header. **CDC only captures base tables, not Views** Change Data Capture reads the PostgreSQL write-ahead log (WAL), which only records changes to physical tables. Database Views are query-time computations with no physical storage—they don't generate WAL entries. **What you cannot capture:** Views, temporary tables, unlogged tables, foreign tables (FDW), or system tables (information\_schema, pg\_catalog). **Solution:** Specify only the underlying base tables that feed your views. You can recreate the view logic in your destination or transformation layer. **Generated columns are not kept up to date** PostgreSQL [generated columns](https://www.postgresql.org/docs/current/ddl-generated-columns.html) (defined with `GENERATED ALWAYS AS (...) STORED`) are calculated by the database from other columns. PostgreSQL does **not** include them in the replication stream Streamkap reads, so live streaming cannot keep their values current. **We recommend excluding generated columns from capture.** In the Source's **Settings → Advanced** section, set **Column Selection Mode** to **Exclusion** and add the column to the **Column Exclusion List** (fully-qualified, e.g. `schema.table.column`). If you need the value at your destination, calculate it there — or with a [transform](/transforms) — from the columns it is derived from. **Snapshots:** An incremental snapshot (the watermarked **Filtered** and **Full** options) of a table that contains a generated column will **fail** unless the column is excluded. **Blocking** and **Parallel** snapshots are not affected, though any value they capture will become stale because streaming cannot update it. See [Snapshots & Backfilling](/snapshots). PostgreSQL 18 and later can optionally stream stored generated columns when the publication is created with `publish_generated_columns = stored`; earlier versions cannot. Click **Save**. ## Troubleshooting When upgrading the PostgreSQL database used by Streamkap, there are specific steps to prevent data loss and ensure continued operation. Streamkap handles network failures and outages well. If a monitored database stops, the connector resumes from the last recorded log sequence number (LSN) once communication is restored. It retrieves this offset and queries PostgreSQL for a matching LSN in the replication slot. A replication slot is required for change capture, but PostgreSQL removes slots during upgrades and doesn’t restore them. When the connector restarts, it requests the last known offset, but PostgreSQL cannot return it. Creating a new replication slot isn't enough to prevent data loss. New slots only track changes from their creation point and lack earlier offsets. The connector fetches its last known offset from Kafka but can't retrieve corresponding data from the new slot. It skips older change events and resumes from the latest log position, causing silent data loss with no warnings. ### Procedure Follow these steps to minimize data loss. Note that a few steps may require support from Streamkap. We recommend notifying us about your database upgrade ahead of time to ensure you have the necessary support. * Using your database's upgrade procedure, ensure writes to it have stopped. * Allow the connector to capture all change events before starting the upgrade procedure. Ask Streamkap to confirm this for you. **Interrupting change data capture** If all the events were not captured before stopping the Source and upgrading the database, you can perform a snapshot (after the procedure is completed) in Streamkap for that Source to ensure no change events were missed. * Assuming all events are captured, stop the Source in the Streamkap app. This flushes the last records and saves the last offset. * Stop the database and upgrade it using your upgrade procedure. Once the database is upgraded, and **before** allowing writes again: * Recreate the logical replication slot, otherwise, Streamkap will miss changes. See [Create Publication & Slot](/postgresql-on-azure-database#5-create-publication--slot). * Verify the publication for Streamkap exists; recreate it if necessary: [Create Publication & Slot](/postgresql-on-azure-database#5-create-publication--slot). **Update the Source in the Streamkap app** If you have chosen different names for the logical replication slot or publication, make sure to update them in the Streamkap setup page for the relevant PostgreSQL Source. In this case, contact Streamkap, as they may need to reset your Connector's offsets in this scenario. * Restore write access to the database. * Resume or restart the Source in the Streamkap app. Introduced in PostgreSQL 13, the `REPLICA IDENTITY` table setting controls what data is logged for row updates and deletes. By default, only the primary key and Streamkap metadata column values are retained for deleted records. All other columns will be empty. This leaves you with an incomplete record. If you require - for auditing and historical tracking purposes - **all** column values for deleted records, or if your deletion strategy for your destination is 'soft deletes' (retain the deleted record with a deletion flag), you should set the `REPLICA IDENTITY` to `FULL` for all capture tables. ```SQL SQL theme={null} ALTER TABLE {table} REPLICA IDENTITY FULL; ``` This ensures complete data retention. For capturing partitioned tables, it's essential to enable `publish_via_partition_root` on the publication. By default, changes to partitions are published from the partition itself. The connector expects changes to come from the root table. To ensure compatibility and consistent replication, enable this setting: `ALTER PUBLICATION streamkap_pub SET (publish_via_partition_root = true);` # PostgreSQL Source: https://docs.streamkap.com/postgresql-self-hosted Connect a self-hosted PostgreSQL database to Streamkap for change data capture, including wal_level, pgoutput, replication users, and heartbeat setup. ## Prerequisites * PostgreSQL version ≥ 10 * A database user with sufficient privileges to configure the database, including enabling logical replication and creating users ## PostgreSQL Setup ### 1. Grant Database Access * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your database. ### 2. Enable Logical Replication Logical replication is a method of replicating data objects and their changes, based upon their replication identity (usually a primary key). The Connector relies on PostgreSQLs implementation of this. Set the following parameters in your database configuration file: * `wal_level = logical` * `wal_sender_timeout = 0`. A non zero amount may cause disconnects in low volume databases. You should in which case enable [Heartbeats](/postgresql-self-hosted#5-heartbeats) or set a suitable value here. * `statement_timeout = 0` OR greater than 5 minutes * Update `pg_hba.conf` file with an entry to authenticate Streamkap's connection to the WAL. * Update `postgresql.conf` file and ensure that the `max_wal_senders` parameter is at least twice the total number of logical replication slots. **WAL and database storage** The write-ahead logs take up disk space on your database. If you run out of disk space, you may face an interruption to your database. To mitigate this: * Ensure that your database server has ample free space. * Enable sufficient space with auto growth on the database. * Monitor the WAL growth and configure alerts. * Configure the publication (See [Create Publication & Slot](/postgresql-self-hosted#5-create-publication--slot)) to contain only tables you need. ### 3. Create Database User It's recommended to create a separate user and role for the Connector to access your PostgreSQL database. Below is an example script that does that. ```SQL SQL theme={null} -- Replace { ... } placeholders as required CREATE USER streamkap_user PASSWORD '{password}'; -- Create a role for Streamkap CREATE ROLE streamkap_role REPLICATION LOGIN; GRANT streamkap_role TO streamkap_user; -- Grant Streamkap permissions on the database, schema and all tables to capture GRANT CONNECT ON DATABASE "{database}" TO streamkap_role; GRANT CREATE, USAGE ON SCHEMA "{schema}" TO streamkap_role; GRANT SELECT ON ALL TABLES IN SCHEMA "{schema}" TO streamkap_role; ALTER DEFAULT PRIVILEGES IN SCHEMA "{schema}" GRANT SELECT ON TABLES TO streamkap_role; ``` ### 4. Enable Snapshots To backfill your data, the Connector needs to be able to perform snapshots. See [Snapshots & Backfilling](/snapshots) for more information. To enable this feature, there are 2 methods available: #### Method 1: Enable read only connection Requires PostgreSQL version 13 or higher. This method is recommended if you cannot create a table in the source database and grant the Connector read/write privileges to that. * Set **Read only** to **Yes** during Streamkap Setup. No other configuration should be necessary. #### Method 2: Create a table in the source database Not supported on read replicas. Please use method 1 instead. You will need to create the table and give necessary permissions to the `streamkap_user`. The Connector will use this collection for managing snapshots. Below is an example script that does that. The examples below use `streamkap_signal` as the signal table name, but you can choose any name. During [Streamkap Setup](#3-snapshot-settings), provide the full path to your signal table in `schema.table` format (e.g., `streamkap.streamkap_signal`). ```SQL SQL theme={null} -- Create the schema CREATE SCHEMA streamkap; -- Switch to the newly created schema SET search_path TO streamkap; -- Create the table CREATE TABLE streamkap_signal ( id VARCHAR(255) PRIMARY KEY, type VARCHAR(32) NOT NULL, data VARCHAR(2000) NULL ); -- Grant necessary privileges on the table to the role GRANT CREATE, USAGE ON SCHEMA streamkap TO streamkap_role; GRANT SELECT ON ALL TABLES IN SCHEMA streamkap TO streamkap_role; GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE streamkap_signal TO streamkap_role; ``` **Publications and signal tables** When you create the PostgreSQL publication in the next step, if you choose to specify tables for capture instead of all tables, you **must** include your signal table. ### 5. Heartbeats Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. For read-write connections (when **Read only** is **No** during Streamkap Setup), the Connector writes to the heartbeat table directly. ```SQL SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Switch to the streamkap schema SET search_path TO streamkap; -- Create the heartbeat table with id, text, and last_update fields CREATE TABLE streamkap_heartbeat ( id SERIAL PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Grant permission to the Streamkap user GRANT USAGE ON SCHEMA streamkap TO streamkap_user; GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE streamkap_heartbeat TO streamkap_user; -- Insert the first row into the heartbeat table INSERT INTO streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` **Heartbeat tables and PostgreSQL publications** If the `streamkap_pub` publication created during PostgreSQL Setup was for specific tables e.g. `CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, table3, ...;` instead of `FOR ALL TABLES;`, you **must** add the heartbeat table to the publication: `ALTER PUBLICATION streamkap_pub ADD TABLE streamkap.streamkap_heartbeat;`. For read-only connections (when **Read only** is **Yes** during Streamkap Setup), the Connector cannot write to the heartbeat table directly. Instead, you must configure a scheduled job on the **primary** database to generate artificial traffic. These changes will replicate to the read replica, which the Connector then consumes. **Run these commands on the primary database, not the read replica.** The heartbeat table and scheduled job must be created on the primary database. The changes will automatically replicate to the read replica. The `pg_cron` extension must be allowed in your database's configuration. See your provider's documentation for enabling extensions: Scheduling maintenance with pg\_cron Scheduling maintenance with pg\_cron PostgreSQL extensions PostgreSQL extensions pg\_cron extension pg\_cron extension Supported extensions Once allowed, create the extension: ```sql SQL theme={null} CREATE EXTENSION IF NOT EXISTS pg_cron; ``` ```sql SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Create the heartbeat table CREATE TABLE streamkap.streamkap_heartbeat ( id SERIAL PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Insert the initial row INSERT INTO streamkap.streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` ```sql SQL theme={null} -- Schedule a job to update the heartbeat table every minute SELECT cron.schedule( 'streamkap_heartbeat_job', '*/1 * * * *', $$UPDATE streamkap.streamkap_heartbeat SET text = 'updated_heartbeat', last_update = CURRENT_TIMESTAMP WHERE id = 1;$$ ); ``` Whichever database user is used to create and run the cron jobs (often the `postgres` user or a dedicated cron user) needs appropriate permissions on the heartbeat table. Additionally, the Streamkap user also needs permissions to monitor the heartbeat table. ```sql SQL theme={null} GRANT USAGE ON SCHEMA streamkap TO {cron user}; GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO {cron user}; -- Grant permissions to the Streamkap user for monitoring and diagnostics GRANT USAGE ON SCHEMA streamkap TO streamkap_user; GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO streamkap_user; ``` The `pg_cron` extension stores job execution history in the table `cron.job_run_details`. To prevent this table from growing indefinitely: ```sql SQL theme={null} SELECT cron.schedule( 'streamkap_cron_cleanup', '0 0 * * *', $$DELETE FROM cron.job_run_details WHERE end_time < now() - interval '7 days';$$ ); ``` ```sql SQL theme={null} -- View all scheduled jobs SELECT * FROM cron.job; -- View recent job execution history SELECT * FROM cron.job_run_details ORDER BY start_time DESC LIMIT 10; -- Unschedule a job (replace {jobid} with the actual job ID) SELECT cron.unschedule({jobid}); ``` **Heartbeat tables and PostgreSQL publications** If the `streamkap_pub` publication was created for specific tables (e.g., `CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, ...;`) instead of `FOR ALL TABLES`, you **must** add the heartbeat table to the publication: ```sql SQL theme={null} ALTER PUBLICATION streamkap_pub ADD TABLE streamkap.streamkap_heartbeat; ``` **Logical-message heartbeats** are an alternative to a heartbeat table. The Connector itself calls `pg_logical_emit_message()` on each beat, which appends a transactional record to the write-ahead log (WAL). PostgreSQL treats that record like any other committed change, so the replication slot advances and WAL files are released—without any new table, scheduled job, or write privileges on the source database. **When to choose this over a heartbeat table** Use **logical-message heartbeats** when you want zero footprint in the source database itself—no schemas, tables, scheduled jobs, or write grants to maintain. Use a **heartbeat table** when you want the heartbeat to be visible as an ordinary row that operators can `SELECT` to confirm liveness, or when the requirements below rule logical-message out. **Requirements** * **Streamkap must be connected directly to a primary**, not to a read replica or other hot standby. The Connector executes `pg_logical_emit_message()` against the database it's connected to, and that function writes to the WAL—hot standbys are read-only and will reject the call with an error like `cannot execute ... during recovery`. If your Streamkap source is configured against a replica, use the **Read-only connections** tab instead. * PostgreSQL **14 or later** on the primary. Earlier versions are not supported because the built-in `pgoutput` replication plugin only began streaming logical-decoding messages to the Connector in PostgreSQL 14. On PostgreSQL 13 and earlier, `pg_logical_emit_message()` still appends records to the WAL, but the Connector cannot read them and the replication slot will not advance. * The Streamkap user needs `SELECT` privileges only—no `INSERT`, `UPDATE`, or `DELETE` on any heartbeat table. * Logical replication must already be enabled for the Connector itself (this is a prerequisite for any PostgreSQL source, regardless of heartbeat mode). During [Connection Settings](#2-connection-settings): * Set **Heartbeats** to **Yes**. * Leave **Heartbeat Table Schema** blank. * Set **Use Logical-Message Heartbeat** to **Yes**. No further setup is required in the source database. The Connector will issue the heartbeat query once per minute using the standard Streamkap heartbeat interval. After enabling, you can confirm that the replication slot is advancing against an otherwise idle database. Run on the primary: ```sql SQL theme={null} SELECT slot_name, confirmed_flush_lsn, restart_lsn FROM pg_replication_slots WHERE slot_name = 'streamkap_pgoutput_slot'; ``` Re-run the query after one or two minutes. Both `confirmed_flush_lsn` and `restart_lsn` should move forward. **What the Connector sends** When this mode is enabled, the Connector issues the following query on each beat: ```sql SQL theme={null} SELECT pg_logical_emit_message(true, '__debezium-heartbeat', now()::varchar); ``` The first argument (`true`) makes the emit **transactional**. Non-transactional emits advance the replication slot's `confirmed_flush_lsn` but do not recompute global WAL retention across all slots, so WAL files can still accumulate on an idle source database—transactional emits avoid that. The transactional cost is negligible (one extra transaction ID per beat, well below PostgreSQL's wraparound horizon at the default one-minute interval). The heartbeat record is filtered out before reaching Kafka, so it does not appear as a record in any topic in your pipeline. ### 6. Create Publication & Slot Publications contain a set of change events for the tables you want the Connector to capture. * Create a publication for your tables. You can create a publication for all tables or selected tables. ```SQL SQL - All Tables theme={null} -- Create a publication for all tables to capture CREATE PUBLICATION streamkap_pub FOR ALL TABLES; ``` ```SQL SQL - Selected Tables theme={null} -- Create a publication for specific tables to capture CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, table3, ...; -- Verify the tables to capture were added to the publication SELECT * FROM pg_publication_tables where pubname = 'streamkap_pub'; ``` **Altering publications** You cannot alter `FOR ALL TABLES` publications to include/exclude tables. If you set up a `FOR ALL TABLES` publication and later decide to change that, you have to drop the publication and create another to include specific tables e.g. `CREATE PUBLICATION ... TABLE table1, table2, table3, ...`. However, any change events that occur before the new publication's created will not be included in it, so a snapshot's required to ensure they are not missed by your Streamkap pipelines. You should also stop the [Source](https://app.streamkap.com/connectors/sources) *before* changing the publication. A replication slot represents a stream of change events the Connector reads from. * Create a replication slot. ```SQL SQL theme={null} -- Create a logical replication slot SELECT pg_create_logical_replication_slot('streamkap_pgoutput_slot', 'pgoutput'); -- Verify the replication slot is working (this may take a few moments to return the count) SELECT count(*) FROM pg_logical_slot_peek_binary_changes('streamkap_pgoutput_slot', null, null, 'proto_version', '1', 'publication_names', 'streamkap_pub'); ``` **Replication slot auto-creation** If the Connector cannot find the replication slot named in [Replication Settings](/postgresql-self-hosted#4-replication-settings) during Streamkap Setup, it will attempt to create one automatically as it cannot read the database transaction logs without one. This is assuming the database user has been granted the `REPLICATION` privileges as per [Create Database User](/postgresql-self-hosted#3-create-database-user). #### Excluding columns from replication By default a publication streams every column of each captured table. On **PostgreSQL 15+** you can attach a *column list* to a table in the publication so that only the listed columns are replicated — any column you leave out is excluded at the source and never enters the replication stream. ```SQL SQL - Selected Columns theme={null} -- Only the listed columns are published; every other column is excluded. -- Each table's list MUST include its replica identity (primary key), -- otherwise UPDATE/DELETE for that table cannot be replicated. CREATE PUBLICATION streamkap_pub FOR TABLE public.users (id, email, created_at, updated_at), public.orders (id, user_id, status, total), public.products (id, sku, name, price); -- Add or change a table's column list on an existing publication ALTER PUBLICATION streamkap_pub SET TABLE public.users (id, email, updated_at); -- Verify the published columns per table SELECT pubname, schemaname, tablename, attnames FROM pg_publication_tables WHERE pubname = 'streamkap_pub'; ``` * Requires **PostgreSQL 15 or later**. On earlier versions a publication always streams all columns. * Each table's column list **must include the table's replica identity** — its primary key, or the columns from `REPLICA IDENTITY USING INDEX` / `FULL`. Otherwise `UPDATE` and `DELETE` events for that table are not replicated. * Column lists are only valid with `FOR TABLE`, not `FOR ALL TABLES`. A table listed without parentheses publishes all of its columns, so you can mix restricted and unrestricted tables in one publication. A publication column list only governs **streamed (CDC) changes**. The initial snapshot reads columns directly from the table, so an excluded column can still appear in snapshot records. If a column must never be captured, exclude it from the snapshot as well, not only from the publication. *** ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **PostgreSQL**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Hostname**: Specify the hostname. * **Port**: Default is `5432`. * **Connect via SSH Tunnel**: The Connector will connect to an SSH server in your network which has access to your database. This is necessary if the Connector cannot connect directly to your database. * See [SSH Tunnel](/ssh-tunnel) for setup instructions. * **Username**: Username to access the database. By default, Streamkap scripts use `streamkap_user`. * **Password**: Password to access the database. * **Database**: Specify the database to stream data from. * **Read only**: Whether or not to use a read-only connection. Requires PostgreSQL version 13 or higher. * When connecting to a read replica, set this to **Yes** to support Streamkap snapshots. * **Heartbeats**: Enabled by default. Choose one of three modes—see [Heartbeats](/postgresql-self-hosted#5-heartbeats) for setup instructions: * **Read-write** connections: configure a heartbeat table in the source database and set **Heartbeat Table Schema**. * **Read-only** connections: configure a scheduled heartbeat job on the primary database using `pg_cron`, and include the heartbeat table in [Schema and Table Capture](/postgresql-self-hosted#6-schema-and-table-capture). * **Logical-message** (PostgreSQL 14+, works under read-only): set **Use Logical-Message Heartbeat** to **Yes** and leave **Heartbeat Table Schema** blank. No table or write privileges required on the source. ### 3. Snapshot Settings If you set Read only to **No**, you will need to create a snapshot signal table and give permissions to the `streamkap_user`. See [Enable Snapshots](/postgresql-self-hosted#4-enable-snapshots) for setup instructions. * **Signal Table**: Full path to the signal table including schema and table name (e.g., `streamkap.streamkap_signal`). This table is used for incremental snapshotting. See [Enable Snapshots](#4-enable-snapshots) for setup instructions. ### 4. Replication Settings * **Replication Slot Name**: The name of the replication slot for the connector to use. Default is `streamkap_pgoutput_slot`. * **Publication Name**: The name of the publication for the connector to use. Default is `streamkap_pub`. ### 5. Advanced Parameters * **SSL mode**: Whether to use an encrypted connection to the PostgreSQL server. By default, it's required. * **Prefix with Database Name?**: Changes the format of topics to `DatabaseName_TopicName` * **Represent binary data as**: Specifies how the data for binary columns e.g. `blob`, `binary`, `varbinary` should be interpreted. Your destination for this data can impact which option you choose. Default is `bytes`. Click **Next**. ### 6. Schema and Table Capture * **Add Schemas/Tables**: Specify the schema(s) and table(s) for capture * You can bulk upload here. The format is a simple list of schemas and tables, with each entry on a new row. Save as a `.csv` file without a header. **Adding tables programmatically** -- You can add tables to an existing source via the API or Terraform using the `table.include.list.user.defined` parameter (Terraform: `table_include_list_user_defined`). See [Terraform Resources](/terraform-resources#sources) for Terraform source configuration. **CDC only captures base tables, not Views** Change Data Capture reads the PostgreSQL write-ahead log (WAL), which only records changes to physical tables. Database Views are query-time computations with no physical storage—they don't generate WAL entries. **What you cannot capture:** Views, temporary tables, unlogged tables, foreign tables (FDW), or system tables (information\_schema, pg\_catalog). **Solution:** Specify only the underlying base tables that feed your views. You can recreate the view logic in your destination or transformation layer. **Generated columns are not kept up to date** PostgreSQL [generated columns](https://www.postgresql.org/docs/current/ddl-generated-columns.html) (defined with `GENERATED ALWAYS AS (...) STORED`) are calculated by the database from other columns. PostgreSQL does **not** include them in the replication stream Streamkap reads, so live streaming cannot keep their values current. **We recommend excluding generated columns from capture.** In the Source's **Settings → Advanced** section, set **Column Selection Mode** to **Exclusion** and add the column to the **Column Exclusion List** (fully-qualified, e.g. `schema.table.column`). If you need the value at your destination, calculate it there — or with a [transform](/transforms) — from the columns it is derived from. **Snapshots:** An incremental snapshot (the watermarked **Filtered** and **Full** options) of a table that contains a generated column will **fail** unless the column is excluded. **Blocking** and **Parallel** snapshots are not affected, though any value they capture will become stale because streaming cannot update it. See [Snapshots & Backfilling](/snapshots). PostgreSQL 18 and later can optionally stream stored generated columns when the publication is created with `publish_generated_columns = stored`; earlier versions cannot. Click **Save**. ## Troubleshooting When upgrading the PostgreSQL database used by Streamkap, there are specific steps to prevent data loss and ensure continued operation.

Streamkap handles network failures and outages well. If a monitored database stops, the connector resumes from the last recorded log sequence number (LSN) once communication is restored. It retrieves this offset and queries PostgreSQL for a matching LSN in the replication slot.

A replication slot is required for change capture, but PostgreSQL removes slots during upgrades and doesn’t restore them. When the connector restarts, it requests the last known offset, but PostgreSQL cannot return it.

Creating a new replication slot isn't enough to prevent data loss. New slots only track changes from their creation point and lack earlier offsets. The connector fetches its last known offset from Kafka but can't retrieve corresponding data from the new slot. It skips older change events and resumes from the latest log position, causing silent data loss with no warnings.

### Procedure Follow these steps to minimize data loss. Note that a few steps may require support from Streamkap. We recommend notifying us about your database upgrade ahead of time to ensure you have the necessary support. * Using your database's upgrade procedure, ensure writes to it have stopped. * Allow the connector to capture all change events before starting the upgrade procedure. Ask Streamkap to confirm this for you. **Interrupting change data capture** If all the events were not captured before stopping the Source and upgrading the database, you can perform a snapshot (after the procedure is completed) in Streamkap for that Source to ensure no change events were missed. * Assuming all events are captured, stop the Source in the Streamkap app. This flushes the last records and saves the last offset. * Stop the database and upgrade it using your upgrade procedure. Once the database is upgraded, and **before** allowing writes again: * Recreate the logical replication slot, otherwise, Streamkap will miss changes. See [Create Publication & Slot](/postgresql-self-hosted#5-create-publication--slot). * Verify the publication for Streamkap exists; recreate it if necessary: [Create Publication & Slot](/postgresql-self-hosted#5-create-publication--slot). **Update the Source in the Streamkap app** If you have chosen different names for the logical replication slot or publication, make sure to update them in the Streamkap setup page for the relevant PostgreSQL Source. In this case, contact Streamkap, as they may need to reset your Connector's offsets in this scenario. * Restore write access to the database. * Resume or restart the Source in the Streamkap app.
Introduced in PostgreSQL 13, the `REPLICA IDENTITY` table setting controls what data is logged for row updates and deletes.

By default, only the primary key and Streamkap metadata column values are retained for deleted records. All other columns will be empty. This leaves you with an incomplete record.

If you require - for auditing and historical tracking purposes - **all** column values for deleted records, or if your deletion strategy for your destination is 'soft deletes' (retain the deleted record with a deletion flag), you should set the `REPLICA IDENTITY` to `FULL` for all capture tables.

```SQL SQL theme={null} ALTER TABLE {table} REPLICA IDENTITY FULL; ```
Capturing partitioned tables (PostgreSQL 13 and newer) For capturing partitioned tables, it's essential to enable `publish_via_partition_root` on the publication.

By default, changes to partitions are published from the partition itself. The connector expects changes to come from the root table.

To ensure compatibility and consistent replication, enable this setting:

`ALTER PUBLICATION streamkap_pub SET (publish_via_partition_root = true);`
# PostgreSQL Source FAQ Source: https://docs.streamkap.com/postgresql-source-faq Answers to common PostgreSQL source connector questions in Streamkap, covering replication slots, WAL retention, publications, generated columns, and more. ## PostgreSQL Sources FAQ for Streamkap This FAQ focuses on using PostgreSQL as a source in Streamkap, including general self-hosted setups and cloud variants (AWS RDS/Aurora, Azure, Google Cloud SQL, Neon). Streamkap's PostgreSQL connector provides real-time CDC with managed features like automatic scaling, UI setup, and ETL transformations. A PostgreSQL source in Streamkap enables real-time Change Data Capture (CDC) from PostgreSQL databases, capturing row-level inserts, updates, and deletes with sub-second latency. It uses logical replication to stream changes to destinations, supporting snapshots for initial loads, schema evolution, and heartbeats for low-traffic DBs. Streamkap abstracts complexity, offering a serverless setup via UI or API. * PostgreSQL 10+ for basic CDC; 13+ for advanced features like read-only snapshots and partitioned tables with `publish_via_partition_root`. Compatible with PostgreSQL 15–17+ in cloud deployments. * PostgreSQL 9.4+ for logical decoding; 10+ for `pgoutput` plugin; 13+ for read-only incremental snapshots; 16+ for replica server slots; 17+ for failover-enabled slots. Streamkap supports: * Self-hosted (on-prem/VM) * AWS RDS PostgreSQL (including Aurora and Serverless) * Azure Database for PostgreSQL * Google Cloud SQL for PostgreSQL * Neon PostgreSQL Streamkap also supports these, plus CrunchyBridge and Amazon RDS, with similar configs. * **CDC**: Log-based via `pgoutput` decoder; captures inserts/updates/deletes * **Snapshots**: Ad-hoc/initial backfills using read-only (PG 13+) or signal table methods; watermarking for minimal impact * **Schema Evolution**: Automatic handling of adds/drops/type changes * **Heartbeats**: Enabled by default to prevent WAL buildup in low-traffic DBs via a `streamkap_heartbeat` table * **Data Types**: Standard mappings (numerics, temporals, strings, binary as bytes/base64/hex, arrays/JSON/hstore); supports pgvector/VECTOR types in latest Streamkap * **Ingestion Modes**: Inserts (append) or upserts * **Security**: SSL, SSH/VPN, IP allowlisting * **Monitoring**: Latency, lag, WAL metrics in-app * Streamkap adds transaction metadata, ad-hoc snapshots with filters, and custom types like PostGIS/GEOMETRY Streamkap reads PostgreSQL's write-ahead log (WAL) via logical decoding, emitting changes as events. It uses Streamkap's `pgoutput` plugin for native support. REPLICA IDENTITY FULL ensures complete before/after images for updates/deletes. REPLICA IDENTITY controls logged data for changes: * **DEFAULT**: Logs PK only (partial deletes) * **FULL**: Logs all columns (complete records, recommended for audits/soft deletes). Set via `ALTER TABLE ... REPLICA IDENTITY FULL;`. Required for full delete handling. * Trigger ad-hoc at source/table level.\ Methods: Read-only (PG 13+, no signal table) or `streamkap_signal` table. Uses watermarking for incremental loads. * Modes like `initial`, `always`, `when_needed`; supports incremental (chunked, parallel) and read-only (PG 13+); ad-hoc via signaling. Streamkap simplifies triggering via UI. Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. For read-write connections (when **Read only** is **No** during Streamkap Setup), the Connector writes to the heartbeat table directly. ```SQL SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Switch to the streamkap schema SET search_path TO streamkap; -- Create the heartbeat table with id, text, and last_update fields CREATE TABLE streamkap_heartbeat ( id SERIAL PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Grant permission to the Streamkap user GRANT USAGE ON SCHEMA streamkap TO streamkap_user; GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE streamkap_heartbeat TO streamkap_user; -- Insert the first row into the heartbeat table INSERT INTO streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` **Heartbeat tables and PostgreSQL publications** If the `streamkap_pub` publication created during PostgreSQL Setup was for specific tables e.g. `CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, table3, ...;` instead of `FOR ALL TABLES;`, you **must** add the heartbeat table to the publication: `ALTER PUBLICATION streamkap_pub ADD TABLE streamkap.streamkap_heartbeat;`. * **Basics**: Integers, floats, strings, dates/timestamps (micro/nano precision modes) * **Advanced**: Arrays, JSON, binary (bytes/hex), decimals (precise/double/string modes) * **Custom**: Domain types, network addresses, PostGIS (GEOMETRY/GEOGRAPHY), pgvector (VECTOR/HALFVEC/SPARSEVEC) * **Unsupported**: Non-UTF8 encodings; some spatial/custom without config * Use queries like: ```sql theme={null} SELECT pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) FROM pg_replication_slots WHERE slot_name = 'streamkap_slot'; ``` * Tools: Datadog (`wal_bytes`), New Relic/Grafana (custom queries) * Best Practices: Retain 3–5 days WAL; alert on growth; drop inactive slots * Streamkap: Monitor via JMX; WAL issues from inactive slots common * Non-UTF8 unsupported * Data loss risk on upgrades (recreate slots) * Read replicas limit snapshots * No DDL events * PK changes need manual handling * TOASTed values may be incomplete * Consistency issues pre-commit * Generated columns not streamed — exclude them from capture (incremental snapshots fail otherwise) * WAL buildup without heartbeats * Schema changes during snapshots unsupported Use REPLICA IDENTITY FULL for complete records. Supports soft deletes. In upserts, deletes propagate as events. * **WAL Buildup**: Enable heartbeats; monitor/drop slots; retain 3–5 days * **Upgrade Data Loss**: Stop writes, capture events, recreate slot, resnapshot * **Connection Failures**: Verify `pg_hba.conf`, firewalls, SSL * **Missing Events**: Ensure publication includes tables; check REPLICA IDENTITY **No, CDC cannot capture Views or most virtual database objects.** **Why Views cannot be captured:**\ CDC captures changes by reading the database transaction log (binlog, WAL, oplog, redo log, etc.). Views are query-time computations over base tables—they don't store data or generate transaction log entries. When you query a view, the database engine executes the underlying SELECT statement against the base tables. Since views don't store data, they don't generate transaction log entries. **What cannot be captured:** * **Views**: Virtual tables with no physical storage or WAL entries * **Materialized Views**: Special case—can be captured if they have `REPLICA IDENTITY` configured, but refresh operations may not generate standard change events. Better to capture the source tables. * **Temporary Tables**: Session-scoped, not logged persistently in WAL * **Unlogged Tables**: Explicitly excluded from WAL by design * **Foreign Tables**: Reference external data sources, not local storage * **System/Catalog Tables** (information\_schema, pg\_catalog): Metadata representations, not user data * **CTEs (Common Table Expressions)**: Query-time constructs with no persistent storage **Solution:**\ Configure CDC on the underlying base tables that power your views. The view logic can be recreated in your destination or transformation layer. **Example:**\ If you have a view `sales_summary` that queries tables `orders` and `customers`, capture the `orders` and `customers` tables instead, then recreate the view logic downstream. PostgreSQL stored generated columns (`GENERATED ALWAYS AS (...) STORED`) are **not** included in the replication stream, so streaming cannot keep them up to date. We recommend **excluding generated columns from capture** — in the Source's **Settings → Advanced** section, set **Column Selection Mode** to **Exclusion** and add the column to the **Column Exclusion List** — and recomputing the value at your destination or in a [transform](/transforms) if you need it. An incremental snapshot (the **Filtered** and **Full** options) of a table with a generated column will **fail** unless the column is excluded. **Blocking** and **Parallel** snapshots are not affected. PostgreSQL 18 and later can optionally stream stored generated columns when the publication is created with `publish_generated_columns = stored`; earlier versions cannot. PostgreSQL rejects null bytes (`0x00`) in text columns. If your source data contains null characters, they can cause errors during replication. **Resolution:** Clean null characters from affected columns at the source: ```sql theme={null} UPDATE my_table SET field = REPLACE(field, chr(0), '') WHERE position(chr(0) in field) > 0; ``` Alternatively, add a transform to strip null bytes before delivery to the destination. Snapshots acquire a brief lock during initialization. For very large tables under heavy concurrent write load, the lock acquisition may time out (default 15 minutes). **Resolution:** 1. Schedule snapshots during off-peak hours to reduce lock contention 2. Use [Filtered (Partial) snapshots](/snapshots#snapshot-options) to process smaller data ranges 3. If timeouts persist, contact [Streamkap support](mailto:support@streamkap.com) for assistance * Use dedicated replication user * Limit publications to needed tables * Enable auto-vacuum; set WAL retention * Test snapshots in staging * For cloud: Monitor provider tools; use heartbeats for low traffic * Use PG 17+ for failover resilience Database version upgrades require careful planning to avoid data loss and minimize downtime for your CDC pipelines. See our [Database Upgrade Guide](/database-upgrade-guide) for step-by-step instructions. # Profile Source: https://docs.streamkap.com/profile Manage your personal profile information in Streamkap Profile settings can be found in **Account preferences** by clicking your avatar in the top navigation bar, then selecting **Profile**. Profile settings page showing avatar and basic profile information ## Basic Profile Information You can view and edit the following profile details: * **Email** - Your login email address (read-only) * **Name** - Your display name * **Username** - Optional username for your account * **Phone Number** - Contact phone number (optional) * **Address** - Your address (optional) * **Job Title** - Your role or position (optional) ## Profile Picture Click the edit button on your avatar to upload or change your profile picture. # Project Keys Source: https://docs.streamkap.com/project-keys Create unified credentials that bundle API access, Kafka access, and MCP tool scoping into a single downloadable file. Project Keys are downloadable credential files that bundle one or more of the following into a single `.json` artifact: * **API access** — a Client ID and Secret for the [REST API](/api-reference/authentication/access-token), [CLI](/cli), and [Terraform](/streamkap-provider-for-terraform) * **Kafka access** — SASL credentials and proxy endpoints for direct topic consumption * **MCP tool scoping** — controls which tools an AI agent can use via the [MCP Server](/mcp-server) You can create a key with any combination of these capabilities, and add more later without recreating the key. ## Prerequisites * You need the **Project Keys** write permission (included in the **Admin** and **Data Admin** roles) * **Kafka access** requires an AWS-hosted project with a dedicated namespace — if your project doesn't meet this, the Kafka option will be disabled with a tooltip explaining why ## Create a Project Key Navigate to **Project Settings** > **Project Keys** and click **Create Project Key**. The wizard walks you through six steps. ### Step 1: Basics Enter a **Name** (required, up to 100 characters) and an optional **Description** for the key. Create Project Key wizard — basics step with name and description fields ### Step 2: API Access Choose what this key can access in the Streamkap REST API. You can skip this step to create a Kafka-only key. Assign access using one of two modes: * **By Role** (recommended) — select one or more roles (e.g. Admin, Data Admin, Read Only). Roles can be changed after creation. * **By Permission** — select individual permissions for fine-grained control. Fine-grained permissions cannot be changed after the key is created. Use role-based access if you need flexibility. API Access step showing role assignment ### Step 3: MCP Scoping If this key will be used by an AI agent via the [MCP Server](/mcp-server), you can restrict which tools the agent can call. Skip this step for CLI, Terraform, or direct API use. Choose a **Tool Profile** preset, or fine-tune access with allowed/blocked tool lists: | Profile | Description | | -------------- | --------------------------------------------------------------------- | | Full | All tools — no restrictions | | Read Only | Read/query tools only — no create, update, delete, or Kafka produce | | Agent Operator | Read + safe operations (pause, resume, restart) — no create or delete | | Infra Admin | Full infrastructure management — no direct Kafka data access | You can further customize with: * **Allowed tools** — only these tools are available (overrides profile) * **Blocked tools** — remove specific tools from the profile Tool scoping is enforced server-side. The credential file includes the scoping configuration for reference, but the MCP server always checks the authoritative settings stored in Streamkap — editing the file has no effect. MCP Tool Scoping step with profile selector and tool lists ### Step 4: Kafka Access Toggle **Enable topic read/write** to create dedicated Kafka credentials alongside your key. This gives the key its own SASL username, proxy endpoints, and ACL rules. When enabled, configure: * **Username** — 3-24 characters, lowercase letters, numbers, or hyphens. Cannot start or end with a hyphen. * **Password** — 12-128 characters with at least one uppercase letter, one lowercase letter, one digit, and one special character * **Safe listed IPs** (optional) — restrict connections to specific IP addresses or CIDR ranges (e.g. `192.168.1.0/24`) * **Create Schema Registry credentials** (optional) — generates additional credentials for schema management * **Kafka ACLs** (optional) — define which topics and consumer groups this key can access. Click **+ Add ACL** to add rules, or **Import .CSV** for bulk import. Each ACL rule specifies: * **Name** — the topic name, consumer group name, or prefix to match * **Resource** — `TOPIC` or `GROUP` * **Operation** — `READ`, `WRITE`, `CREATE`, `DELETE`, `ALTER`, `DESCRIBE`, `ALL`, etc. (GROUP resources support `READ`, `DELETE`, and `DESCRIBE`) * **Pattern Type** — `LITERAL` (exact match) or `PREFIXED` (matches resources starting with the name) Kafka Access step with username, password, safe listed IPs, and ACL configuration ### Step 5: Review & Create Review your configuration and click **Create**. Review step showing summary of all settings ### Step 6: Credentials After creation, the credential file is displayed **once**. You can view it as JSON or Base64, and you must download or copy it before continuing. Secrets are only shown once. If you lose them, you'll need to delete the key and create a new one. * **Download .json** — saves as `{key-name}-credentials.json` * **Copy JSON** or **Copy Base64** — copies to clipboard The Base64 format is useful for environment variables, Docker secrets, or CI/CD configs. Credential file display with download and copy options ### Credential File Structure The downloaded file contains everything needed to connect: ```json theme={null} { "type": "streamkap_project_key", "project_key_id": "c1e9c152-...", "project": { "service_id": "65e94bbe...", "name": "Production" }, "api": { "client_id": "894d9f53-...", "client_secret": "9ea4e916-...", "token_endpoint": "https://api.streamkap.com/auth/access-token", "api_url": "https://api.streamkap.com", "roles": ["eb6b0ab4-..."] }, "kafka": { "username": "my-consumer", "password": "your-password", "bootstrap_servers": "tenant-prod-my-consumer.streamkap.net:32400,...", "security_protocol": "SASL_SSL", "sasl_mechanism": "PLAIN", "schema_registry_url": null }, "tool_profile": "agent-operator", "allowed_tools": null, "blocked_tools": ["streamkap_delete_pipeline"], "created_at": "2026-03-26T15:21:16.354052Z" } ``` The `api` section is `null` for Kafka-only keys, and the `kafka` section is `null` for API-only keys. ## Authentication A Project Key with API access can authenticate with the Streamkap API in two ways. ### Option 1: Pass the entire credential file Send the full credential file as the `project_key` field to the [Get Access Token](/api-reference/authentication/access-token) endpoint. The file can be passed as **Base64-encoded** string or as a **raw JSON** object — both formats are accepted. ```bash theme={null} # Base64-encode the credential file PK_BLOB=$(base64 < my-key-credentials.json) # Exchange for a JWT access token curl -X POST https://api.streamkap.com/auth/access-token \ -H "Content-Type: application/json" \ -d "{\"project_key\": \"$PK_BLOB\"}" ``` Use **Base64** when passing the key as an environment variable, HTTP header, or in contexts that don't support nested JSON. Use **raw JSON** when calling the API directly and nesting is convenient. ### Option 2: Extract Client ID and Secret You can extract `api.client_id` and `api.client_secret` from the credential file and pass them as `client_id` / `secret` in the request body — the same fields used by standalone API tokens. This works with the interactive [API Reference](/api-reference/authentication/access-token) playground. ```bash theme={null} curl -X POST https://api.streamkap.com/auth/access-token \ -H "Content-Type: application/json" \ -d '{"client_id": "", "secret": ""}' ``` Both options return `access_token` and `refresh_token`. Use the access token as a Bearer token for all subsequent API calls. ## Using Your Project Key [Authenticate](#authentication) using the full credential file or extracted Client ID and Secret, then use the JWT to call any endpoint: ```bash theme={null} curl -X GET https://api.streamkap.com/api/pipelines \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` You can also try this interactively in the [API Reference](/api-reference/authentication/access-token) playground using the extracted `client_id` and `secret`. The [MCP Server](/mcp-server) accepts a Project Key directly — no need to extract individual credentials. This is the recommended way to authenticate AI agents, as it also enables [MCP tool scoping](#step-3-mcp-scoping). **Local mode (stdio):** ```bash theme={null} STREAMKAP_PROJECT_KEY=$(base64 < my-key-credentials.json) \ npx -y @streamkap/tools ``` **Remote mode (HTTP):** ```bash theme={null} claude mcp add --scope user \ --header "X-Streamkap-Project-Key: $(base64 < my-key-credentials.json)" \ --transport http \ streamkap https://mcp.streamkap.com/mcp ``` Or in `.mcp.json`: ```json theme={null} { "mcpServers": { "streamkap": { "type": "http", "url": "https://mcp.streamkap.com/mcp", "headers": { "X-Streamkap-Project-Key": "" } } } } ``` When using a Project Key, tool scoping is enforced automatically based on the key's configuration. See the [MCP Server](/mcp-server) docs for setup instructions for other clients (VS Code Copilot, Claude Desktop, etc.). The [Streamkap CLI](/cli) uses the Client ID and Secret from the credential file: ```bash theme={null} export STREAMKAP_CLIENT_ID="" export STREAMKAP_CLIENT_SECRET="" streamkap doctor # Verify connectivity streamkap pipelines list ``` API-only and API+Kafka keys work with the CLI. Kafka-only keys cannot be used with the CLI. Use the credentials from the `kafka` section of the file: ```python theme={null} from confluent_kafka import Consumer consumer = Consumer({ "bootstrap.servers": "tenant-prod-my-consumer.streamkap.net:32400,...", "security.protocol": "SASL_SSL", "sasl.mechanism": "PLAIN", "sasl.username": "my-consumer", "sasl.password": "your-password", "group.id": "my-consumer-group", }) consumer.subscribe(["my-topic"]) ``` Connection details: | Setting | Value | | ----------------- | ----------------------------------------------------- | | Bootstrap Servers | From `kafka.bootstrap_servers` in the credential file | | Security Protocol | `SASL_SSL` | | SASL Mechanism | `PLAIN` | | Username | From `kafka.username` | | Password | From `kafka.password` | If Schema Registry was enabled, the URL and credentials are also included in the file. Use the Client ID and Secret from the `api` section: ```hcl theme={null} provider "streamkap" { client_id = "" client_secret = "" } ``` See [Terraform Provider](/streamkap-provider-for-terraform) for full configuration. ## Use with Streaming Agents A Project Key can authenticate the **Streamkap MCP** tool used by [streaming agents](/streaming-agents), letting an agent operate your Streamkap platform during a run. Enable **Use with agents** on the key — the toggle is available when you create a key and on the edit page for an existing key. Once enabled, the key becomes selectable as the Streamkap MCP credential in the agent builder, and its [MCP tool scoping](#step-3-mcp-scoping) controls which platform tools the agent can call. Disabling **Use with agents** breaks any agent bound to that key. Re-enable it, or point the agent at another agentic-enabled key. ## Manage Project Keys ### Viewing Keys The **Project Keys** list shows all keys for the current project with their status, access type, roles, and creation date. Project Keys list page showing keys with status, access type, and actions ### Key Status | Status | Meaning | | ----------------- | -------------------------------------------- | | **Active** | Ready to use | | **Creating** | Provisioning in progress | | **Failed** | Creation failed — you can retry or delete | | **Deleting** | Deletion in progress | | **Delete Failed** | Deletion partially failed — retry the delete | ### Editing a Key Click the **Edit** button on any active key to change: * **Name** and **Description** * **Roles** (if the key uses role-based API access) * **MCP tool scoping** (profile, allowed tools, blocked tools) * **Kafka ACLs** and **Safe listed IPs** (if the key has Kafka access) The sidebar shows the key's capabilities, status, identifiers, and metadata (including last used time and token TTL). Role changes take effect when existing access tokens expire (typically within a few hours). The key does not need to be recreated. Edit Project Key page showing key details, access control, MCP scoping, and overview sidebar ### Adding Capabilities You can add API or Kafka access to an existing key without recreating it: * **Add Kafka to an API-only key** — click **Add Kafka Access** on the edit page and configure username, password, and ACLs * **Add API to a Kafka-only key** — click **Add API Credentials** and assign roles or permissions When you add a capability, new credentials are shown once (just like at creation). Your existing credentials remain unchanged. ### Rotating Kafka Password On the edit page for a key with Kafka access, use the **Rotate Password** option to set a new password. The new password takes effect immediately. Rotating the password will disconnect any active Kafka consumers using the old password. Coordinate with your team before rotating. ### Deleting a Key Click the **Delete** button to permanently remove a key and all its associated resources (API token, Kafka user, proxy endpoints). This action cannot be undone. If deletion fails (e.g. due to a transient infrastructure issue), the key moves to **Delete Failed** status. Click **Retry Delete** to try again. ## Linked Resources API tokens and Kafka users created by a Project Key are managed exclusively through the Project Key. They are protected from independent modification — edits and deletions must be done from the Project Key's edit page. ## Access Types at a Glance | Type | REST API | Kafka | MCP Scoping | Requirements | | --------------- | -------- | ----- | ----------- | ------------------------ | | **API Only** | Yes | No | Optional | Any cloud provider | | **Kafka Only** | No | Yes | No | AWS, dedicated namespace | | **API + Kafka** | Yes | Yes | Optional | AWS, dedicated namespace | You can start with API-only or Kafka-only and add the other capability later. ## Related * [MCP Server](/mcp-server) — connect AI agents to Streamkap * [CLI](/cli) — command-line management tool * [Terraform Provider](/streamkap-provider-for-terraform) — infrastructure as code for Streamkap resources * [API Reference](/api-reference/authentication/access-token) — full REST API documentation * [Team Management](/team-management) — manage roles and permissions # Projects Source: https://docs.streamkap.com/projects Manage and switch between Streamkap projects across regions and environments A Streamkap project is the complete infrastructure stack for your Change Data Capture (CDC) pipelines, including Apache Kafka, Apache Flink, Debezium, connectors, and supporting components. Projects are isolated environments that can represent different stages (Development, Staging, Production) or geographic regions. ## Overview The Projects page displays all projects in your organization with their current status: Projects list view showing project cards with status and region information ## Project Architecture Each Streamkap project includes: * **Pipelines**: pipelines streaming source topics and/or transformed topics to destinations * **Source Connectors**: Database CDC connectors (MySQL, PostgreSQL, DynamoDB, etc.) * **Destination Connectors**: Data warehouse/lake connectors (Snowflake, BigQuery, ClickHouse, etc.) * **Transforms**: Data transformation engine * **Topics**: Kafka topics for data streams * **Monitoring**: Metrics, logs, and alerts infrastructure ## Project States `AVAILABLE`: The project is fully operational and ready to use: * All infrastructure components are running * Connectors can be created and managed * Data pipelines can process events * Marked with green badge * Shows `CURRENT` if you're actively using this project `UNAVAILABLE`: The project exists but is not currently operational: * Infrastructure is paused or stopped * Cannot create or modify connectors * Data pipelines are not processing * Marked with gray badge Projects in `UNAVAILABLE` state do not incur compute charges, but may retain data depending on your configuration. ## Projects List The Projects page displays the following information for each project: * **Name**: Project identifier * Shows `CURRENT` badge for the active project * **Type**: Project environment type (e.g., `production`, `development`) * **Created On**: Timestamp when the project was provisioned * **State**: Current operational status (`AVAILABLE`, `UNAVAILABLE`) * **Cloud**: Cloud provider (AWS, Azure, GCP) * **Region**: Geographic region (e.g., `us-west-2`, `us-east-1`, `ap-southeast-2`) ### Search Use the search bar to filter projects by name: * Type to filter the project list in real-time * Useful when managing many projects across environments ## Switching Between Projects To switch from one project to another: Click on your organization name or the Projects link in the navigation to view all available projects. Click on the project you want to switch to in the projects list. The page will reload and you'll be redirected to the selected project's dashboard. Check the project selector in the top navigation bar to confirm you're in the correct project. Switching projects changes the entire context of the application. All pages (Pipelines, Connectors, Topics, etc.) will now show data for the selected project. ## Project Settings Click on a project to access its settings. The Project Settings page contains multiple tabs for managing different aspects of your project: Project Settings page showing Details, Location, API, and Kafka Access tabs ### Details Tab View and manage basic project information: * **Project Name**: The display name for this project * **Project ID**: Unique identifier (use "Copy" button for API calls and support tickets) * **Environment Type**: Production, Development, Staging, etc. * **Created Date**: When the project was provisioned ### Location Tab View the deployment location for this project: * **Cloud Provider**: AWS, Azure, or GCP * **Region**: Geographic region where the project is deployed ### API Tab Access API configuration for programmatic access: * **API Tokens**: Navigate to create and manage API tokens for this project * **API Documentation**: Links to API reference documentation ### Kafka Access Tab Manage Kafka connectivity for external clients: * **Kafka Brokers**: Copy the broker connection string for external client connections * **Kafka Users**: Manage authentication credentials for Kafka access See the [Kafka Access](/kafka-access) page for detailed information on creating and managing Kafka users. ## Common Use Cases ### Environment Separation Maintain separate projects for different environments: **Development Project:** * Test schema changes and new connectors * Experiment with transforms and pipelines * Lower resource allocation, smaller dataset * Region: Choose closest to development team **Staging Project:** * Mirror production configuration * Run integration tests * Validate deployments before production * Same region as production for realistic testing **Production Project:** * Live data pipelines * High availability configuration * Optimal region for data sources * Monitoring and alerting enabled ### Geographic Distribution Create projects in multiple regions for: **Data Locality:** * Reduce latency by placing projects near data sources * Comply with data residency regulations (GDPR, etc.) * Example: EU project for European databases, US project for American databases **Disaster Recovery:** * Replicate pipelines across regions * Failover capability for business continuity * Active-active or active-passive configurations **Global Operations:** * Support teams in different time zones * Reduce network latency for distributed teams * Scale horizontally across regions ## Creating New Projects Project creation is managed by Streamkap support to ensure proper infrastructure provisioning and configuration. To request a new project: Reach out via the **Help** button in the top navigation bar or email [support@streamkap.com](mailto:support@streamkap.com). Specify the following: * **Environment type**: Production, Development, Staging, etc. * **Cloud provider**: AWS, Azure, or GCP * **Region**: Geographic location (e.g., us-west-2, eu-central-1) * **Expected throughput**: Data volume and connector count * **Special requirements**: VPC peering, private link, compliance needs Streamkap will provision the infrastructure: * Kafka cluster setup * Flink deployment * Networking configuration * Monitoring setup You'll receive confirmation when the project is ready, and it will appear in your Projects list. ## Managing Project Resources Each project has independent resources: ### Connectors Sources and destinations are project-specific: * Must be recreated in each project * Cannot be moved between projects * Configuration can be exported/imported ### Pipelines Data pipelines exist within a single project: * Define data flow from source to destination * Cannot span multiple projects * Must be recreated in each environment ### Topics Kafka topics are isolated to each project: * Topic names can be reused across projects * Data is not shared between projects * Replication limited to project boundaries ### Billing Usage is tracked per project: * View project-specific usage in Billing page * Switch projects to see different usage metrics * Projects are billed independently ## Best Practices 1. **Use Consistent Naming**: Name projects clearly (e.g., "Production-US-East", "Development-EU") 2. **Document Project Purpose**: Maintain documentation of what each project is for 3. **Limit Production Access**: Restrict who can switch to production projects 4. **Test in Non-Production First**: Always validate changes in dev/staging before production 5. **Monitor Project Health**: Regularly check project state and resource usage 6. **Copy IDs for Automation**: Store project IDs in your infrastructure as code 7. **Keep Kafka Brokers Secure**: Treat broker connection strings as sensitive data 8. **Rotate API Tokens**: Regularly rotate tokens for security 9. **Plan for Multi-Region**: Consider geographic distribution for global operations 10. **Align Regions with Data**: Place projects near your data sources to reduce latency ## Troubleshooting ### Project Shows UNAVAILABLE If a project is unexpectedly unavailable: 1. **Check Project Status**: Contact Streamkap support for infrastructure status 2. **Verify Billing**: Ensure account is in good standing 3. **Review Recent Changes**: Check if project was intentionally paused 4. **Check Maintenance Window**: Project may be undergoing scheduled maintenance ### Cannot Switch Projects If you cannot switch to another project: 1. **Refresh Page**: Reload the browser page 2. **Clear Cache**: Clear browser cache and cookies 3. **Check Permissions**: Verify you have access to the target project 4. **Contact Support**: Reach out if the issue persists ### Kafka Brokers Not Connecting If external clients can't connect using the broker connection string: 1. **Verify Credentials**: Ensure you have valid Kafka Access credentials 2. **Check Network**: Verify network connectivity and firewall rules 3. **Confirm Project State**: Project must be AVAILABLE for connections 4. **Review Security**: Check SSL/TLS configuration if required 5. **Use Correct Port**: Verify you're using the correct port (usually 9092 or 9093 for SSL) ### API Token Not Working If API tokens don't authenticate: 1. **Verify Token Scope**: Ensure token is for the correct project 2. **Check Expiration**: Tokens may have expiration dates 3. **Confirm Permissions**: Token must have required permissions for the operation 4. **Regenerate Token**: Create a new token if issues persist ## Related Documentation * [Kafka Access](/kafka-access) - Manage Kafka users and obtain credentials for broker connections * [Pipelines](/pipelines) - Create and manage data pipelines within a project * [Sources](/sources) - Configure source connectors in your project * [Destinations](/destinations) - Configure destination connectors in your project * [Usage](/usage) - View organization-level usage and billing information * [API Reference](/api-reference/authentication/access-token) - Use API tokens for programmatic project management # Cloudflare R2 Source: https://docs.streamkap.com/r2 Change Data Capture using Cloudflare R2 with Streamkap # Requirements * Cloudflare R2 Access Key and Secret Access Key with the following permissions to the destination bucket: * `Object Read & Write` : Allows the ability to read, write, and list objects in specific buckets. # Configure Cloudflare R2 Connector * `Name`: A descriptive name for the connector * `Access Key`: An Access Key with the appropriate permissions for the bucket to which Streamkap will load data * `Secret Access Key`: The Secret Access Key with the appropriate permissions for the bucket to which Streamkap will load data * `Account ID`: Cloudflare Account ID. This is required to access the R2 bucket. * `Bucket Name`: The name of the bucket to which Streamkap will load data * `Format`: The format of the file. The following options are available: `CSV`, `JSONL`, `JSON` and `Parquet` * `Filename Template`: The format of the filename. See below for more information about formatting options. * `Compression Type`: Compression type for output files. Supported algorithms are `gzip`, `snappy`, `zstd` and `none`. Defaults to `gzip` * `Output Fields`: List of fields to include in output. Available options are: `key`, `offset`, `timestamp`, `value` and `headers`. Defaults to `value`. **Filename template** (default: `{{topic}}--{{partition}}--{{start_offset}}`): The format of the filename. You can combine any of the elements below using other text or characters, including dashes (`-`) and underscores (`_`) | Element | Description | | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `{{topic}}` | The Streamkap topic name. For example, a PostgreSQL Source table `web.salesorders` topic's name would be `salesorders` | | `{{partition:padding=true\|false}}` | The partition number of the records in the file, typically `0`. Streamkap topics and their data can be partitioned for better performance in certain scenarios. For example, a topic `salesorders` has 10 partitions, 0 through to 9. If padding set to true it will set leading zeroes for offset, the default value is false; | | `{{start_offset:padding=true\|false}}` | The offset number of the first record in the file. Every record streamed has an incrementing offset number. For example, a topic `salesorders` has 1000 records, offsets 0 through to 999. Note that in the case of a multi-partitioned topic, offset numbers are **not unique** across partitions. If padding set to true it will set leading zeroes for offset, the default value is false; | | `{{timestamp:unit=yyyy\|MM\|dd\|HH}}` | The timestamp for when the file was created by the Connector. For example, the template `{{topic}}{{timestamp:unit=yyyy}}-{{timestamp:unit=MM}}` and timestamp of `2024-01-01 20:24` would create a file named `salesorders2024-01` | | `{{key}}` | The Kafka key | Currently, supported combinations of variables are: * topic, partition, start\_offset, and timestamp - grouping by the topic, partition, and timestamp; * key - grouping by the key. * key, topic, partition - grouping by the topic, partition, and key. # Kafka (Reading) Source: https://docs.streamkap.com/reading-from-kafka Read directly from Kafka Customers on a paid plan can enable direct access via Proxy. This guide shows you how to read messages from your Streamkap Kafka topics using Python or command-line tools. ## Creating Kafka Users You can create and manage Kafka users through the Streamkap web interface at [Kafka Access](https://app.streamkap.com/kafka-access). To create a new Kafka user, click the "Create User" button. This will open the user creation dialog where you can configure the user's permissions and access settings. For detailed step-by-step instructions on creating and managing Kafka users through the UI, see the [Kafka Access](/kafka-access) documentation. ### User Configuration When creating a Kafka user, you'll need to configure: * **Username**: Enter a lowercase username for the Kafka user * **Password**: Set a secure password for authentication * **Safe listed IPs**: Specify IP addresses or CIDR ranges that are allowed to connect * **Kafka ACLs**: Configure access control lists to define what the user can do ### Access Control Lists (ACLs) Kafka ACLs control what operations users can perform on specific resources. When creating a user, you can configure: * **Resource Type**: * `TOPIC` - Controls access to Kafka topics * `GROUP` - Controls access to consumer groups * **Operation**: The type of operation allowed (varies by resource type)
**For `TOPIC` resources:** * `ALL` - All operations * `WRITE` - Write/produce messages * `READ` - Read/consume messages * `ALTER` - Modify resource configurations * `ALTER_CONFIGS` - Modify resource configurations * `CREATE` - Create new resources * `DELETE` - Delete resources * `DESCRIBE` - View resource metadata * `DESCRIBE_CONFIGS` - View resource configurations **For `GROUP` resources (consumers only):** * `READ` - Join and consume from consumer group * `DELETE` - Delete consumer group * `DESCRIBE` - View consumer group metadata * **Pattern Type**: How the resource name is matched * `LITERAL` - Exact match of the resource name * `PREFIXED` - Match resources with the specified prefix * **Name**: The specific resource name or prefix to apply the ACL to ### Connection Details Once a user is created, your endpoints are shown under "Proxy Endpoints". These endpoints follow the naming pattern: `-.streamkap.net:PORT` Where: * `` - Your Streamkap service/tenant name * `` - The Kafka user's username * `PORT` - One of the available ports: 32400, 32401, or 32402 **Example proxy endpoints:** * `my-service-kafka-user.streamkap.net:32400` * `my-service-kafka-user.streamkap.net:32401` * `my-service-kafka-user.streamkap.net:32402` **Connection settings:** * **Security protocol**: `SASL_SSL` (recommended for secure connections) * **SASL mechanism**: `PLAIN` * **Username/password**: As configured for the user ### Required Permissions To read from Kafka topics, your user needs these ACL permissions: **Essential permissions (always required):** * **Resource Type**: `TOPIC` | **Operation**: `READ` | **Pattern Type**: `LITERAL` or `PREFIXED` | **Name**: Your topic name/prefix **Consumer group permissions (required for Python only):** * **Resource Type**: `GROUP` | **Operation**: `READ` | **Pattern Type**: `LITERAL` or `PREFIXED` | **Name**: Your consumer group ID **Additional permissions (commonly required):** * **Resource Type**: `TOPIC` | **Operation**: `DESCRIBE` | **Pattern Type**: `LITERAL` or `PREFIXED` | **Name**: Your topic name/prefix `TOPIC READ` is always required. `GROUP READ` is only required for Python consumers (the confluent-kafka library always uses consumer groups). CLI tools like kcat work with just `TOPIC READ` permissions. Add `DESCRIBE` permissions if your client needs metadata access. ## Code Examples ### Prerequisites Install the required packages: ```bash theme={null} pip install confluent-kafka certifi ``` Install kcat: ```bash theme={null} # macOS brew install kcat # Ubuntu/Debian sudo apt-get install kcat ``` ```python Python theme={null} from confluent_kafka import Consumer import socket import certifi conf = { 'bootstrap.servers': '-.streamkap.net:32400,-.streamkap.net:32401,-.streamkap.net:32402', 'security.protocol': 'SASL_SSL', 'sasl.mechanism': 'PLAIN', 'sasl.username': '', 'sasl.password': '', 'group.id': '', 'client.id': socket.gethostname(), 'auto.offset.reset': 'earliest', # Required to trust AWS root certificates 'ssl.ca.location': certifi.where(), } consumer = Consumer(conf) consumer.subscribe(['']) while True: msg = consumer.poll(1.0) if msg is None: continue if msg.error(): print(f"Consumer error: {msg.error()}") continue print(f"Received message: {msg.value().decode('utf-8')}") consumer.close() ``` ```bash CLI (Latest Message) theme={null} # Consume latest messages (real-time) kcat -b -.streamkap.net:32400 \ -X security.protocol=SASL_SSL \ -X sasl.mechanisms=PLAIN \ -X sasl.username= \ -X sasl.password= \ -C -t -K \| -c1 -o -1 ``` ```bash CLI (All Messages) theme={null} # Consume from beginning (all messages) kcat -b -.streamkap.net:32400 \ -X security.protocol=SASL_SSL \ -X sasl.mechanisms=PLAIN \ -X sasl.username= \ -X sasl.password= \ -C -t -o beginning ``` ```bash CLI (Direct Partition) theme={null} # Read from specific partition/offset (requires only TOPIC READ) kcat -b -.streamkap.net:32400 \ -X security.protocol=SASL_SSL \ -X sasl.mechanisms=PLAIN \ -X sasl.username= \ -X sasl.password= \ -C -t -p 0 -o 0 -c1 ``` **Replace the following values in the examples above:** Your proxy endpoints are listed in the Streamkap web interface at [Kafka Access](https://app.streamkap.com/kafka-access) under "Proxy Endpoints". The format is `-.streamkap.net:PORT`. * `-` - Your proxy endpoints * `` - Your Kafka user username * `` - Your Kafka user password * `` - The topic you want to read from * `` - Your consumer group ID (Python only) ## Integrations Here are quick links to some integrations that support reading from Kafka: * Materialize: [link to docs](https://materialize.com/docs/sql/create-source/kafka/) * Tinybird: [link to docs](https://www.tinybird.co/docs/get-data-in/connectors/kafka) * SingleStore: [link to docs](https://docs.singlestore.com/cloud/load-data/load-data-with-pipelines/how-to-load-data-using-pipelines/load-data-from-kafka/) * Starburst: [link to docs](https://docs.starburst.io/starburst-galaxy/working-with-data/data-ingest/kafka-streaming-ingestion.html) * StarTree: [link to docs](https://dev.startree.ai/docs/use-data-manager/kafka) * Microsoft Fabric: [link to docs](https://learn.microsoft.com/en-us/fabric/real-time-intelligence/event-streams/add-source-apache-kafka) ## Troubleshooting Before diving into complex debugging, verify basic network connectivity to your Streamkap Kafka cluster. **Test DNS Resolution:** ```bash theme={null} # Check if hostname resolves nslookup -.streamkap.net ``` **Test Port Connectivity:** ```bash theme={null} # Test with netcat (preferred - quick and clean) nc -zv -.streamkap.net 32400 # Test all three ports nc -zv -.streamkap.net 32400 nc -zv -.streamkap.net 32401 nc -zv -.streamkap.net 32402 # Alternative with telnet (press Ctrl+C to exit after connection success) telnet -.streamkap.net 32400 ``` **Test SSL/TLS Handshake:** ```bash theme={null} # Test SSL handshake and certificate chain openssl s_client -connect -.streamkap.net:32400 -servername -.streamkap.net # Alternative with timeout (press Ctrl+C to exit) echo "Q" | openssl s_client -connect -.streamkap.net:32400 -servername -.streamkap.net ``` **Common Network Issues & Solutions:** * **VPN interference**: Disconnect VPN and try again * **Firewall blocking ports**: Ensure ports 32400-32402 are accessible * **Safe listed IPs**: Verify your public IP address is in the user's safe list If basic connectivity fails, check your network configuration before proceeding with Kafka-specific troubleshooting. **Common Errors**: * `SSL connection closed by peer` during message production * SSL certificate verification failures * `SASL authentication failed` or authentication errors * SSL handshake failures **Authentication Solutions**: 1. Verify username and password are correct 2. Ensure `sasl.mechanism` is set to `PLAIN` and `security.protocol` is set to `SASL_SSL` 3. Check that the user account is active and not disabled 4. Confirm the user has basic connection permissions **SSL Solutions**: 1. **For Python**
Ensure certificates are properly configured: ```bash theme={null} pip install --upgrade certifi ``` ```python theme={null} import certifi 'ssl.ca.location': certifi.where() ``` 2. **For CLI tools**
Try different certificate paths: ```bash theme={null} -X ssl.ca.location=/etc/ssl/cert.pem # or -X ssl.ca.location=/etc/ssl/certs/ca-certificates.crt ``` 3. **Disable hostname verification** (temporary): ```python theme={null} 'ssl.endpoint.identification.algorithm': 'none' ``` 4. **Contact support** if issues persist - may require infrastructure team resolution **Note**: Metadata operations (listing topics) may work while data operations fail
**Error**: `Topic authorization failed` or `TOPIC_AUTHORIZATION_FAILED` **Cause**: Missing `TOPIC READ` or `TOPIC WRITE` permissions **Solution**: Add the appropriate ACL permissions: * **Resource Type**: `TOPIC` * **Operation**: `READ` (for consumers) or `WRITE` (for producers) * **Pattern Type**: `LITERAL` or `PREFIXED` * **Name**: Your topic name or prefix **Error**: `GROUP_AUTHORIZATION_FAILED` or `Group authorization failed` **Cause**: Missing `GROUP READ` permissions for your consumer group **Solution**: Add the following ACL permission: * **Resource Type**: `GROUP` * **Operation**: `READ` * **Pattern Type**: `LITERAL` or `PREFIXED` * **Name**: Your consumer group ID (e.g., `my-consumer-group`) **Note**: This only affects Python consumers and CLI tools using consumer groups **Issue**: Consumer polls but receives no messages **Possible Causes**: 1. **No messages in topic**: Topic is empty or messages are at different offsets 2. **Consumer group offset**: Group has already consumed available messages 3. **Partition assignment**: Messages might be in different partitions 4. **Offset reset**: Check `auto.offset.reset` setting **Solutions**: 1. **Check topic contents**: Use CLI to verify messages exist 2. **Use fresh consumer group**: Try with a new `group.id` 3. **Reset offsets**: Set `auto.offset.reset` to `earliest` 4. **Check all partitions**: For CLI, try without specifying partition
# Overview Source: https://docs.streamkap.com/redis-destination Stream data to Redis * [Redis Cloud](/redis-destination-cloud) * [Redis (Generic)](/redis-destination-generic) If you don't see your platform listed, please contact us as we're happy to help find and provide the setup documentation you need. # Redis Cloud Source: https://docs.streamkap.com/redis-destination-cloud Stream data into Redis Cloud # Prerequisites * A Redis Cloud user account with permissions to manage users and roles # Granting Privileges It's recommended to create a separate user and role for Streamkap to access your Redis Cloud database. ## Redis Cloud Console ### Create Role 1. In the left hand navigation menu, go to the [Data Access Control > Roles](https://app.redislabs.com/#/data-access-control/roles) tab 2. Click the + button to create a new role and enter the role name `streamkap_role` 3. For **ACL Rule** choose `Read-Write` 4. For **Databases** choose the database Streamkap will stream data to 5. Click the check mark to confirm the association 6. Click **Save** ### Create User 1. In the left hand navigation menu, go to the [Data Access Control > Users](https://app.redislabs.com/#/data-access-control/users) tab 2. Click the + button to create a new user an enter the user name `streamkap_user` 3. For **Role** choose the `streamkap_role` (created earlier) 4. Enter a strong password (minimum of 8 alphanumeric characters including upper-case and lower-case characters, and symbols) 5. Click the check mark to create the user # Get Connection Details ## Redis Cloud Console 1. In the left hand navigation menu, go to the [Databases](https://app.redislabs.com/#/databases) page 2. Click on the database Streamkap will stream data to 3. Under the **General** section, copy the **Public endpoint** ## RedisInsight-v2 App Assuming you already have the Redis database added to the app: 1. Open the **My Redis databases** page (opens by default or by clicking on the Redis icon in the left hand navigation menu) 2. Copy the **Host:Port** # Streamkap Setup 1. Create a new [Redis Cloud Destination](https://app.streamkap.com/connectors/destinations/new/rediscloud) 2. Enter the following information: 1. **Name** - A unique and memorable name for this Connector 2. (Optional) **Redis Cluster** (default: `No`) - If you're connecting to a Redis Cluster rather than a Redis Standalone instance, select `Yes` 3. **Hostname** - The **Public endpoint** or **Host:Port** copied earlier 4. **Username** (Case sensitive) - `streamkap_user` or the username you chose 5. **Password** - The password for the user 6. (Optional) **Enable TLS?** (default: `Yes`) - Establish a secure TLS connection 7. (Optional) **Allow Insecure Connections?** (default: `No`) - Allow insecure connections (e.g. invalid certificates) when using SSL 8. redis.key.file 9. redis.key.cert 10. redis.key.password 11. redis.cacert 12. redis.charset 13. redis.command 14. redis.multiexec 3. Click **Save** # Redis (Generic) Source: https://docs.streamkap.com/redis-destination-generic Stream data into Redis ## Prerequisites * Redis version ≥ 6.0 * A Redis user with sufficient privileges to write data * Network access from Streamkap to your Redis instance ## Redis Setup ### 1. Grant Database Access * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your Redis instance. ### 2. Create Database User It's recommended to create a separate user for the Connector to access your Redis instance. Below is an example that creates a user with appropriate permissions. ```bash Redis CLI theme={null} # Create a user for Streamkap (Redis 6.0+) # Replace {password} with a strong password ACL SETUSER streamkap_user on >{password} ~* +@write +@read +@connection # Verify the user was created ACL LIST ``` The ACL configuration above grants: * `on`: Activates the user * `>{password}`: Sets the password * `~*`: Allows access to all keys * `+@write`: Allows all write commands * `+@read`: Allows all read commands * `+@connection`: Allows connection-related commands **Redis ACL compatibility** ACL support requires Redis 6.0 or higher. For earlier versions, you can use the `requirepass` setting in `redis.conf` for password authentication, though this provides less granular access control. ### 3. Connection Details You'll need the following information for the Streamkap Setup: * **Hostname/IP**: The Redis server hostname or IP address * **Port**: Default is `6379` * **Database**: Redis database number (0-15 by default) * **Username**: The username created above (if using ACL) * **Password**: The password for the user **TLS/SSL Connections** If your Redis instance requires TLS/SSL, ensure you have: * Valid certificates * The correct hostname that matches the certificate * TLS enabled on your Redis instance *** ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Destination * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Destinations). * Choose **Redis**. ### 2. Connection Settings * **Name**: Enter a unique and memorable name for this Connector. * **Redis Type**: Choose the type of Redis deployment: * **Standalone**: Single Redis instance * **Cluster**: Redis Cluster deployment * **Hostname**: Specify the hostname or IP address of your Redis server. * **Port**: Default is `6379`. * **Database**: Specify the Redis database number to stream data to (0-15 by default). * Note: Not applicable for Redis Cluster deployments. * **Connect via SSH Tunnel**: The Connector will connect to an SSH server in your network which has access to your Redis instance. This is necessary if the Connector cannot connect directly. * See [SSH Tunnel](/ssh-tunnel) for setup instructions. * **Username**: Username to access Redis (case sensitive). By default, Streamkap scripts use `streamkap_user`. * Leave empty for Redis versions \< 6.0 without ACL support. * **Password**: Password to access the Redis instance. * **Enable TLS?**: Whether to use an encrypted TLS/SSL connection to the Redis server. Default is `Yes`. * **Allow Insecure Connections?**: Allow connections with invalid or self-signed certificates. Default is `No`. * Not recommended for production environments. ### 3. Data Format Settings * **Redis Command**: The Redis command to use for writing data. Options include: * **SET**: Simple key-value pairs (default) * **HSET**: Hash maps * **LPUSH**: List (left push) * **RPUSH**: List (right push) * **SADD**: Set * **ZADD**: Sorted set * **XADD**: Redis Stream * **Key Pattern**: Pattern for generating Redis keys from Kafka records. Supports placeholders: * `${topic}`: The Kafka topic name * `${key}`: The Kafka message key * Default: `${topic}:${key}` * **Time to Live (TTL)**: Optional. Set a TTL in seconds for Redis keys. Leave empty for no expiration. ### 4. Advanced Parameters * **Batch Size**: Number of records to write in a single batch. Default is `1000`. * Adjust based on your Redis instance's performance. * **Enable Multi-Exec**: Whether to use Redis MULTI/EXEC transactions for batch writes. Default is `No`. * Improves atomicity but may impact throughput. * **Character Encoding**: Character set for encoding string values. Default is `UTF-8`. Click **Save**. ## Data Mapping The connector maps Kafka records to Redis based on the selected command: ### SET Command * **Key**: Generated from the key pattern * **Value**: The entire Kafka record value (JSON string or serialized format) ### HSET Command * **Key**: Generated from the key pattern * **Fields**: Each field in the Kafka record becomes a hash field * **Values**: Corresponding field values ### XADD Command (Redis Streams) * **Stream Name**: Generated from the key pattern * **Fields**: Kafka record fields become stream entry fields ### Collection Commands (LPUSH, RPUSH, SADD, ZADD) * **Key**: Generated from the key pattern * **Elements**: Values extracted from Kafka records ## Troubleshooting If you cannot connect to Redis:

* Verify the hostname, port, and network connectivity * Check firewall rules allow traffic on the Redis port * Ensure Redis is configured to accept connections from Streamkap's IP addresses * For Redis Cluster, verify all cluster nodes are accessible * Check TLS/SSL settings match your Redis configuration
If you're experiencing authentication issues:

* Verify the username and password are correct (usernames are case-sensitive) * For Redis 6.0+, ensure the user has appropriate ACL permissions * Check if `requirepass` is set correctly in redis.conf for Redis versions \< 6.0 * Ensure the user has write permissions to the target database
If you're experiencing slow write performance:

* Increase the batch size to write more records per batch * Enable Multi-Exec for better transaction handling * Monitor Redis metrics (CPU, memory, network I/O) * Consider using Redis Cluster for horizontal scaling * Check network latency between Streamkap and Redis
If you're experiencing unexpected key overwrites:

* Review your key pattern to ensure uniqueness * Consider including additional fields in the key pattern (e.g., timestamp) * For append operations, use LPUSH/RPUSH instead of SET * Enable TTL to automatically expire old keys * Review your data model and key naming strategy
If you encounter errors related to data types:

* Ensure the Redis command matches your data structure * Verify the Kafka record format is compatible with the selected command * Check that nested structures are properly handled for HSET commands * Review the character encoding settings for string data * Validate that sorted set scores are numeric for ZADD commands
# Overview Source: https://docs.streamkap.com/redis-source Stream data from Redis * [Redis (Generic)](/redis-source-generic) If you don't see your platform listed, please contact us as we're happy to help find and provide the setup documentation you need. # Redis Source Source: https://docs.streamkap.com/redis-source-generic Redis Change Data Capture Setup with Streamkap ## Prerequisites * Redis version ≥ 6.0 * A Redis user with sufficient privileges to read data and manage keyspace notifications * Keyspace notifications enabled for change data capture ## Redis Setup ### 1. Grant Database Access * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your Redis instance. ### 2. Enable Keyspace Notifications Keyspace notifications allow clients to receive events affecting the Redis data set in some way. The Connector relies on Redis keyspace notifications for capturing changes. Set the following parameter in your Redis configuration: * `notify-keyspace-events = KEA` This enables: * **K**: Keyspace events, published with `__keyspace@__` prefix * **E**: Keyevent events, published with `__keyevent@__` prefix * **A**: Alias for all event types (g\$lshzxe) **Keyspace notifications and performance** Enabling keyspace notifications may impact Redis performance, especially in high-throughput environments. Monitor your Redis instance after enabling this feature. * Configure notifications for specific event types if full coverage isn't needed. * Monitor memory usage and CPU utilization. * Consider using a dedicated Redis instance for CDC if performance becomes an issue. You can enable keyspace notifications via: ```bash Redis CLI theme={null} # Enable keyspace notifications via redis-cli redis-cli CONFIG SET notify-keyspace-events KEA # Verify the configuration redis-cli CONFIG GET notify-keyspace-events ``` ```conf Redis Configuration File theme={null} # Add to redis.conf notify-keyspace-events KEA ``` **Configuration persistence** When using `CONFIG SET`, changes are not persisted across Redis restarts unless you also run `CONFIG REWRITE` or update the `redis.conf` file directly. ### 3. Create Database User It's recommended to create a separate user for the Connector to access your Redis instance. Below is an example that creates a user with appropriate permissions. ```bash Redis CLI theme={null} # Create a user for Streamkap (Redis 6.0+) # Replace {password} with a strong password ACL SETUSER streamkap_user on >{password} ~* +@read +@connection +config|get # Verify the user was created ACL LIST ``` The ACL configuration above grants: * `on`: Activates the user * `>{password}`: Sets the password * `~*`: Allows access to all keys * `+@read`: Allows all read commands * `+@connection`: Allows connection-related commands * `+config|get`: Allows CONFIG GET command for verification **Redis ACL compatibility** ACL support requires Redis 6.0 or higher. For earlier versions, you can use the `requirepass` setting in `redis.conf` for password authentication, though this provides less granular access control. ### 4. Connection Details You'll need the following information for the Streamkap Setup: * **Hostname/IP**: The Redis server hostname or IP address * **Port**: Default is `6379` * **Database**: Redis database number (0-15 by default) * **Username**: The username created above (if using ACL) * **Password**: The password for the user **TLS/SSL Connections** If your Redis instance requires TLS/SSL, ensure you have: * Valid certificates * The correct hostname that matches the certificate * TLS enabled on your Redis instance *** ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **Redis**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Redis Type**: Choose the type of Redis deployment: * **Standalone**: Single Redis instance * **Cluster**: Redis Cluster deployment * **Hostname**: Specify the hostname or IP address of your Redis server. * **Port**: Default is `6379`. * **Database**: Specify the Redis database number to stream data from (0-15 by default). * **Connect via SSH Tunnel**: The Connector will connect to an SSH server in your network which has access to your Redis instance. This is necessary if the Connector cannot connect directly to your database. * See [SSH Tunnel](/ssh-tunnel) for setup instructions. * **Username**: Username to access Redis. By default, Streamkap scripts use `streamkap_user`. * Leave empty for Redis versions \< 6.0 without ACL support. * **Password**: Password to access the Redis instance. * **Enable TLS?**: Whether to use an encrypted TLS/SSL connection to the Redis server. * **Allow Insecure Connections?**: Allow connections with invalid or self-signed certificates (not recommended for production). ### 3. Advanced Parameters * **Batch Size**: Number of keys to process in a single batch. Default is `1000`. * Adjust based on your Redis instance's performance and network capacity. * **Poll Interval (ms)**: How frequently to poll Redis for changes. Default is `100`. * Lower values provide near real-time CDC but increase load on Redis. Click **Save**. ## Data Capture The Redis Source Connector captures changes to Redis keys and publishes them to Kafka topics. Each change event includes: * **Key**: The Redis key that changed * **Operation**: The type of operation (set, delete, expire, etc.) * **Value**: The current value (for set operations) * **Timestamp**: When the change occurred ### Topic Naming By default, topics are named based on the Redis database: * Pattern: `redis_db_{database_number}` * Example: `redis_db_0` for database 0 ### Supported Redis Data Types The connector supports the following Redis data types: * **String**: Simple key-value pairs * **Hash**: Hash maps * **List**: Ordered lists * **Set**: Unordered sets * **Sorted Set**: Ordered sets with scores * **Stream**: Redis Streams (if using Stream Source mode) ## Troubleshooting Verify that keyspace notifications are enabled:

```bash theme={null} redis-cli CONFIG GET notify-keyspace-events ``` The response should include `KEA` or the specific event types you need. If not, enable them as described in [Enable Keyspace Notifications](/redis-source-generic#2-enable-keyspace-notifications).
If you're experiencing authentication issues:

* Verify the username and password are correct * For Redis 6.0+, ensure the user has appropriate ACL permissions * Check if `requirepass` is set in redis.conf for Redis versions \< 6.0 * Verify network connectivity and firewall rules
If Redis performance degrades after enabling CDC:

* Reduce the batch size to lower per-batch load * Increase the poll interval to reduce polling frequency * Monitor Redis metrics (CPU, memory, network I/O) * Consider using a dedicated Redis replica for CDC * Limit keyspace notifications to specific event types instead of using `KEA`
If you're not capturing all expected changes:

* Verify keyspace notifications include all necessary event types * Check that the connector is running and connected * Review connector logs for errors or warnings * Ensure the Redis user has permissions to read all keys * Verify the database number is correct in the configuration
# Redshift Source: https://docs.streamkap.com/redshift Stream data into Redshift ## Prerequisites * Connection details * Streamkap user and role ## Granting Privileges It's recommended to create a separate user and role for Streamkap to access your Redshift database. Below is an example script that does that. ```SQL SQL theme={null} -- Connect to your Redshift cluster using a SQL client -- Create the Streamkap User CREATE USER streamkap_user PASSWORD {'password'} -- Grant privileges to the user on specific schemas and tables GRANT USAGE ON SCHEMA {schema} TO streamkap_user; -- GRANT SELECT, INSERT, UPDATE, DELETE ON streamkap.streamkap_table TO streamkap_user; ``` ## Streamkap Setup 1. Go to [Destinations]() and choose **Redshift** 2. Input the following information: * **Name** - A unique and memorable name for this Connector * **Hostname** - The hostname connection string without the port. * **Port** - The port number of the connection * **Username** (Case sensitive) - `STREAMKAP_USER` OR the username you chose * **Password** - The password for your username * **Database Name** (Case sensitive) - `STREAMKAPDB` OR the database name you chose * **Schema Name** (Case sensitive) - `STREAMKAP` OR the schema name you chose * **Insert Mode** Insert or Upserts (i.e. append only or replace) * **Delete Mode** - Delete records in destination if deleted at source * **Schema Evolution** - Handle additional columns automatically? * **Tasks** - Amount of parallelism in writing events. 3. Click **Save** ## How this Connector Works The Redshift connector supports idempotent write operations by using upsert semantics and basic schema evolution. The following features are supported: * At-least-once delivery * Delete mode * Idempotent writes (Insert/Upsert mode) * Schema evolution ## At-least-once delivery The Redshift connector guarantees that events that is consumes are processed at least once. ## Delete mode The PostreSQL connector can delete rows in the destination database when a DELETE or tombstone event is consumed. ## Idempotent writes The Redshift connector supports idempotent writes, allowing the same records to be replayed repeatedly and the final database state to remain consistent. In order to support idempotent writes, the Redshift connector must be set to Upsert mode. An upsert operation is applied as either an update or an insert, depending on whether the specified primary key already exists. If the primary key value already exists, the operation updates values in the row. If the specified primary key value doesn’t exist, an insert adds a new row. ## Schema evolution The Redshift connector supports schema evolution The connector automatically detects fields that are in the event payload but that do not exist in the destination table. The connector alters the destination table to add the new fields. When schema evolution is set to Yes, the connector automatically creates or alters the destination database table according to the structure of the incoming event. When an event is received from a topic for the first time, and the destination table does not yet exist, the Redshift connector uses the event’s key, or the schema structure of the record to resolve the column structure of the table. If schema evolution is enabled, the connector prepares and executes a CREATE TABLE SQL statement before it applies the DML event to the destination table. When the Redshift connector receives an event from a topic, if the schema structure of the record differs from the schema structure of the destination table, the connector uses either the event's key or its schema structure to identify which columns are new, and must be added to the database table. If schema evolution is enabled, the connector prepares and executes an ALTER TABLE SQL statement before it applies the DML event to the destination table. Because changing column data types, dropping columns, and adjusting primary keys can be considered dangerous operations, the connector is prohibited from performing these operations. ## Related Documentation * [Ingestion Modes](/insertsupserts) - Understand insert vs. upsert ingestion modes * [Primary Key Modes](/insertsupserts#primary-key-modes) - Configure how primary keys are determined for destination tables * [Delete Handling](/insertsupserts#delete-handling) - Configure how delete events are processed at the destination * [Error Reference](/error-reference) - Common error codes and resolution steps # Regex Support for >100k Tables Source: https://docs.streamkap.com/regex-support Use regex patterns in Source schema configuration to merge related tables into logical topics for multi-tenant, sharded, or cell-based architectures. There are various use cases where a group of related tables must be considered one logical table when streamed to one or more destinations: * **Multi-tenancy** implemented with "DB per tenant" or "Schema per tenant" * **Application-level sharding** * **Cell-based architecture** In such cases, use the **Regex support** in the Schema configuration for a Source connector. All tables matching one regex in the schema will be considered one logical topic and streamed to one logical destination table. ## Schema Configuration Regex can be applied at the **schema or database level**, at the **table level**, or **both**. In the Source connector's **Schema** tab, add a schema using a regex pattern (e.g. `shard[0-9]*`). All matching schemas will be merged, and the tables within them will be treated as a single logical set. Schema tab showing a regex pattern shard[0-9]* matching 3 tables: Orders, Customers, Products1 ## Advanced Settings To enable regex support, go to the Source connector's **Settings** tab and scroll to the **Advanced** section: 1. Toggle **Enable regex support** on 2. Configure the **Replacement string** for matching regex snippets 3. Set the **Regex support key field template** — an extra key field is needed to ensure unique records across merged sources (e.g. `{{database}}.{{table}}`) 4. Set the **metadata field name** to store source information and ensure uniqueness (e.g. `_streamkap_source_metadata`) Advanced settings showing Enable regex support toggle, replacement string, key field template, and metadata field name Column Selection Mode can also be used with regex support to include or exclude specific columns using regex patterns matching the fully qualified column name format: `schemaName.tableName.columnName`. # Amazon S3 Source: https://docs.streamkap.com/s3 Change Data Capture using Amazon S3 with Streamkap ## Requirements * AWS Access Key and Secret Access Key with the following permissions to the destination bucket: * `s3:GetObject` * `s3:PutObject` * `s3:AbortMultipartUpload` * `s3:ListMultipartUploadParts` * `s3:ListBucketMultipartUploads` ## Configure Amazon S3 Connector * `Name`: A descriptive name for the connector * `AWS Access Key`: An Access Key with the appropriate permissions for the bucket to which Streamkap will load data * `AWS Secret Access Key`: The Secret Access Key with the appropriate permissions for the bucket to which Streamkap will load data * `Region`: Name of the region for bucket to which Streamkap will load data * `Bucket Name`: The name of the bucket to which Streamkap will load data * `Format`: The format of the file. The following options are available: `JSON Lines`, `JSON Array` and `Parquet` CSV output is not currently supported. Writing CSV requires reading the source data as raw, unparsed bytes (a `ByteArrayConverter`) instead of as structured records, and Streamkap doesn't currently offer that as a serialization option. If you have a use case that needs CSV output, contact [Streamkap support](mailto:support@streamkap.com) to discuss options. * `Filename Template`: The format of the filename. A file extension is appended automatically based on the selected `Format` and `Compression Type` — do not include one in the template. See below for more information about formatting options. * `Compression Type`: Compression type for output files. Supported algorithms are `gzip`, `snappy`, `zstd` and `none`. Defaults to `gzip`. Only shown for `JSON Lines` and `JSON Array` — hidden for `Parquet`, which compresses internally and doesn't use this setting. * `Envelope Output` (Advanced, default: enabled): Wraps each output record in an envelope with Kafka metadata (`key`, `offset`, `timestamp`, `headers`) alongside the value. Disable to write only the record's own value. For `Parquet`, only applies when the value is a record or map. * `Output Fields` (Advanced): List of fields to include in output. Available options are: `key`, `offset`, `timestamp`, `value` and `headers`. Defaults to `value`. * `Max Records Per File` (Advanced, default: `0`): Maximum number of records buffered into a single file before it's rotated. This caps how large a file can grow, but rotation only makes a new file eligible for upload — files are still written to S3 on the connector's normal flush interval, not immediately when the cap is hit. Set to `0` for unlimited (one file per topic-partition per flush interval). * `S3 Multipart Upload Part Size (bytes)` (Advanced, default: `5242880` / 5MB): Size of each part in a multipart upload to S3. This controls how a single file is chunked for upload to S3, not how many records go into a file — see `Max Records Per File` for that. Larger parts mean fewer S3 API calls for big files but more memory used per upload. AWS requires multipart parts to be at least 5MB (except the last part of a file). **Filename template** (default: `{{topic}}-{{partition}}-{{start_offset}}`): The format of the filename, excluding the extension. You can combine any of the elements below using other text or characters, including dashes (`-`) and underscores (`_`). Static text placed at the start of the template (e.g. `dir1/dir2/{{topic}}-{{partition}}-{{start_offset}}`) acts as a directory prefix. | Element | Description | | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `{{topic}}` | The Streamkap topic name. For example, a PostgreSQL Source table `web.salesorders` topic's name would be `salesorders` | | `{{partition:padding=true\|false}}` | The partition number of the records in the file, typically `0`. Streamkap topics and their data can be partitioned for better performance in certain scenarios. For example, a topic `salesorders` has 10 partitions, 0 through to 9. If padding set to true it will set leading zeroes for offset, the default value is false; | | `{{start_offset:padding=true\|false}}` | The offset number of the first record in the file. Every record streamed has an incrementing offset number. For example, a topic `salesorders` has 1000 records, offsets 0 through to 999. Note that in the case of a multi-partitioned topic, offset numbers are **not unique** across partitions. If padding set to true it will set leading zeroes for offset, the default value is false; | | `{{timestamp:unit=yyyy\|MM\|dd\|HH}}` | The timestamp for when the file was created by the Connector. For example, the template `{{topic}}{{timestamp:unit=yyyy}}-{{timestamp:unit=MM}}` and timestamp of `2024-01-01 20:24` would create a file named `salesorders2024-01` | | `{{key}}` | The Kafka key | Currently, supported combinations of variables are: * topic, partition, start\_offset, and timestamp - grouping by the topic, partition, and timestamp; * key - grouping by the key. * key, topic, partition - grouping by the topic, partition, and key. # S3 Source Source: https://docs.streamkap.com/s3-source Stream files from Amazon S3 into Streamkap The S3 Source connector polls an S3 bucket for new files and streams their contents into Kafka topics. It supports JSON, CSV, and Avro files with configurable polling, file cleanup policies, and dynamic topic routing based on the S3 key path. This is a **batch file source**, not a CDC connector. Files are scanned at a configurable interval and each file is processed once. ## Prerequisites * An S3 bucket containing the files to be ingested * AWS credentials (Access Key ID and Secret Access Key) with the following permissions on the source bucket: * `s3:GetObject` * `s3:ListBucket` * `s3:DeleteObject` — only required when using the **Delete** cleanup policy ```JSON Recommended IAM Policy theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:ListBucket", "s3:DeleteObject" ], "Resource": [ "arn:aws:s3:::", "arn:aws:s3:::/*" ] } ] } ``` ## Supported File Formats | Format | File Extension | Notes | | ------ | -------------- | ----------------------------------------------- | | JSON | `.json` | JSON Lines (one object per line) or JSON arrays | | CSV | `.csv` | Comma-separated; headers optional | | Avro | `.avro` | Apache Avro binary with embedded schema | Only files matching the selected format's extension under the configured prefix are picked up. *** ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **S3**. ### 2. Connection Settings * **Name**: A name for your connector. * **AWS Access Key**: The AWS Access Key ID with the permissions listed in [Prerequisites](#prerequisites). * **AWS Secret Access Key**: The AWS Secret Access Key that pairs with the key above. * **Region**: The AWS region where the S3 bucket lives. Click **Next**. ### 3. Source Settings * **Bucket Name** (required): The S3 bucket to scan. * **File format** (required): The file format to process — `json`, `csv`, or `avro`. Default: `json`. * **CSV Has Headers**: (Visible when format is CSV) Whether the first row contains column headers. When disabled, columns are auto-generated as `column1`, `column2`, etc. Default: `true`. * **Object Prefix**: The S3 key prefix (folder path) to scan within the bucket. Only objects under this prefix are processed. Default: `file-pulse/`. * **Topic Postfix**: The default topic name suffix. When Dynamic Topic Routing is disabled, all files are streamed to this single topic. When enabled, this is used as a fallback for files that do not match the routing rules. Default: `default`. * **Dynamic Topic Routing**: Enable to derive the Kafka topic name per file from its S3 key path. When disabled, all files go to the single default topic. Default: `false`. See [Dynamic Topic Routing](#dynamic-topic-routing) below for details. Click **Save**. ### 4. Advanced Settings Available under **Advanced**: * **Scan Interval (ms)**: How often the connector polls the bucket for new files. Range: 100 – 100,000. Default: `10000` (10 seconds). * **Cleanup Policy**: What to do with a file after it has been processed: * **Log** (default): Mark the file as processed without deleting it. Tracked internally so it is not reprocessed. * **Delete**: Remove the file from S3 after successful processing. Requires `s3:DeleteObject`. * **Tasks**: Maximum number of parallel tasks processing files concurrently. Range: 1 – 10. Default: `5`. ## Dynamic Topic Routing By default, every file is sent to a single topic (using the **Topic Postfix** as the topic name). Dynamic topic routing derives the topic name per file from its S3 key path, so files under different folders land in different topics. When **Dynamic Topic Routing** is enabled, you can choose between two modes: ### Simple Mode (Folder Skip / Folder Levels) The default mode when Dynamic Topic Routing is enabled. Uses folder position to build the topic name. * **Folder Skip**: Number of leading path segments to drop from the S3 key. Default: `0`. * **Folder Levels**: Number of folder segments (after the skip) to include in the topic name, joined with dots. Default: `0`. #### Example Given the S3 key `data/region/us-east/customers/file.json`: | Folder Skip | Folder Levels | Resulting Topic Suffix | | ----------- | ------------- | ---------------------- | | 0 | 1 | `data` | | 1 | 1 | `region` | | 1 | 2 | `region.us-east` | | 2 | 2 | `us-east.customers` | If **Folder Skip** + **Folder Levels** exceeds the number of path segments in the S3 key, the missing segments are silently ignored — the topic name will be shorter than expected rather than causing an error. For example, if the key has only 3 folders but you set Folder Skip to 1 and Folder Levels to 4, only the 2 available folders after the skip will appear in the topic name. ### Advanced Mode (ScEL Expression) Enable **Use Advanced Expression** to switch to a custom ScEL expression for full control over the topic suffix. This replaces the Folder Skip / Folder Levels settings. Available functions: | Function | Description | | -------------------------------------- | ------------------------------------- | | `$metadata.name` | The full S3 key (folders + file name) | | `split(str, delimiter)` | Break a string into an array | | `extract_array(array, index)` | Pick a segment by 0-based position | | `replace_all(str, regex, replacement)` | Strip or rewrite parts of a string | | `concat_ws(separator, ...parts)` | Join multiple parts with a separator | #### Examples **Combine two folders.** For the S3 key `data/region/us-east/file.json`, produce the suffix `region.us-east`: ``` concat_ws('.', extract_array(split($metadata.name, '/'), 1), extract_array(split($metadata.name, '/'), 2)) ``` **Combine a folder and the file-name prefix.** For keys like `data/region/us-east/orders_0001.json` and `data/region/us-east/products_0001.json`, produce suffixes `us-east.orders` and `us-east.products`: ``` concat_ws('.', extract_array(split($metadata.name, '/'), 2), replace_all(extract_array(split($metadata.name, '/'), 3), '_.*', '')) ``` ## Troubleshooting Verify the following: * The **Bucket Name** and **Object Prefix** are correct. * Files match the selected **File format** (e.g. `.json` files for JSON). * The AWS credentials have `s3:GetObject` and `s3:ListBucket` on the bucket. * New files are being placed under the configured prefix. * The **Scan Interval (ms)** is not set excessively high. The IAM principal associated with the provided AWS credentials lacks sufficient permissions. **Resolution:** * Ensure the IAM policy grants `s3:GetObject` and `s3:ListBucket` for the bucket. * If using the **Delete** cleanup policy, also grant `s3:DeleteObject`. * Check that no bucket policy explicitly denies access. * Confirm the bucket is in the configured **Region**. This typically means **CSV Has Headers** does not match the actual file content. **Resolution:** * If your CSV files have a header row, set **CSV Has Headers** to `true`. * If they do not, set it to `false` — columns are auto-named `column1`, `column2`, etc. * Verify the delimiter is a comma (other delimiters are not currently supported). The connector tracks processed files in an internal status topic. Reprocessing can occur if: * The connector was deleted and recreated (file tracking state is lost). * The internal status topic was deleted. **Resolution:** * Avoid deleting and recreating connectors for the same bucket/prefix. * Contact [Streamkap support](mailto:support@streamkap.com) if you need to reset the processing state. # SAML IDP Setup Source: https://docs.streamkap.com/saml-idp-setup Configure SAML 2.0 identity provider integration for Streamkap single sign-on. SAML stands for Security Assertions Markup Language. This XML-based open standard allows identity providers (IdP) to pass authorization credentials to service providers (SP). Below is an example with Okta. Create a new SAML application with Okta that you can use as the Identity Provider for users. In the Streamkap app, click your avatar in the top navigation bar, select **Account preferences**, then click **SSO** under the Workspace section. Click the add new button to configure the type of connection and the Identity Provider by filling out the details or uploading a metadata file from the identity provider. We recommend selecting the automatic option. It is the same as manual but less prone to implementation errors. After selecting automatic, you will need to upload the XML file from the identity provider. Okta will require information from the Service Provider, ACS URL, and Entity ID. See the example below. Open the new SSO configuration modal on Admin portal and use the values for ACS URL under the SSO URL on Okta (other IDP). This would be a domain followed by /auth/saml/callback. For the SP Entity ID, enter the Entity ID from the Admin portal. **Using SSO with sub-accounts?** By default, the Entity ID is shared across your root account and all sub-accounts. If your identity provider requires unique identifiers per enterprise application (e.g., Microsoft Entra rejects duplicate SAML Identifiers), contact Streamkap support to have unique Entity IDs assigned per sub-account. See [SSO with Sub-Accounts](/sso#sso-with-sub-accounts) for details. Choose the Name ID format of EmailAddress and Application username as Okta username. The final step is to transfer an XML file from the identity provider to Admin portal. To do that, first, download the metadata XML from the Identity Provider (Okta). Next, upload the XML file to the Admin portal. If the IDP does not offer an XML download, the user may need to copy the XML data from the IDP and save it to an .xml file then upload that file to the Admin Portal. If selected manual, you'll need to enter the SSO Endpoint and Public Certificate from the identity provider. After configuring the identity provider, you will need to claim one or more domains for the account. This step is required in order to avoid abuse of a domain. The domain is the same domain you login with. For example, ours would be streamkap.com. The domain needs to be claimed by copying the TXT record and applying it to your DNS provider. If you cannot obtain access to your organization's DNS, please contact your application Administrator. You can configure multiple domains for an account. This can be useful if you're using multiple environments for development or multiple production applications on separate domains and need the SSO connection to cover several domains. Select which roles should be assigned to SSO users by default and map IDP groups to specific roles (see step 7). Roles you assign to users through SSO will apply regardless of the additional roles you assign. **Default SSO Roles** Assign default roles to all SSO users by adding one or more Frontegg roles from your list of predefined roles. Whichever roles you include in the input, those roles will be assigned to all SSO users. By default the groups that the user passes from the IDP are being checked only upon user creation - first login via SSO. If you'd like to check for the users groups' on each login, please contact Frontegg. When configuring SSO for an account, you have the option to map your IDP groups to roles available in the application. For the mapping to work, first, you should configure your IDP so that the name of the group attribute that is sent in the SAML response is groups. You'll also need the IDP group names from your IDP that you want to map to your roles. Below is an example of how to find IDP group names in Okta. Sign in to your Okta account and go to Directory and find the Groups page. In the image below of an example Okta account, Managers is an Okta Group Name. Use those Group Names to map groups to roles. In the configuration section of the Admin portal map the groups that your users will be passing to corresponding roles in your application. Save the connection and make sure that it is enabled. # Schema Evolution Source: https://docs.streamkap.com/schema-evolution-support How Streamkap handles schema changes between source and destination, including column name case sensitivity across databases. Schema evolution enables a destination to adapt the schema of its objects (e.g. columns, fields, tables, files, collections) over time without disrupting operations or data integrity. Streamkap Connectors support - where applicable - the following types of schema changes: | Object | Change | Behavior | | ------ | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Column | Add | Adds the new column to the end of the destination table. | | | Remove | Keeps the column in the destination table intact but is left empty (`NULL` values) from the point of change onwards. | | | Data type | Adds a new column. For example if there is a column named `AGE` which is the `NUMBER` type but is then altered to `VARCHAR` type, an additional column is added named `AGE_CHR` of type `VARCHAR`. `AGE` column is kept intact. | | | Rename | Adds a new column. For example, if there is a column named `FIRST_NAME` that's renamed to `FULL_NAME`, an additional column is added named `FULL_NAME` and will be populated from the point of change onwards. `FIRST_NAME` column is kept intact but is left empty (`NULL` values). | | Table | Add | Adding a table is done by modifying your Source Connector and Pipelines. Streamkap will automatically backfill/snapshot the newly added table (by default) while streaming data simultaneously. | | | Remove | Keeps the destination table intact but stops capturing change event data for it. | | | Rename | Renaming table(s) will report an error and you will need to add the renamed table to your affected Source Connector and Pipeline(s) to capture it. | ## How to Configure Schema evolution is configured per destination connector. The setting is found in the **SMT (Single Message Transform) tab** of your destination configuration. ### Steps 1. Navigate to **Destinations** and select your destination connector 2. Open the **Settings** panel 3. Select the **SMT** tab 4. Locate the **`schema.evolution`** setting 5. Choose one of the available options: * **`basic`** (default) -- Automatically evolves the destination schema when source schema changes are detected * **`none`** -- Source schema changes are ignored at the destination; no automatic schema modifications are made 6. Click **Save** ### Option Details When set to `basic`, Streamkap automatically adapts the destination schema in response to source changes: * **New columns** are added to the destination table automatically * **Data type changes** are handled by adding a new column with a type suffix (e.g., an `AGE` column changed from `NUMBER` to `VARCHAR` results in a new `AGE_CHR` column) * **Renamed columns** result in a new column being added; the original column is retained but receives `NULL` values going forward * **Removed columns** are kept in the destination but receive `NULL` values from the point of removal This is the recommended setting for most use cases. When set to `none`, Streamkap does not modify the destination schema in response to source changes: * New columns in the source are **not** added to the destination * Data type changes, renames, and removals in the source are **ignored** * You must manage the destination schema manually Use this option when you need strict control over the destination schema, such as when a DBA manages table structures or when you have custom table layouts. ### Destination-Specific Notes **Snowflake: Upsert mode required** For Snowflake destinations, schema evolution is only available when the ingestion mode is set to **`upsert`**. If you are using `append` mode, the `schema.evolution` setting is not applicable. Additionally, the Snowflake role used by the connector must have the **`OWNERSHIP`** privilege on the target tables for schema evolution to work. See the [Snowflake Schema Evolution Permissions](/snowflake#schema-evolution-permissions) section for details and required SQL grants. **Other destinations** For all other supported destinations, schema evolution is available regardless of the ingestion mode. No additional privileges beyond the standard connector permissions are required. ## Verifying Schema Evolution After configuring schema evolution, verify that changes propagate correctly from source to destination. ### Steps to Test 1. **Add a new column** at the source database (e.g., `ALTER TABLE orders ADD COLUMN notes VARCHAR(255);`) 2. **Insert or update a row** in that table so the change event includes the new column 3. **Wait for propagation** -- the new column should appear at the destination after the pipeline processes the change event 4. **Check the destination** -- confirm the new column exists and has the expected data type The propagation time depends on pipeline lag. Under normal operating conditions with low lag, schema changes typically propagate within seconds to minutes. During high-throughput periods or active snapshots, propagation may take longer. Schema evolution only applies to new change events. Existing rows that were written before the schema change will not be retroactively updated with new columns. To backfill existing rows with values for the new column, trigger an [incremental snapshot](/snapshots#triggering-a-snapshot) of the affected table. **Switching schema evolution modes mid-pipeline** Changing the `schema.evolution` setting from `basic` to `none` (or vice versa) on an active pipeline can cause inconsistencies. For example, switching from `basic` to `none` means any future source schema changes will no longer propagate, but previously auto-evolved columns remain in the destination. Switching from `none` to `basic` will only evolve the schema for changes detected after the switch -- it will not retroactively apply changes that occurred while the mode was `none`. Test mode changes in a staging environment first. If you need to switch modes on a production pipeline, review the current destination schema to ensure it is consistent before making the change. ## Column Name Case Sensitivity When streaming data between systems, column name casing can differ between the source and destination. A column created as `firstName` in one database may arrive as `FIRSTNAME` or `firstname` in another. Understanding each system's default behavior helps prevent schema mismatches and failed writes. ### How Streamkap Handles Case Streamkap preserves the original column name casing from the source database through the pipeline. The column name that appears in the source's change event is carried as-is through Kafka and delivered to the destination connector. However, the destination database may apply its own case-folding rules when creating or matching columns. This means the final column name at the destination depends on how that particular database handles identifiers. ### Sources | Database | Default Behavior | Notes | | ---------- | ---------------- | -------------------------------------------------------------------------------------- | | PostgreSQL | Lowercase | Folds unquoted identifiers to lowercase. Double-quoted names preserve original casing. | | MySQL | Varies | Depends on the operating system and the `lower_case_table_names` server setting. | | Oracle | UPPERCASE | Folds unquoted identifiers to uppercase. Double-quoted names preserve original casing. | | SQL Server | Varies | Case behavior depends on the database or column collation setting. | | MongoDB | Case-sensitive | Field names are preserved exactly as-is. | ### Destinations | Database | Default Behavior | Notes | | ------------ | ---------------- | ----------------------------------------------------------------------------------------------------------- | | Snowflake | UPPERCASE | Folds unquoted identifiers to uppercase. Double-quoted names preserve original casing. | | ClickHouse | Case-sensitive | Column names are stored and matched exactly as provided. | | BigQuery | Case-sensitive | Column names are stored and matched exactly as provided. | | PostgreSQL | Lowercase | Folds unquoted identifiers to lowercase. Double-quoted names preserve original casing. | | Redshift | Lowercase | Folds all identifiers to lowercase regardless of quoting. | | S3 / Iceberg | Case-preserving | Column names are preserved as received. Final behavior may depend on the file format (Parquet, Avro, etc.). | ### Case Handling Scenarios The following examples illustrate how column names flow from specific sources to specific destinations. PostgreSQL folds unquoted identifiers to lowercase. A column created as `firstName` (unquoted) is stored as `firstname`. When Streamkap streams this to Snowflake, the change event carries the name `firstname`. Snowflake then folds unquoted identifiers to uppercase, so the column is created as `FIRSTNAME` in the destination table. * Source column: `firstname` (PostgreSQL lowercase) * Destination column: `FIRSTNAME` (Snowflake uppercase) If you need to preserve the original lowercase name in Snowflake, use a RenameFields transform or configure Snowflake to use quoted identifiers. MySQL's case sensitivity depends on the operating system and the `lower_case_table_names` setting. If MySQL stores a column as `CustomerName`, the change event carries that exact casing. When Streamkap delivers this to PostgreSQL, PostgreSQL folds unquoted identifiers to lowercase, so the column is created as `customername`. * Source column: `CustomerName` (MySQL mixed-case) * Destination column: `customername` (PostgreSQL lowercase) The data flows correctly, but the casing changes at the destination. Queries against the PostgreSQL destination must use the lowercase name. MongoDB preserves field names exactly as-is, and BigQuery also stores column names exactly as provided. A field named `userEmail` in MongoDB arrives as `userEmail` in BigQuery with no case transformation. * Source field: `userEmail` (MongoDB exact) * Destination column: `userEmail` (BigQuery exact) This is the simplest scenario -- both systems are case-sensitive and case-preserving, so names pass through unchanged. If you need to normalize column names before they reach the destination (for example, converting `camelCase` to `snake_case`, or ensuring all columns are lowercase), you can use the **RenameFields** destination-side transform. See [Transform Examples](/transform-examples#renamefields) for a concrete before-and-after example. To avoid case sensitivity issues in your CDC pipelines, keep column naming conventions consistent across your source and destination. If your source uses mixed-case or uppercase identifiers (e.g., Oracle), verify that your destination handles them as expected. When in doubt, use lowercase column names without special quoting, as this is the most widely compatible convention. # Schema Registry Source: https://docs.streamkap.com/schema-registry Browse and manage Kafka schema registry subjects, view schema definitions, and track schema evolution Schema Registry provides a centralized repository for managing and validating schemas used in your Kafka topics. The Schema Registry page allows you to browse schema subjects, view schema definitions, compare schema versions, and understand schema evolution over time. ## Overview The Schema Registry page displays all schema subjects registered in your project: ### Key Features * **Search Functionality**: Filter schemas by subject name using the search bar * **Type Filtering**: Filter schemas by type (AVRO, JSON, Protobuf) * **Version Tracking**: View and compare different schema versions * **Schema Inspector**: Browse schema definitions with syntax highlighting * **Evolution History**: Track how schemas change over time * **Global ID Reference**: Unique identifier for each schema version ## Schema Registry Table The main table lists all schema subjects with the following columns: * **Subject Name**: The schema subject identifier * Click to navigate to the schema detail page * Typically follows the pattern: `{topic-name}-key` or `{topic-name}-value` * Format: `source_{id}.{database}.{table}-{key|value}` * **Schema Type**: The serialization format * `AVRO`: Apache Avro binary format (most common for CDC) * `JSON`: JSON Schema format * `PROTOBUF`: Protocol Buffers format * **Latest Version**: The current version number of the schema * Increments with each schema update * Version 1 indicates the initial schema * **Global ID**: Unique identifier across all schemas in the registry * Used internally by Kafka for schema resolution * Increments monotonically across all schema versions ### Search and Filtering * **Search Bar**: Filter schemas by entering part or all of a subject name * **Type Filter**: Use the "All Types" dropdown to show only schemas of a specific format * **Pagination**: Navigate through schemas with configurable page size (10, 20, 50, or 100 per page) Subject names ending in `-key` define schemas for message keys, while `-value` subjects define schemas for message payloads. ## Schema Detail Page Click any subject name to view detailed information about the schema, including its definition and version history. ### Schema Overview The detail page displays key information about the selected schema: **Schema Metadata:** * **Schema Type**: The serialization format (shown as a badge in the top-right) * **Version Selector**: Dropdown to switch between schema versions * "Latest" shows the most recent version * Version numbers listed for historical versions * **Global ID**: The unique identifier for this schema version in the registry * **Current Version**: The version number currently selected * **Has References**: Indicates if the schema references other schemas * "Yes": Schema imports or references external schema definitions * "No": Schema is self-contained with no external dependencies Use the version selector to compare how a schema has evolved over time. This is helpful for understanding breaking changes or debugging schema compatibility issues. ### Schema Definition The Schema Definition section displays the complete schema structure with interactive viewing options: **Features:** * **Syntax Highlighting**: JSON/Avro syntax with color coding * **Line Numbers**: Easy reference to specific parts of the schema * **Collapsible Sections**: Click arrows to expand/collapse nested fields * **Field Summaries**: Collapsed sections show item counts (e.g., "// 12 items") * **Copy Schema**: Button to copy the entire schema definition to clipboard * **Expand All**: Button to expand all collapsed sections at once **Schema Structure Elements:** For `AVRO` schemas, you'll typically see: * **type**: The schema type (e.g., "record", "string", "int") * **name**: The name of the record or field * **namespace**: Fully qualified namespace for the schema * **fields**: Array of field definitions with names and types * **connect.name**: The Kafka Connect-specific schema identifier `AVRO` schemas are the most common format for CDC data in Streamkap. They provide rich type information and support schema evolution with compatibility rules. ## Understanding Schema Subjects Schema subjects organize schemas in the registry by their purpose and data structure. ### Subject Naming Conventions **CDC Source Schemas:** ``` source_{connector_id}.{schema}.{table}-key source_{connector_id}.{schema}.{table}-value ``` **Examples:** * `source_67d010f15ad5681e912ccc5b.ecommerce2.Customers-key`: Key schema for Customers table * `source_67d010f15ad5681e912ccc5b.ecommerce2.Customers-value`: Value schema for Customers table **Transform Schemas:** ``` {transform_name}-key {transform_name}-value ``` **Heartbeat Schemas:** ``` snapshot___debezium-heartbeat.{source_name}-key snapshot___debezium-heartbeat.{source_name}-value ``` ### Key vs Value Schemas **Key Schemas:** * Define the structure of message keys * Typically contain primary key fields from source tables * Used for partitioning and compaction **Value Schemas:** * Define the structure of message payloads * Contain the full record data from source tables * Include metadata fields like timestamps and operations ## Schema Versioning Schema Registry maintains a version history for each subject, enabling schema evolution tracking. ### Version Numbers * **Version 1**: The initial schema registered for this subject * **Version 2+**: Subsequent schema updates * **Latest**: Alias for the most recent version ### Schema Evolution When schemas evolve, new versions are created. Common evolution scenarios: **Backward Compatible Changes:** * Adding fields with defaults * Removing optional fields **Forward Compatible Changes:** * Adding fields * Removing fields with defaults **Fully Compatible Changes:** * Adding optional fields with defaults (both backward and forward compatible) Breaking changes like changing field types, or removing required fields without proper compatibility mode, can cause consumer failures. Always test schema changes in non-production environments first. ## Schema Types Streamkap supports multiple schema serialization formats: In almost all cases, the source connector determines which schema serialization format is used. ### AVRO **Most common for CDC workloads, especially from relational, structured sources** **Advantages:** * Compact binary format * Rich type system with logical types * Strong schema evolution support * Excellent compression * Fast serialization/deserialization **Use Cases:** * CDC data from relational databases * High-throughput data pipelines * Data warehousing workflows ### JSON Schema **Human-readable text format, typically used with semi-structured sources** **Advantages:** * Easy to read and debug * Wide tooling support * Flexible schema definitions **Use Cases:** * REST API integrations * Semi-structured data * Developer-friendly debugging ### Protocol Buffers (Protobuf) **Google's binary serialization format** **Advantages:** * Compact binary format * Strong typing * Code generation support * Cross-language compatibility **Use Cases:** * Microservices communication * gRPC integrations * Cross-platform data exchange ## Working with Schemas ### Copying Schema Definitions To copy a schema for external use: 1. Navigate to the schema detail page 2. Select the desired version from the dropdown 3. Click the **Copy Schema** button 4. The entire schema JSON will be copied to your clipboard 5. Paste into your development tools, documentation, or schema management systems Copying schemas is useful for generating code, creating documentation, or validating compatibility with downstream systems. ### Inspecting Schema Changes To compare schema versions: 1. Open the schema detail page 2. Note the current schema structure 3. Use the version dropdown to select an earlier version 4. Compare the field definitions, types, and structure 5. Identify additions, removals, or type changes ### Understanding Schema References Schemas with `Has References: Yes` depend on other schema definitions: * The schema imports types from other schemas * Changes to referenced schemas may affect this schema * Check referenced schemas for their definitions and versions ## Schema Registry Best Practices 1. **Plan Schema Changes**: Design schemas with future evolution in mind 2. **Test Compatibility**: Validate schema changes against existing consumers before deployment 3. **Document Changes**: Maintain a changelog of schema modifications 4. **Use Optional Fields**: Make new fields optional with defaults for backward compatibility 5. **Avoid Breaking Changes**: Never remove required fields or change field types 6. **Monitor Versions**: Keep track of which schema versions are in use by consumers 7. **Clean Up Unused Schemas**: Remove schemas for decommissioned topics or connectors ## Schema Compatibility Modes Schema Registry enforces compatibility rules to prevent breaking changes: ### BACKWARD (Default) * Consumers using new schema can read data written with old schema * Safe to add fields with defaults * Safe to remove fields ### FORWARD * Consumers using old schema can read data written with new schema * Safe to add fields * Safe to remove fields with defaults ### FULL * Both backward and forward compatible * Safest option for schema evolution * Most restrictive ### NONE * No compatibility checking * Use with caution * Risk of consumer failures Streamkap typically uses `BACKWARD` compatibility mode for CDC schemas, allowing safe addition of columns in source databases. ## Troubleshooting ### Schema Not Appearing If a schema is missing from the registry: 1. **Check Topic Activity**: Schemas are registered on first message write 2. **Verify Connector**: Ensure the source or destination connector is running 3. **Refresh the Page**: Click refresh or reload the browser 4. **Check Project**: Verify you're viewing the correct Streamkap project ### Schema Registration Failures If schemas fail to register: 1. **Review Connector Logs**: Check for schema-related errors ([Logs](/logs)) 2. **Validate Schema Format**: Ensure the schema is valid `AVRO` | `JSON` | `Protobuf` 3. **Check Compatibility**: Verify the new schema is compatible with existing versions 4. **Check Registry Permissions**: Ensure the connector has schema registry permissions ### Version Mismatch If consumers report schema version mismatches: 1. **Check Consumer Configuration**: Verify schema registry URL is correct 2. **Review Global IDs**: Ensure consumers are requesting the correct global ID 3. **Update Consumers**: Redeploy consumers with the latest schema version 4. **Validate Subject Names**: Confirm consumers are using the correct subject name ### Schema Evolution Errors If schema evolution causes errors: 1. **Review Compatibility Mode**: Check the compatibility setting for the subject 2. **Identify Breaking Changes**: Compare old and new schema versions 3. **Rollback if Needed**: Revert to a previous schema version if consumers fail 4. **Update Consumers**: Deploy consumer updates to handle new schema structure ## Related Documentation * [Topics](/topics) - Kafka topics that use schemas for serialization * [Sources](/sources) - Source connectors that register schemas * [Destinations](/destinations) - Destination connectors that consume schema-encoded data * [Schema Evolution Support](/schema-evolution-support) - How Streamkap handles schema changes * [Kafka Access](/kafka-access) - Configure permissions for schema registry access * [Logs](/logs) - Troubleshoot schema-related errors # Security Posture Source: https://docs.streamkap.com/security-posture Streamkap Security Posture - Quick Links Streamkap has SOC 2 compliance as well as enterprise features accessible to all. Here are some shortcuts to help you learn more. * Visit our [Trust Center](https://trust.streamkap.com) for our SOC 2 compliance attestation, subprocessors and real-time 24/7 monitoring * [Federated Authentication and SSO](/sso) * [Organisation Security](/organisation-security) * [Personal Privacy & Security](/personal-privacy-security) * [Team Management](/team-management) * [Audit Logs](/view-audit-logs) * [Connection Options](/connection-options) for connecting to sources/destinations. # Setup SSO Source: https://docs.streamkap.com/setup-sso Configure single sign-on for your Streamkap organization SSO configuration can be found in **Account preferences** under the **Workspace** section. Click your avatar in the top navigation bar and select **Account preferences**, then click **SSO**. Single Sign On configuration page showing SSO Connections Starting SSO setup will guide you through a full tutorial. The documentation below provides additional assistance. ## Supported Authentication Methods Streamkap supports: * **Magic Link** (Passwordless email authentication) * **Google** * **GitHub** * **Microsoft** * **Slack** ## Self-Service SSO Setup We support self-service setup of SSO (Single Sign-On) via: * [SAML IDP Setup](/saml-idp-setup) * [OpenID Connect - IDP Setup](/openid-connect-idp-setup) ## Supported Identity Providers During step 1 (Configure Your IdP) of the SSO wizard you can choose from a set of pre-built templates or select a custom option. The following are currently supported: ### SAML | Provider | Notes | | ---------------- | ---------------------------------------- | | Okta | Pre-configured template | | Azure (Entra ID) | Pre-configured template | | Google | Pre-configured template | | OneLogin | Pre-configured template | | PingIdentity | Pre-configured template | | JumpCloud | Pre-configured template | | Rippling | Pre-configured template | | Custom SAML | Use for any other SAML 2.0 compliant IdP | ### OpenID Connect | Provider | Notes | | ------------- | ----------------------------------- | | Okta | Pre-configured template | | Custom OpenID | Use for any OIDC compliant provider | If your provider is not explicitly listed, choose the relevant *Custom* option and map the standard fields (ACS / redirect URL, Entity ID / Client ID, Audience, Metadata / JWKS, etc.) as described in the setup guides above. # Sign Up Source: https://docs.streamkap.com/sign-up Create your Streamkap account using email or social authentication Sign up to the app is via [app.streamkap.com](https://app.streamkap.com) ## Sign-up Methods You can sign up using any of the following methods: * **Email** - Enter your business email, name, and company name * **Google** - Sign up with your Google account * **GitHub** - Sign up with your GitHub account * **Microsoft** - Sign up with your Microsoft account * **Slack** - Sign up with your Slack workspace Streamkap Sign Up Page When signing up with email, you'll receive a magic link to verify your account. Make sure to use your business email address. # Snapshots & Backfilling Source: https://docs.streamkap.com/snapshots Trigger ad hoc Filtered, Full, Blocking, and Fast Parallel (Beta) snapshots in Streamkap to backfill historical data from your source databases. Please ensure you've followed the relevant connector setup guide to enable the snapshots feature Streamkap Sources use snapshots to backfill historical data from your source tables. ## Snapshot Options When triggering a snapshot, you can choose from three options: ### Filtered Snapshot Apply filter conditions to capture specific rows. Streaming continues during snapshot. * Best for capturing a subset of data based on conditions (e.g., date ranges, specific statuses) * Uses incremental watermarking to capture data in small chunks * Requires tables to have **primary keys** (or a [Surrogate Key](#surrogate-key)) * Can continue from where it left off on failure or cancellation The filter syntax depends on your Source type — see [Filtered Snapshot Configuration](#filtered-snapshot-configuration) below for the exact format and examples per Source. ### Full Snapshot Capture all rows from selected tables. Streaming continues during snapshot. * Best for complete data backfills where you need all historical data * Uses incremental watermarking to capture data in small chunks * Requires tables to have **primary keys** (or a [Surrogate Key](#surrogate-key)) * Can continue from where it left off on failure or cancellation ### Blocking Snapshot **Database locks:** Blocking snapshots may hold database locks for the duration of the operation. Use with caution on high-traffic tables. Capture all rows while pausing streaming. Streaming resumes automatically after snapshot completes. * **Required for keyless tables**: Tables without primary keys cannot use incremental snapshots (unless a [Surrogate Key](#surrogate-key) is specified) * **Point-in-time consistency**: Guarantees a consistent view of data at a specific moment * **Faster for large tables**: Can be more performant since it captures all data in one operation * **Multiple tables in parallel**: Depending on connector configuration, multiple tables can be snapshotted simultaneously However, blocking snapshots **cannot** be resumed on failure or cancellation—they must be re-triggered. Blocking snapshots do not currently support filters. To apply filters, use the Filtered Snapshot option instead (requires primary keys). ### Fast Parallel Snapshot **Beta — PostgreSQL only.** Fast Parallel snapshots are currently available for PostgreSQL Sources. Support for other Source types is in progress. Backfill large tables and large schemas much faster by reading many rows concurrently, while streaming continues. * **Best for very large tables, keyless tables, and large/regex schemas** where Filtered/Full can run for days * The only option available for [regex](/regex-support) connectors * **Reads in parallel** across multiple tables at once — and can split a single very large table into multiple concurrent readers (applied automatically based on table size) * **No source database locks and no long-running transactions.** Data is read in small chunks (a single short `SELECT` per chunk, sized to \~512 KB), so there is no server-side cursor and no lock held for the duration * **Streaming continues** throughout, like Filtered/Full snapshots * **Resumable:** continues from where it left off on failure or cancellation, per table * **No primary key required** on PostgreSQL — keyless tables are read by physical row location (`ctid`) without a blocking snapshot * **Supports filters and a [Surrogate Key](#surrogate-key)**, the same as Filtered and Full snapshots Snapshot Options dialog showing the Fast Parallel option Deletions are not captured during a Fast Parallel snapshot, the same as all other snapshot types — deletes are only reflected through streaming CDC. ### Surrogate Key Available under **Advanced Options** when configuring Filtered or Full snapshots. A surrogate key allows you to specify an alternative column for the connector to use as the primary key during snapshot chunking. This is useful when: * **Keyless tables:** Tables without primary keys can use a surrogate key (e.g., a timestamp or auto-increment column) for incremental snapshots instead of requiring a blocking snapshot * **Performance optimization:** A different column may provide better chunking performance (e.g., using `created_at` instead of a UUID primary key for more efficient range queries) Advanced Options showing Surrogate Key configuration **Limitations:** * Only **single-column** surrogate keys are supported (composite keys are not available) * The surrogate key column must exist in the table and contain sortable values Leave the field empty to use the table's primary key for chunking (default behavior). ## Log Retention Prerequisites Before triggering a snapshot, ensure your source database retains enough transaction log history to cover the snapshot duration plus a safety buffer. We recommend a minimum of 3 days retention. See the setup guide for your specific source for log retention configuration. If the log is truncated mid-snapshot, the connector may lose its position and require a full re-snapshot. If log retention is insufficient, a snapshot may still report success while CDC events from within the snapshot window are silently lost. Configure retention to cover the full snapshot duration plus a buffer to prevent this. ## Choosing a Snapshot Type Each snapshot type has different trade-offs for speed, streaming impact, and resumability: | Snapshot Type | Streaming During Snapshot | Resumable on Failure | Requires Primary Key | Best For | | ----------------------------------------- | ------------------------- | -------------------- | ---------------------------------------- | ----------------------------------------------------------------------------- | | **Filtered** | Yes (continues) | Yes | Yes (or [Surrogate Key](#surrogate-key)) | Backfilling a subset of data based on conditions | | **Full** | Yes (continues) | Yes | Yes (or [Surrogate Key](#surrogate-key)) | Complete backfills where all historical data is needed | | **Blocking** | No (paused) | No | No | Keyless tables, point-in-time consistency, or faster large-table snapshots | | **Fast Parallel** (Beta, PostgreSQL only) | Yes (continues) | Yes | No | Fastest option for very large tables and large/regex schemas; no source locks | * **Filtered and Full snapshots** use incremental watermarking, which is slower but allows streaming to continue simultaneously. If a table lacks a primary key, you can specify a [Surrogate Key](#surrogate-key) to enable incremental snapshots. * **Blocking snapshots** are faster for large tables because they capture all data in one operation, but streaming is paused for the duration. For very large tables with primary keys, consider using **Filtered snapshots** to backfill data in manageable time-bounded ranges. This reduces load on your source database and gives you more control over the process. ### Factors Affecting Duration | Factor | Impact | Guidance | | ------------------------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | | **Snapshot type** | Blocking is typically faster than incremental (Filtered/Full) for large tables | Use Blocking for speed when streaming can be paused; use Filtered/Full when streaming must continue | | **Table size (row count)** | Larger tables take proportionally longer | For tables with hundreds of millions of rows, expect snapshots to run for hours | | **Row width (columns and data size)** | Wide rows with large text/blob columns increase processing time | Tables with many columns or large payloads will snapshot more slowly | | **Source database load** | High concurrent query load can slow snapshot reads | Schedule snapshots during off-peak hours when possible | | **Network latency** | Higher latency increases round-trip time per chunk | Cross-region sources will experience slower snapshots | | **Number of tables** | Incremental snapshots process tables sequentially | Blocking snapshots may process multiple tables in parallel depending on connector configuration | | **Index availability** | Snapshots read data in primary key order; missing or fragmented indexes slow reads | Ensure primary keys are well-indexed on your source tables | ### Estimating Snapshot Time There is no exact formula, but as a general guideline: * **Small tables** (under 1 million rows): Typically complete within minutes * **Medium tables** (1-100 million rows): May take 30 minutes to several hours * **Large tables** (100+ million rows): Can take many hours depending on row width and source performance * **Very large tables** (1 TB+): Can take 24 hours to days depending on row width, source database performance, and network latency For incremental snapshots (Filtered/Full), snapshot speed is also influenced by the chunk size used during watermarked reads. Streamkap optimizes this automatically, but throughput depends on your source database's ability to serve read queries alongside its normal workload. ## Snapshot Lifecycle | When | Behavior | | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | At connector creation | The connector starts in streaming mode, reading any change data seen from this point onwards. No snapshots are triggered automatically. | | After connector creation | You can trigger ad-hoc snapshots for any or all of the tables the connector is configured to capture. A confirmation prompt is required before the snapshot begins. | | Pipeline creation and edit | You can choose to trigger snapshots for the topics the pipeline will stream to your destination. A confirmation prompt is required before the snapshot begins. | ## Behavior **Deletions are not captured during snapshots.** Snapshots read existing rows at a point in time—deletion events can only be processed during streaming, or, replayed if Streamkap data retention policies allow. ### Filtered & Full Snapshots These snapshots use incremental watermarking, capturing data in small chunks to minimize database impact. **Streaming continues** while historical data is being backfilled. When snapshotting multiple tables, tables are processed sequentially—one at a time. Each table must complete before the next begins. **PostgreSQL generated columns.** An incremental (Filtered or Full) snapshot of a PostgreSQL table that contains a [generated column](https://www.postgresql.org/docs/current/ddl-generated-columns.html) will fail unless that column is excluded from capture. Blocking and Parallel snapshots are not affected. See the [PostgreSQL setup guide](/postgresql-self-hosted#6-schema-and-table-capture). **On failure:** The snapshot resumes from where it left off. If it cannot resume automatically, you can re-trigger it at the Connector or Table level once the issue is resolved. **On cancellation:** The snapshot stops at its current progress. Streaming continues uninterrupted. You can resume the snapshot later from where it left off. When rows are modified while an incremental snapshot is running, event ordering may vary, because the Connector's streaming and snapshotting in parallel: * **Updates:** You may receive events in different orders (`read` → `update`, `update` → `read`, or just `update`) * **Deletes:** You may receive `read` → `delete`, or just `delete` This is normal behavior—The Connector resolves these out-of-sequence events when the same row appears in both the snapshot and the streaming log ensuring they are processed in the correct order and deduplicated. The snapshot process uses watermark signals to coordinate between the streaming and snapshot tracks: 1. A **low watermark** signal is written before each chunk is read 2. The chunk of rows is read from the source table (ordered by primary key) 3. A **high watermark** signal is written after the chunk is read 4. Any streaming events that arrived between the low and high watermarks are de-duplicated against the snapshot chunk This ensures that rows captured by both the snapshot and the streaming track are not duplicated in your destination. No data loss occurs during snapshots when log retention is configured appropriately — streaming change events that arrive while a snapshot is in progress are buffered and continue to be delivered. ### Blocking Snapshots **Database locks:** Blocking snapshots may hold database locks for the duration of the operation. Use with caution on high-traffic tables. These snapshots capture all data in a single transaction. **Streaming pauses** until the snapshot completes, then resumes automatically. Multiple tables may be processed in parallel depending on connector configuration. **On failure:** Streaming resumes immediately. Re-trigger the snapshot once the issue is resolved—it will start from the beginning since blocking snapshots capture all rows in one operation. **On cancellation:** Since streaming is paused during blocking snapshots, when you cancel a blocking snapshot, Streamkap restarts the connector to terminate the snapshot immediately. Streaming resumes after the restart. A brief delay exists between signaling a blocking snapshot and when streaming actually pauses. This may result in some duplicate events being emitted after the snapshot completes. Ensure your destination can handle idempotent writes or has deduplication enabled. **Snapshot Limitations: Streaming Transforms** Streaming transforms configured in your pipeline process all records flowing through the Kafka topic, including snapshot records. However, snapshot records have operation type `r` (read) rather than `c` (create) or `u` (update). If your transform logic filters or branches based on the operation type, verify that snapshot data is handled as expected after the snapshot completes. A high-performance [Fast Parallel Snapshot](#fast-parallel-snapshot) option is now available in Beta for PostgreSQL Sources. ## Triggering a Snapshot You can trigger an ad-hoc snapshot at the *Source* level or per *Table* from the Connector's page. ### Source Level Snapshot This will trigger an **incremental** snapshot for *all* tables/topics captured by the Source: Source quick actions menu with Snapshot option #### Snapshot Options Dialog When triggering a source-level snapshot, you can choose between Full Snapshot, Blocking Snapshot, or (for PostgreSQL Sources, in Beta) [Fast Parallel](#fast-parallel-snapshot): Source snapshot options dialog showing Full, Blocking, and Fast Parallel snapshot types ### Table/Topic Level Snapshot This will trigger a snapshot for the *selected* tables/topics only: Topic quick actions menu with Snapshot option #### Snapshot Options Dialog When triggering a table/topic snapshot, you can choose the snapshot type and configure advanced options: Snapshot options dialog showing Filtered, Full, and Blocking snapshot types with Advanced Options ### Filtered Snapshot Configuration Select **Filtered Snapshot** to apply filter conditions. The filter format depends on your Source type: | Source type | Sources | Filter format | Example | | ----------------------------- | ----------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | | **SQL-based** | PostgreSQL, MySQL, MariaDB, SQL Server, Oracle, DB2, Vitess, PlanetScale, Informix, AlloyDB, Supabase | SQL `WHERE` clause fragment | `created_at >= '2025-01-01' AND created_at < '2025-02-01'` | | **Document (MongoDB-family)** | MongoDB, MongoDB Atlas / Hosted, Amazon DocumentDB | BSON document in [MongoDB Extended JSON v2](https://www.mongodb.com/docs/manual/reference/mongodb-extended-json/) | `{"status": "active"}` | Before triggering a filtered snapshot on a large table or collection, make sure the right index exists. Without one, the snapshot can run for hours and may get stuck — recovery currently requires an offset reset. See [Best Practices](#best-practices-for-filtered-snapshots) below for what to check. Filtered snapshot configuration with SQL filter editor #### MongoDB-family filter examples MongoDB, MongoDB Hosted, and Amazon DocumentDB filters are parsed as BSON documents — type-specific values like ObjectIds and dates need their MongoDB Extended JSON form, not the literal driver syntax you might write in the shell. | Goal | Filter | | ------------------------------------ | ------------------------------------------------------------- | | Match a single document by `_id` | `{"_id": {"$oid": "65a1f2b3c4d5e6f7a8b9c0d1"}}` | | Match documents updated after a date | `{"updated_at": {"$gte": {"$date": "2025-01-01T00:00:00Z"}}}` | | Match a status field | `{"status": "active"}` | | Combine conditions | `{"status": "active", "amount": {"$gt": 100}}` | **SQL syntax against a Mongo Source is rejected.** Filters such as `_id = '65a1f2b3c4d5e6f7a8b9c0d1'` are not valid for MongoDB-family Sources and the API will reject the snapshot with HTTP 400 before sending it to the connector. Always use the BSON document form above. Extended JSON is also how the Streamkap connector emits Mongo data downstream, so filters and emitted records share the same value representation. **Translating from `mongosh` / MongoDB Compass.** If you're copying a filter from `mongosh`, MongoDB Compass, or a driver-side example, it likely uses **shell-style constructors** (`ObjectId(...)`, `ISODate(...)`, `NumberLong(...)`, etc.). The Streamkap API requires the **MongoDB Extended JSON** equivalent — translate before submitting: | Shell-style (mongosh / Compass) | Extended JSON (Streamkap filter) | | ------------------------------------------------------------------------- | ---------------------------------------------------------------------- | | `ObjectId("65a1f2b3c4d5e6f7a8b9c0d1")` | `{"$oid": "65a1f2b3c4d5e6f7a8b9c0d1"}` | | `ISODate("2025-01-01T00:00:00Z")`
`new Date("2025-01-01T00:00:00Z")` | `{"$date": "2025-01-01T00:00:00Z"}` | | `NumberLong("9000000000")` | `{"$numberLong": "9000000000"}` | | `NumberDecimal("19.99")` | `{"$numberDecimal": "19.99"}` | | `UUID("3b241101-e2bb-4255-8caf-4136c566a962")` | `{"$binary": {"base64": "OyQRAeK7QlWMr0E2xWapYg==", "subType": "04"}}` | | `/^A.*/i` (regex literal) | `{"$regularExpression": {"pattern": "^A.*", "options": "i"}}` | So a `mongosh` query like: ```javascript theme={null} db.orders.find({ _id: ObjectId("65a1f2b3c4d5e6f7a8b9c0d1") }) ``` becomes the following Streamkap filter: ```json theme={null} {"_id": {"$oid": "65a1f2b3c4d5e6f7a8b9c0d1"}} ``` #### Best Practices for Filtered Snapshots When using Filtered snapshots, we **strongly recommend**: * **Use closed range filters** when applying comparative operators on timestamp or date fields. Closed ranges (lower bound *inclusive*, upper bound *exclusive*) ensure you capture all intended data without gaps or overlaps. * **SQL Source:** `created_at >= '2025-01-01' AND created_at < '2025-02-01'` * **MongoDB-family Source:** `{"created_at": {"$gte": {"$date": "2025-01-01T00:00:00Z"}, "$lt": {"$date": "2025-02-01T00:00:00Z"}}}` * **Filter on indexed or primary key fields** for optimal performance. Filtering on columns that are part of your table's indices or primary key allows the database to efficiently locate matching rows, significantly reducing the load on your source database. * **Make sure your index covers both the filter column AND the ordering column.** When a filtered snapshot starts, it needs to find the highest ordering-column value that matches your filter (MongoDB uses `_id`; SQL uses the primary key). An index on just the filter column — or just the ordering column — isn't enough, and the database may end up scanning a large chunk of the table. Check this with your query planner before triggering on a large table. * **MongoDB-family Source:** if you're filtering on `lastupdated`, the index you need is `{ lastupdated: 1, _id: -1 }`. Verify with: `db..find().sort({_id: -1}).limit(1).explain("queryPlanner")`. The winning plan should show `IXSCAN` and no `SORT` stage. * **SQL Source:** if you're filtering on `updated_at` with a `BIGINT id` primary key, the index you need is `(updated_at, id)`. Run `EXPLAIN` on the filter query and confirm an index scan — not a sequential scan followed by a sort. ### Confirmation Prompt After initiating a snapshot, you must confirm your action by typing "snapshot" in the confirmation dialog: Snapshot confirmation dialog ## Snapshot Progress Upon triggering a snapshot, the Connector status will update to reflect the snapshot operation: Source status tab showing snapshot progress Also, the Topics list will show the snapshot status per table/topic: Topics table with snapshot status column ## Cancelling a Snapshot You can cancel an in-progress snapshot from the Connector's quick actions menu: Source quick actions menu with Cancel Snapshot option ## Snapshotting After Schema Changes When your source table schema changes (columns added, removed, or modified), you may need to trigger a new snapshot to ensure your destination reflects the updated structure. ### When to Snapshot Not all schema changes require a snapshot. Use the following guidance: | Schema Change | Snapshot Needed? | Reason | | ----------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **New column added** | Recommended | Existing rows in your destination will have `null` for the new column unless snapshotted. Streaming CDC will populate the new column for future changes only. | | **Column removed** | Usually not required | The removed column will stop appearing in new CDC events. Existing destination data retains the old column values. | | **Column type changed** | Recommended | Type mismatches between historical and new data can cause issues in your destination. A snapshot ensures consistency. | | **Table renamed** | Yes | A renamed table appears as a new topic. You must configure the connector to capture the new table name and trigger a snapshot. | | **Primary key changed** | Yes | Primary key changes affect how data is keyed and de-duplicated. A snapshot is required to ensure correct upsert behavior. | Streamkap supports [schema evolution](/schema-evolution-support) for most sources. Column additions and compatible type changes are automatically propagated to destinations that support schema evolution. However, a snapshot may still be needed to backfill the new column values for historical rows. ### How to Trigger a Snapshot You can trigger a snapshot using the same methods as any other snapshot: 1. **Via the UI**: Navigate to the Source detail page and trigger a snapshot at the Source level (for all tables) or at the individual Table/Topic level. See [Triggering a Snapshot](#triggering-a-snapshot) above. 2. **Via the API**: Use the Streamkap API to trigger a snapshot programmatically. This is useful for automating snapshots as part of a schema migration workflow. See the [REST API](/api) documentation for details. ### Best Practices for Schema Change Snapshots * **Wait for the schema change to propagate** before triggering a snapshot. Ensure the DDL change has been committed and is visible in the source database's change log. * **Avoid schema changes during active snapshots.** If a snapshot is already running for a table, wait for it to complete (or cancel it) before applying DDL changes. Schema changes during an active snapshot are not supported and may cause failures. * **Use Filtered (Partial) snapshots** if you only need to backfill the new column for a specific time range rather than snapshotting the entire table. * **Coordinate with downstream consumers.** If your destination enforces strict schemas, ensure the destination table has been updated to accept the new schema before triggering the snapshot. ## Troubleshooting ### Failed Snapshot Recovery If a Filtered or Full snapshot fails, it can resume from where it left off once the underlying issue is resolved. If the snapshot cannot automatically resume, re-trigger it — it will continue from the last completed chunk rather than restarting from the beginning. Blocking snapshots cannot be resumed and must be re-triggered from scratch. Common causes of snapshot failure include network timeouts, source database overload, insufficient permissions, disk space exhaustion, and schema changes during the snapshot. Check the connector's [Logs](/logs) for specific error messages, and see the [Error Reference](/error-reference) for detailed resolution steps. If a snapshot fails repeatedly for the same table, contact [Streamkap support](mailto:support@streamkap.com) with the connector logs and error details. ### Verifying Snapshot Completion After triggering a snapshot, you can verify that it completed successfully and that your destination contains the expected data. #### Check Snapshot Status in the UI The most direct way to confirm completion is through the Streamkap UI. Navigate to your [Source's](/sources) detail page and check the **Status** tab: * **Per-topic status:** Each table/topic shows its snapshot state (e.g., "Running", "Completed"). When all topics show "Completed", the snapshot is finished. * **Connector status:** The connector status returns to its normal streaming state after all snapshots complete. #### Compare Row Counts To verify data completeness, compare the row count at the source with the row count at the destination: 1. **Source row count:** Run a `SELECT COUNT(*)` (or equivalent) on the source table. For very large tables, an approximate count may be sufficient (e.g., `pg_class.reltuples` in PostgreSQL or `TABLE_ROWS` from `information_schema.tables` in MySQL). 2. **Destination row count:** Run a `SELECT COUNT(*)` on the corresponding destination table. Row counts may not match exactly during or immediately after a snapshot because streaming CDC events (inserts, updates, deletes) continue to arrive concurrently. A small difference is normal. If the counts diverge significantly, investigate further. #### Check Pipeline Lag Pipeline lag indicates how far behind the destination is from the source. After a snapshot completes: * **Lag drops to near-zero:** The pipeline has caught up and is processing events in near real-time. This confirms the snapshot is complete and streaming has resumed normally. * **Lag remains elevated:** The pipeline may still be processing buffered events that accumulated during the snapshot. Wait for lag to stabilize before concluding. You can monitor pipeline lag on the [Pipelines](/pipelines) page or via the [API](/api). #### Distinguishing "Stuck" from "Still Running" If a snapshot appears to be taking longer than expected: * **Check throughput metrics.** If records per second is greater than zero, the snapshot is still actively processing data. Large tables simply take longer. You can view throughput on the [Pipelines](/pipelines) page. * **Check for errors.** If throughput has dropped to zero and the status does not show "Completed", the snapshot may have encountered an error. Check the connector [Logs](/logs) and the [Failed Snapshot Recovery](#failed-snapshot-recovery) section. * **Check source database load.** High load on the source can slow snapshot reads significantly. Monitor the source database's CPU, I/O, and active connections during the snapshot. For very large snapshots, monitor throughput trends rather than absolute progress. A steady throughput rate (even if slow) indicates the snapshot is progressing normally. A sudden drop to zero throughput is the signal to investigate. # Snowflake Source: https://docs.streamkap.com/snowflake Stream CDC data into Snowflake using Streamkap, with support for upsert and append ingestion modes, schema evolution, Snowflake-managed Iceberg tables, and key pair authentication. ## Prerequisites A Snowflake account granted `ACCOUNTADMIN` [system-defined role](https://docs.snowflake.com/en/user-guide/security-access-control-overview.html#system-defined-roles) or [custom role](https://docs.snowflake.com/en/user-guide/security-access-control-overview.html#custom-roles) with privileges to: * `CREATE WAREHOUSE, DATABASE, SCHEMA` * `CREATE ROLE, USER` * `CREATE NETWORK POLICY` ## Snowflake Setup It's recommended to create a separate user and role for Streamkap to access your Snowflake database. Below is an example script that does that. ```SQL SQL theme={null} -- We've provided defaults so, change these as required with names for Database Objects in 'UPPERCASE' SET user_name = UPPER('STREAMKAP_USER'); SET user_password = '{password}'; -- IMPORTANT: Make sure to change this! SET warehouse_name = UPPER('STREAMKAP_WH'); -- Used for automatic QA, UPSERT mode and optional views SET database_name = UPPER('STREAMKAPDB'); SET schema_name = UPPER('STREAMKAP'); SET role_name = UPPER('STREAMKAP_ROLE'); SET network_policy_name = UPPER('STREAMKAP_NETWORK_ACCESS'); -- If your Snowflake account uses custom roles to grant privileges, change these values below SET sysadmin_role = UPPER('SYSADMIN'); SET securityadmin_role = UPPER('SECURITYADMIN'); SET accountadmin_role = UPPER('ACCOUNTADMIN'); -- Create a warehouse with defaults: -- Standard, X-Small, No Scaling, Auto-Suspend after 1 Minute USE ROLE IDENTIFIER($sysadmin_role); CREATE WAREHOUSE IF NOT EXISTS IDENTIFIER($warehouse_name) AUTO_SUSPEND =1; -- Create a database and schema for Streamkap USE WAREHOUSE IDENTIFIER($warehouse_name); CREATE DATABASE IF NOT EXISTS IDENTIFIER($database_name); USE DATABASE IDENTIFIER($database_name); CREATE SCHEMA IF NOT EXISTS IDENTIFIER($schema_name); -- Create a Snowflake role with privileges for the Streamkap connector USE ROLE IDENTIFIER($securityadmin_role); CREATE ROLE IF NOT EXISTS IDENTIFIER($role_name); -- Grant privileges on the warehouse GRANT USAGE ON WAREHOUSE IDENTIFIER($warehouse_name) TO ROLE IDENTIFIER($role_name); -- Grant privileges on the database GRANT USAGE ON DATABASE IDENTIFIER($database_name) TO ROLE IDENTIFIER($role_name); -- Optional: Permissions for auto schema creation on the database GRANT CREATE SCHEMA ON DATABASE IDENTIFIER($database_name) TO ROLE IDENTIFIER($role_name); -- Grant privileges on the database schema USE ROLE IDENTIFIER($sysadmin_role); USE DATABASE IDENTIFIER($database_name); GRANT USAGE ON SCHEMA IDENTIFIER($schema_name) TO ROLE IDENTIFIER($role_name); GRANT CREATE TABLE ON SCHEMA IDENTIFIER($schema_name) TO ROLE IDENTIFIER($role_name); GRANT CREATE FILE FORMAT ON SCHEMA IDENTIFIER($schema_name) TO ROLE IDENTIFIER($role_name); GRANT CREATE STAGE ON SCHEMA IDENTIFIER($schema_name) TO ROLE IDENTIFIER($role_name); GRANT CREATE PIPE ON SCHEMA IDENTIFIER($schema_name) TO ROLE IDENTIFIER($role_name); -- Grant privileges for dynamic table and task creation (Only if auto-creation is enabled) GRANT CREATE DYNAMIC TABLE ON SCHEMA IDENTIFIER($schema_name) TO ROLE IDENTIFIER($role_name); GRANT CREATE TASK ON SCHEMA IDENTIFIER($schema_name) TO ROLE IDENTIFIER($role_name); USE ROLE IDENTIFIER($accountadmin_role); GRANT EXECUTE TASK ON ACCOUNT TO ROLE IDENTIFIER($role_name); -- Create a user for Streamkap USE ROLE IDENTIFIER($securityadmin_role); CREATE USER IDENTIFIER($user_name) PASSWORD = $user_password DEFAULT_ROLE = $role_name; -- Grant the custom role to the Streamkap user GRANT ROLE IDENTIFIER($role_name) TO USER IDENTIFIER($user_name); -- Set the custom role as the default role for the Streamkap user. -- If you encounter an 'Insufficient privileges' error, verify the '$securityadmin_role' has OWNERSHIP privilege on the '$user_name'. ALTER USER IDENTIFIER($user_name) SET DEFAULT_ROLE = $role_name; -- Prevents Snowflake getting confused in an edge-case where a table exists in the public schema and current schema with the same name. ALTER USER IDENTIFIER($user_name) SET SEARCH_PATH = '$current'; -- Allow the Streamkap user access to the Snowflake account -- Latest IPs can be found here: https://docs.streamkap.com/docs/streamkap-ip-addresses -- If you need to edit the network policy, you can use: -- ALTER NETWORK POLICY STREAMKAP_NETWORK_ACCESS SET ALLOWED_IP_LIST=('52.32.238.100'); CREATE NETWORK POLICY IDENTIFIER($network_policy_name) ALLOWED_IP_LIST=('52.32.238.100'); ALTER USER IDENTIFIER($user_name) SET NETWORK_POLICY = $network_policy_name; ``` We do not use `CREATE OR REPLACE` in our scripts. This is to avoid destroying something by mistake that already exists in your Snowflake account. **Warehouse auto-suspend for CDC workloads** The setup script above sets `AUTO_SUSPEND = 1` (1 minute). For CDC workloads -- especially in **upsert mode** where a warehouse must be running -- very short auto-suspend timeouts can cause frequent suspend/resume cycles. Each resume incurs provisioning latency (up to several seconds) and additional Snowflake credit charges, since warehouses are billed in 60-second minimum increments per resume. For active CDC pipelines, consider setting `AUTO_SUSPEND` to **60 seconds or higher** and ensure the warehouse size matches your ingestion throughput. In append mode with Snowpipe Streaming, the warehouse is only used for optional operations (QA, dynamic tables, tasks), so a short auto-suspend is acceptable. ### Key Pair Authentication The connector relies on an RSA key pair for authentication which you can generate using OpenSSH. Below are example scripts that do that. You can modify them to suit your security policies, but please ensure the key pair meets these **minimum** requirements: * RSA 2048-bit * PKCS#8 key format **SSH key generation on Windows** Snowflake does **not** support keys generated by [PuTTY](https://www.putty.org/) Key Generator. One of the easiest and quickest ways to generate a valid OpenSSL key is via Git Bash which is installed by default with [Git for Windows](https://gitforwindows.org/). After installation, you can open a Git Bash prompt by Left Shift + Right Clicking on your Desktop, choosing "Open Git Bash here" and then executing the OpenSSL commands below. If you have any issues following these instructions or are unable to install Git for Windows, please contact us. ```bash Shell - With passphrase theme={null} # Make sure to change '{passphrase}' to a password of your choice openssl genrsa 2048 | openssl pkcs8 -topk8 -v2 aes256 -inform PEM -out streamkap_key.p8 -passout pass:{passphrase} # generates the public key, referencing the private key # Don't forget to replace '{passphrase}' with the password used in the previous command openssl rsa -in streamkap_key.p8 -pubout -out streamkap_key.pub -passin pass:{passphrase} ``` ```bash Shell - Without passphrase theme={null} openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out streamkap_key.p8 -nocrypt # generates the public key, referencing the private key openssl rsa -in streamkap_key.p8 -pubout -out streamkap_key.pub ``` The scripts above should create two files (the key pair), one private (may have an extension e.g. `.p8`) and the other public (usually has the extension `.pub`). Store both files in a **secure** place. Once generated, the public key needs to be assigned to the Snowflake database user created for Streamkap earlier. This command will copy the public key you generated to your clipboard. ```PowerShell Shell theme={null} egrep -v '^-|^$' ./streamkap_key.pub | pbcopy ``` ```PowerShell PowerShell theme={null} Get-Content .\streamkap_key.pub | Where-Object { $_ -notmatch '^-|^$' } | Set-Clipboard ``` Now attach the public key to the user: ```SQl SQL theme={null} -- We've provided a default, so change this as required SET user_name = UPPER('STREAMKAP_USER'); USE ROLE SECURITYADMIN; -- Replace '{public key}' below with the public key file contents -- If you used the previous command to copy the key to your clipboard, use Ctrl+V (Windows) -- or Cmd+V (MacOS) to replace the '{public key}' placeholder with the key -- Key part MUST start with 'MII' excluding any headers and footers ALTER USER IDENTIFIER($user_name) SET RSA_PUBLIC_KEY = '{public key}'; ``` ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Destination * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Destinations). * Choose **Snowflake**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Snowflake URL**: The URL for accessing your Snowflake account. This URL must include your account identifier. Note that the protocol (`https://`) and port number are optional. * **Username**: User login name for the Snowflake account (Case sensitive). * **Private Key**: Provide the private key you generated by using the command below. ```PowerShell Shell theme={null} egrep -v '^-|^$' ./streamkap_key.p8 | pbcopy ``` ```PowerShell PowerShell theme={null} Get-Content .\streamkap_key.p8 | Where-Object { $_ -notmatch '^-|^$' } | Set-Clipboard ``` * **Key secured with passphrase?**: If checked (default), provide your SSH key's passphrase, otherwise, uncheck for SSH keys without passphrase. * **Private Key Passphrase**: The passphrase is used to decrypt the private key. * **Database Name**: The name of the database to use (Case sensitive). * **Schema Name**: The name of the schema where tables will be created (Case sensitive). * **Snowflake Role**: The name of an existing role with necessary privileges (for Streamkap) assigned to the user specified by **Username** (Case sensitive). ### 3. Ingestion Settings * **Ingestion Mode**: How the Connector loads data into the Snowflake tables. See [Upsert mode](/snowflake#upsert-mode) for further details. **Changing ingestion mode** `append` and `upsert` modes use different, incompatible methods for loading data into the Snowflake tables. If - for whatever reason - you want to change modes for an existing Snowflake Connector, please create a **new** Snowflake Destination instead i.e. a separate destination for `append`, and for `upsert`. * **`append`mode**: * **Use Dynamic Tables**: Specifies whether the connector should create Dynamic Tables & Cleanup Tasks. See [Dynamic Tables](/snowflake#dynamic-tables). * **Custom SQL Template - Dynamic Table Creation**: These template queries run for each table the first time a record is streamed for them. Changes are not retroactive for tables that already have a Dynamic Table — see [Dynamic Tables](/snowflake#dynamic-tables) for how to force recreation. * **Custom SQL Template - Dynamic Table Name**: Can be used as `{{dynamicTableName}}` in dynamic table creation SQL. It can use input JSON data for more complex mappings and logic. * **Custom SQL Template - Input JSON data**: Use `{"TABLE_DATA": {"{table_name}": {"{key}": "{value}"}, ...}, ...}` to set table specific data. This data will be available in the custom SQL templates e.g. `SELECT {{key}}`. * **Auto QA Deduplication Table Mapping**: Mapping between the tables that store append-only data and the deduplicated tables. The dedupeTable in mapping will be used for QA scripts. If dedupeSchema is not specified, the deduplicated table will be created in the same schema as the raw table. * **Use Iceberg Table**: Specifies whether the connector should write into a pre-existing Snowflake-managed Iceberg table instead of a standard Snowflake table. This is a distinct ingestion mode with its own Snowflake setup and privileges, and **cannot be changed after the destination is created**. See [Iceberg Tables](/snowflake#iceberg-tables) before enabling. * **`upsert`mode**: * **Delete Mode**: Specifies whether the connector processes deletions (or tombstone events) and removes the corresponding row from the database. * **Use Hybrid Tables**: Specifies whether the connector should create Hybrid Tables. Click **Save**. ## Iceberg Tables Streamkap's Snowflake Connector can write into a [Snowflake-managed Iceberg table](https://docs.snowflake.com/en/user-guide/kafka-connector/classic/iceberg) instead of a standard Snowflake table. This is enabled with the **Use Iceberg Table** setting, available in **append** mode. Iceberg ingestion is a **distinct ingestion mode**, not a variation of standard-table ingestion. It has its own Snowflake setup, its own privileges, and its own behaviour — all covered below. Read this section in full before creating an Iceberg destination. **Use Iceberg Table cannot be changed after the destination is created.** The setting is fixed at creation. To switch an existing connector between standard and Iceberg tables you must create a **new** destination — there is no in-place migration, and the existing tables are not converted. Decide which mode you need before you create the destination. ### Snowflake setup for Iceberg This is **in addition to** the [Snowflake Setup](/snowflake#snowflake-setup) above — the warehouse, user, role and network policy are still required. Iceberg then needs three further things. **1. An external volume.** Iceberg tables store their data and metadata as open-format files in your own cloud storage, so an [external volume](https://docs.snowflake.com/en/user-guide/tables-iceberg-configure-external-volume) must exist and the Streamkap role must be able to use it. Setting a default external volume on the database or schema is recommended — the connector's Dynamic Table DDL then inherits it, and no template editing is required. ```SQL SQL theme={null} SET role_name = UPPER('STREAMKAP_ROLE'); SET database_name = UPPER('STREAMKAPDB'); SET schema_name = UPPER('STREAMKAP'); SET external_volume = UPPER('MY_EXTERNAL_VOLUME'); -- your existing external volume -- Allow the Streamkap role to use the external volume. -- Run as the role that owns the external volume (ACCOUNTADMIN only if that is the owner). GRANT USAGE ON EXTERNAL VOLUME IDENTIFIER($external_volume) TO ROLE IDENTIFIER($role_name); -- Recommended: make it the default so Iceberg DDL can omit EXTERNAL_VOLUME ALTER SCHEMA IDENTIFIER($schema_name) SET EXTERNAL_VOLUME = $external_volume; ``` **2. Pre-created Iceberg tables.** Unlike standard Snowflake tables, Streamkap does **not** create Iceberg tables. Create one per topic, before starting the connector. Only the metadata column is required — the connector adds the data columns by schema evolution. ```SQL SQL theme={null} CREATE ICEBERG TABLE MY_TABLE ( record_metadata OBJECT() ) EXTERNAL_VOLUME = 'MY_EXTERNAL_VOLUME' -- omit if set as the schema default CATALOG = 'SNOWFLAKE'; ``` The table name must match the name the connector writes to, as determined by your topic-to-table mapping. **3. Ownership of each Iceberg table, and schema evolution enabled on it.** Because the tables are created by you rather than by the connector, both of the following are required on every Iceberg table the connector writes to. ```SQL SQL theme={null} -- Give the Streamkap role ownership of the table GRANT OWNERSHIP ON TABLE MY_TABLE TO ROLE IDENTIFIER($role_name); -- Enable schema evolution on the table itself ALTER ICEBERG TABLE MY_TABLE SET ENABLE_SCHEMA_EVOLUTION = TRUE; ``` Streamkap always runs with schema evolution active, and the connector checks **two** things before it will start: 1. the role holds `OWNERSHIP` (or `EVOLVE SCHEMA`) on the table, **and** 2. the table has `ENABLE_SCHEMA_EVOLUTION = TRUE`. Both are required. Because Streamkap does not create Iceberg tables, it does not set this property for you — unlike standard tables, where the connector enables it at creation. If either is missing the connector fails on startup with a schema evolution error. Both statements must be run for every Iceberg table you create for the connector. Schema evolution works as it does for standard tables: new columns are added automatically as they appear in the source. Iceberg additionally allows an existing column to be widened in place — for example `INT` to `LONG`, or an increase in decimal precision. ### Dynamic Tables with Iceberg [Dynamic Tables](/snowflake#dynamic-tables) work with Iceberg, but they are created as **Dynamic Iceberg Tables** so the deduplicated table also stays in open format in your external volume. Streamkap selects the correct template automatically when **Use Iceberg Table** is enabled. A standard Dynamic Table cannot be used over an Iceberg table, because it cannot hold the typed `RECORD_METADATA` object that Iceberg tables use. The default template omits `EXTERNAL_VOLUME` so that it is inherited from the database or schema default. If you have not set a default, edit the **Custom SQL Template - Dynamic Table Creation** field in the Streamkap UI to add `EXTERNAL_VOLUME = ''`. ### Behaviour differences vs. standard tables * **Tables are not auto-created.** They must exist before the connector starts (see above). * **JSON-sourced columns** are written as plain text rather than a native, queryable `VARIANT` column. * **Nested or composite source columns** (structs, arrays, maps) are written as rigid, fully-typed `OBJECT`/`ARRAY`/`MAP` structures rather than a flexible `VARIANT` column — every new field or shape requires a schema-evolution event, and values outside the mapped types will cause errors. * **Record keys** are written to `RECORD_METADATA.key` as text. Composite and structured keys are serialised to JSON. * **Numeric and temporal columns** behave the same as on standard tables. For further detail, see Snowflake's [Snowpipe Streaming for Iceberg documentation](https://docs.snowflake.com/en/user-guide/snowpipe-streaming/snowpipe-streaming-high-performance-iceberg), which covers additional limitations and caveats on the Snowflake side. ## Troubleshooting ### Dynamic Tables Snowflake Dynamic Tables are materialized views which consist of the latest records inserted into Snowflake. Streamkap's Snowflake Connector creates them—if enabled—for each table the first time a record is streamed for them. They refresh themselves according to their `TARGET_LAG`. Below is the default template—shown in the Streamkap UI. You can modify it there to suit your requirements. ```SQL SQL theme={null} CREATE OR REPLACE DYNAMIC TABLE {{table}}_DT TARGET_LAG = '15 minutes' -- Minimum is 1 minute WAREHOUSE = {{warehouse}} AS SELECT * EXCLUDE dedupe_id FROM ( SELECT *, ROW_NUMBER() OVER ( PARTITION BY {{primaryKeyColumns}} ORDER BY _streamkap_ts_ms DESC, _streamkap_offset DESC ) AS dedupe_id FROM {{table}} ) WHERE dedupe_id = 1 -- Latest record AND __deleted = 'false'; -- Excluding deleted records CREATE OR REPLACE TASK {{table}}_CT -- This statement and the `ALTER TASK {{table}}_CT RESUME;` can be removed if you don't want to clean up old records WAREHOUSE = {{warehouse}} SCHEDULE = '4380 minutes' -- We don't recommend changing this to a very short interval (e.g. 30 minutes) as it can increase Snowflake costs TASK_AUTO_RETRY_ATTEMPTS = 3 ALLOW_OVERLAPPING_EXECUTION = FALSE AS DELETE FROM {{table}} WHERE NOT EXISTS ( SELECT 1 FROM ( SELECT {{primaryKeyColumns}}, MAX(_streamkap_ts_ms) AS max_timestamp FROM {{table}} GROUP BY {{primaryKeyColumns}} ) AS subquery WHERE {{{keyColumnsAndCondition}}} AND {{table}}._streamkap_ts_ms = subquery.max_timestamp ); ALTER TASK {{table}}_CT RESUME; ``` **Template changes are not retroactive.** The connector only runs the **Custom SQL Template - Dynamic Table Creation** template for a table the first time it detects that table doesn't already have a Dynamic Table. Once a Dynamic Table exists, editing the template has no effect on it — the new template only applies to tables that don't yet have one (e.g. new tables streamed for the first time after the change). To apply a template change to an existing table, drop its Dynamic Table and Cleanup Task and let the connector recreate them: ```SQL SQL theme={null} DROP TASK IF EXISTS {{table}}_CT; DROP DYNAMIC TABLE IF EXISTS {{table}}_DT; ``` The connector detects the missing Dynamic Table on its next schema check and recreates it using the current template. Restarting or pausing/resuming the connector alone does **not** force recreation for tables that still have an existing Dynamic Table. ### Offset Management (Append Mode) Streamkap retains topic data based on your service's retention policy (typically 7 days by default). You can only replay messages that are still within the retention window. #### Understanding Dual Offset Tracking When using **append mode** with Snowflake destinations, there are two, separate offset systems to manage: 1. **Consumer Group Offsets** (`connect-{connector-id}`) * Tracks which messages the connector has consumed from the source topic * Visible in Streamkap UI under [Consumer Groups](https://app.streamkap.com/consumer-groups) 2. **Snowflake Channel Offsets** * Tracks which messages have been successfully ingested into Snowflake via Snowpipe Streaming * Each topic partition creates a Snowflake channel (e.g., `TOPIC_0` for partition 0) * Managed within Snowflake using `SYSTEM$SNOWPIPE_STREAMING_UPDATE_CHANNEL_OFFSET_TOKEN` These two offset systems can become **misaligned**, especially when topics are deleted and recreated. **Symptoms of misalignment can include:** * New data is expected but not appearing in the destination tables * Lag showing as negative (e.g. -1) or unusually high in Streamkap UI * Snowflake channels showing offset positions that don't match the Consumer Group offsets (via `SHOW CHANNELS` command) * Missing data in destination table **Critical requirement**: Destinations must be stopped before attempting any offset reset operations. #### Replaying Messages to Snowflake To replay messages (resend data to Snowflake), you must coordinate resets across both offset systems: **Before you start:** * Ensure you have the required Snowflake privileges (e.g., OWNER on the table) * Note the offset position you want to replay to (earliest, specific offset, or timestamp) * Plan for the replay window within your topic retention period **Step 1: Stop the Destination Connector** In Streamkap UI, navigate to the connector and click **Stop** or **Pause**. Wait for the connector status to show as stopped. This prevents Snowpipe Streaming from committing offsets while you reset them. **Step 2: Reset Consumer Group Offsets** Use Streamkap UI to reset the consumer group: 1. Go to **[Consumer Groups](https://app.streamkap.com/consumer-groups)** 2. Find the consumer group for the destination connector (e.g. `connect-{connector-id}`) 3. Follow the [Consumer Groups Reset Procedure](/consumer-groups#reset-procedure) to reset to your desired position To find the connector ID, navigate to the connector details page. The ID appears in the URL, or by clicking Copy ID in the quick actions menu (three dots) at the top right. **Step 3: Reset Snowflake Channel Offsets** Connect to Snowflake (using your preferred client: SnowSQL, DBeaver, or Snowflake web UI) and reset each channel to `-1`. This tells Snowflake to defer to the Consumer Group offset position. ```sql theme={null} -- First, view current channels and their offsets SHOW CHANNELS IN SCHEMA .; -- For each channel, reset the offset to -1 -- Replace , , , and with your actual values SELECT SYSTEM$SNOWPIPE_STREAMING_UPDATE_CHANNEL_OFFSET_TOKEN( '..', '', -- Channel name (usually TOPIC_PARTITION_NUMBER, e.g., ORDERS_0, ORDERS_1) '-1' -- Reset to -1 so Snowflake uses the Consumer Group offset ); ``` **Example: Reset channel to use Consumer Group offset** ```sql theme={null} -- Reset topic "orders" partition 0 to use Consumer Group offset SELECT SYSTEM$SNOWPIPE_STREAMING_UPDATE_CHANNEL_OFFSET_TOKEN( 'MY_DB.MY_SCHEMA.ORDERS_TABLE', 'ORDERS_0', '-1' ); -- Repeat for each partition (ORDERS_1, ORDERS_2, etc.) ``` Setting the Snowflake channel offset to `-1` simplifies the reset process. Instead of manually aligning offset numbers between the Consumer Group and Snowflake channels, the channel will automatically start from the Consumer Group's current offset position. **Step 4: Resume the Destination Connector** In Streamkap UI, click **Resume** or **Start** on the connector. **Step 5: Verify the Replay** Monitor the connector and destination table to confirm: * Connector status shows as active/running * Consumer lag decreases as messages are re-ingested * Data appears in the destination table #### Offset Reset Strategies The offset position is controlled by the Consumer Group. When resetting offsets, choose one of the following strategies in Streamkap UI (see [Consumer Groups Reset Procedure](/consumer-groups#reset-procedure)): | Strategy | Description | Use Case | | ---------------------- | ------------------------------------------------- | ----------------------------------------------------- | | **Earliest** | Reset to the beginning of the partition | Replay all available messages within retention window | | **Latest** | Reset to the end of the partition | Skip all existing messages and start fresh | | **Specific Timestamp** | Reset to the first offset after a given timestamp | Replay messages from a specific point in time | | **Specific Offset** | Set a custom offset position | Precise control over where to resume | After resetting the Consumer Group offset to your desired position, reset the Snowflake channel offset to `-1` so it defers to the Consumer Group position: ```sql theme={null} SELECT SYSTEM$SNOWPIPE_STREAMING_UPDATE_CHANNEL_OFFSET_TOKEN( '..', '', '-1' ); ``` ### Upsert mode Snowflake destination connector can run in *upsert* mode. This mode switches off the use of snowpipe streaming and connector uses periodic `MERGE INTO` statements to *upsert* data into target snowflake tables. Dynamic tables or other de-duplication mechanisms will not be necessary when using *upsert* mode. **Snowflake costs** Currently *upsert* mode requires a warehouse to be running so overall the costs will be higher compared to append mode which uses snowpipe streaming. ### Getting the Snowflake URL You can also run the script below in a Snowflake worksheet to return the Snowflake URL. You need to be logged into Snowflake with an account granted `ORGADMIN` [system-defined role](https://docs.snowflake.com/en/user-guide/security-access-control-overview.html#system-defined-roles) to run this script. ```SQL SQL theme={null} USE ROLE ORGADMIN; -- Snowflake URL is the 'account_url', not 'account_locator_url' SHOW ACCOUNTS; ``` ### Snowflake Setup scripts failing There can be many reasons for them to fail, but the scripts below can help you diagnose the issues. You need to be logged into Snowflake with an account granted `ACCOUNTADMIN` [system-defined role](https://docs.snowflake.com/en/user-guide/security-access-control-overview.html#system-defined-roles) or custom role with equivalent privileges to run these scripts. Copy paste the scripts below into Snowflake worksheets. Change the object names at the top as required and run all queries. ```SQL Script #1 - Check Objects exist theme={null} -- Replace object names below with the names used by your Snowflake Setup script SET warehouse_name = UPPER('STREAMKAP_WH'); SET database_name = UPPER('STREAMKAPDB'); SET schema_name = UPPER('STREAMKAP'); SET role_name = UPPER('STREAMKAP_ROLE'); SET user_name = UPPER('STREAMKAP_USER'); SET network_policy_name = UPPER('STREAMKAP_NETWORK_ACCESS'); -- If your Snowflake account uses custom roles to grant privileges, change the role name below SET accountadmin_role = UPPER('ACCOUNTADMIN'); USE ROLE IDENTIFIER($accountadmin_role); -- If any of the queries fail or return no results, '$accountadmin_role' doesn't have necessary privileges, or the object doesn't exist -- Warehouses, databases and schemas DESC WAREHOUSE IDENTIFIER($warehouse_name); DESC DATABASE IDENTIFIER($database_name); -- Displays schemas; no need for DESC SCHEMA ... query also -- Users DESC USER IDENTIFIER($user_name); -- Shows name, defaults and RSA details; no need for separate queries -- Network policies DESC NETWORK POLICY IDENTIFIER($network_policy_name); ``` ```SQL Script #2 - List privileges theme={null} -- Replace role name below with the role name used by your Snowflake Setup script SET role_name = UPPER('STREAMKAP_CONNECTOR'); -- If your Snowflake account uses custom roles to grant privileges, change the role name below SET accountadmin_role = UPPER('ACCOUNTADMIN'); USE ROLE IDENTIFIER($accountadmin_role); -- List privileges SHOW GRANTS TO ROLE IDENTIFIER($role_name); ``` If any of the queries return an **error** or **no results**: * Check in the top right corner (next to **Share** and **Run** buttons) of the Snowflake Worksheets that the role is set to `ACCOUNTADMIN`, or a custom role with equivalent privileges * Depending on which query failed or returned no results, check the object names at the **top** of the script are correct * If a query returns `"Object does not exist or is not authorized"` error, go to the [Snowsight UI](https://docs.snowflake.com/en/user-guide/ui-snowsight-gs.html#using-snowsight) **Admin** page and see if the object is showing there. For example, if `DESC WAREHOUSE ...` failed, go to **Admin** -> **Warehouses** page and check if the Warehouse is shown on that page If the warehouse, database, schema, role and user exists, **privileges** might be an issue. Run Script #2 and ensure the privileges displayed match or include the following: ### Troubleshooting Common Issues Transient Snowflake Streaming API overload. **Do NOT restart the connector** — the built-in retry mechanism with exponential backoff handles recovery automatically, typically within 5-30 minutes. If the error persists beyond 30 minutes, contact [Streamkap support](mailto:support@streamkap.com). Snowflake has a maximum record size of 16 MB. Records exceeding this limit are routed to the [dead letter queue (DLQ)](/dlq-operations). **Resolution:** * Identify the oversized columns in the DLQ message payload * Exclude large columns from replication if they are not needed at the destination * Add a transform to truncate large fields before they reach the destination When a source column value exceeds the VARCHAR size defined at the destination, the record is routed to the [DLQ](/dlq-operations). **Resolution:** * Increase the column size at the destination: `ALTER TABLE ... ALTER COLUMN ... TYPE VARCHAR(n)` * Configure a transform (SMT) to truncate values before delivery if increasing the column size is not feasible ### Schema Evolution Permissions Streamkap's Snowflake connector supports schema evolution -- automatically adding new columns to destination tables when the source schema changes. For this to work, the Snowflake role used by the connector must have the correct privileges on the target tables. **OWNERSHIP privilege required for schema evolution** Schema evolution requires the `OWNERSHIP` privilege on the target tables -- not just `ALTER`. When Snowpipe Streaming detects new columns in incoming data, it must alter the destination table, which Snowflake restricts to the table owner. If schema evolution fails with an `Insufficient privileges` error, grant `OWNERSHIP` on the affected tables (or future tables in the schema) to the Streamkap role: ```sql theme={null} -- Grant ownership on all existing tables in the schema GRANT OWNERSHIP ON ALL TABLES IN SCHEMA . TO ROLE STREAMKAP_ROLE COPY CURRENT GRANTS; -- Grant ownership on future tables so new tables are also covered GRANT OWNERSHIP ON FUTURE TABLES IN SCHEMA . TO ROLE STREAMKAP_ROLE COPY CURRENT GRANTS; ``` If you do not want to grant `OWNERSHIP`, you can pre-create your destination tables with the expected columns and manage schema changes manually. ## Related Documentation * [Ingestion Modes](/insertsupserts) - Understand insert vs. upsert ingestion modes * [Delete Handling](/insertsupserts#delete-handling) - Configure how delete events are processed at the destination # Sources Source: https://docs.streamkap.com/sources Sources refer to data sources we will receive data from. On this page you can create new connectors or view the status of existing ones. **Functionality** * Create * View existing Sources & Quick Actions * Create & Edit Authentication * Settings * Schema * Status * Config History ## View Existing Sources * Shows types, quantity of topics latency * Able to search and filter the list ### Row Actions Menu Click the actions menu (⋮) on any source row to access: * **View**: Open the source detail page * **Copy ID**: Copy the source UUID to clipboard (useful for API calls and support tickets) * **Snapshot**: Trigger a new data snapshot for this source * **Cancel Snapshot**: Stop an in-progress snapshot operation * **Clone**: Create a copy of this source with the same configuration * **Logs**: View logs for this source connector * **Restart**: Stop and resume the connector without clearing offsets (useful for recovering from a stuck or errored state) * **Reset**: Clear all offsets and restart streaming from the current point in time **Resetting** Resetting a Source will clear all offsets and restart the streaming of data from this point in time onward. Because of that, you will likely need to trigger a new snapshot to ensure data consistency. This is a destructive action and should be used with caution. You will be prompted to confirm this action. * **Delete**: Remove this source connector (requires confirmation) Row actions menu showing View, Copy ID, Snapshot, Cancel Snapshot, Clone, Logs, Restart, Reset, and Delete options ## Bulk Actions Select multiple sources using the checkboxes on the left side of the table to perform actions on multiple sources at once. When sources are selected, the Bulk Actions menu becomes available. Available bulk actions: * **Snapshot**: Trigger a new snapshot for all selected sources * **Cancel Snapshot**: Cancel ongoing snapshots for all selected sources * **Stop**: Stop all selected sources from streaming data * **Resume**: Resume streaming for all selected stopped sources * **Reset**: Clear offsets and restart streaming for all selected sources (requires confirmation) * **Delete**: Remove all selected sources (requires confirmation) You can select individual sources or use "Select all X matching source" to select all sources in the current filtered view. Bulk actions are applied to all selected sources simultaneously. Use caution with destructive actions like Reset and Delete. ## Create & Edit Authentication ## Settings Varies per connector but you will find values to configure such as how to carry out snapshots and how fast to consume data. ## Schema * The adding and editing of schema to consume * Streamkap can consume multiple databases and schemas per instance * You can bulk upload the list of databases/tables. Format is a simple list of a database or table per row without header * Coming Soon - Data discovery, so that you can browse and select from the source ## Status * View headline statistics such as Latency * View volume, events retries and snapshot status per table/topic * Trigger a new snapshot/backfill per connecor or table/topics ## Config History The Config History tab provides a complete timeline of all configuration changes made to your connector. This feature helps you track changes over time, understand their impact, and troubleshoot issues. **What's Tracked:** * Kafka Connect configuration changes that affect data flow * Changes made during connector creation and edits * Both successful and failed configuration updates * Note: Metadata updates (name, tags, description) are not included **Key Features:** * **Timeline View** - Chronological list of all configuration changes with timestamps * **Status Indicators** - Visual indicators showing successful (green) and failed (red) updates * **AI-Generated Summaries** - Streamkap AI analyzes each change and provides: * A clear explanation of what was modified * Potential impact on your data pipeline * Relevant warnings about performance or data flow changes * **Sync Delay Notice** - Recent changes may take a few minutes to appear as the system syncs **Example Use Cases:** * Investigate when a configuration change caused an issue * Review the history of schema or table inclusion changes * Understand how settings like snapshot parallelism or table lists have evolved * Document configuration changes for compliance or audit purposes ### Topic Actions Menu Click the actions menu (⋮) on any topic row in the Status tab to access: * **Snapshot**: Trigger a new data snapshot for this specific topic # Overview Source: https://docs.streamkap.com/sql-server Streamkap supports syncing SQL Server in real-time using change data capture for: If you don't see your platform listed, please contact us as we're happy to help find and provide the setup documentation you need. # SQL Server (Generic) Source: https://docs.streamkap.com/sql-server-generic SQL Server Change Data Capture Self Hosted or Generic with Streamkap ## Prerequisites * SQL Server version ≥ 2016 Service Pack 1 Standard or Enterprise Edition * SQL Server Agent is configured and running * A database user with sufficient privileges to configure the database, including enabling CDC, CT and creating users ## SQL Server Setup ### 1. Grant Database Access * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your database. ### 2. Enable Change Data Capture Before you can use CDC with your SQL Server instance, you must enable it on the database. ```SQL SQL theme={null} -- Replace { ... } placeholders as required USE [{database}]; EXEC sys.sp_cdc_enable_db; ``` Microsoft recommends keeping CDC data files separate from your primary database files. ```SQL SQL theme={null} -- Replace { ... } placeholders as required ALTER DATABASE [{database}] ADD FILEGROUP Streamkap_ChangeTracking; ALTER DATABASE [{database}] ADD FILE ( NAME = Streamkap_ChangeTracking_Data, FILENAME = N'{path}\Streamkap_ChangeTracking_Data.ndf' ) TO FILEGROUP Streamkap_ChangeTracking; GO ``` After CDC is enabled on the database, for every table you want Streamkap to capture, enable change tracking on that using the script below: ```SQL SQL theme={null} -- Replace { ... } placeholders as required EXEC sys.sp_cdc_enable_table @source_schema = N'{schema}', @source_name = N'{table}', @role_name = N'streamkap_role', @filegroup_name = N'Streamkap_ChangeTracking', @supports_net_changes = 0 GO ``` ### 3. Create Database User It's recommended to create a separate user and role for Streamkap to access your SQL Server database. Below is an example script that does that. ```SQL SQL theme={null} -- Replace {database}, {schema} and {password} placeholders as required USE {database}; GO CREATE LOGIN streamkap_user WITH PASSWORD = '{password}'; CREATE USER streamkap_user FOR LOGIN streamkap_user; CREATE ROLE streamkap_role; ALTER ROLE streamkap_role ADD MEMBER streamkap_user; GRANT SELECT ON SCHEMA::{schema} TO streamkap_role; GRANT SELECT ON SCHEMA::cdc TO streamkap_role; USE master; GO GRANT VIEW SERVER STATE TO streamkap_user; -- Used for monitoring the SQL Server Agent service state GO ``` ### 4. Enable Snapshots To backfill your data, the Connector needs to be able to perform **snapshots** (See [Snapshots & Backfilling](/snapshots) for more information). To enable this process, a table must be created for the Connector to use. The examples below use `streamkap_signal` as the signal table name, but you can choose any name. During [Streamkap Setup](#3-snapshot-settings), provide the full path to your signal table in `schema.table` format (e.g., `streamkap.streamkap_signal`). ```SQL SQL theme={null} CREATE SCHEMA streamkap; -- Create the table within the schema CREATE TABLE streamkap.streamkap_signal ( id VARCHAR(255) PRIMARY KEY, type VARCHAR(32) NOT NULL, data VARCHAR(2000) NULL ); -- Enable change tracking on the signal table EXEC sys.sp_cdc_enable_table @source_schema = N'streamkap', @source_name = N'streamkap_signal', @role_name = N'streamkap_role', @filegroup_name = N'Streamkap_ChangeTracking', @supports_net_changes = 0 GO -- Grant permission on the signal table to the Streamkap role GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_signal TO streamkap_role; ``` ### 5. Heartbeats Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. For read-write connections (when **Read only** is **No** during Streamkap Setup), the Connector writes to the heartbeat table directly. ```SQL SQL theme={null} -- Create the heartbeat table with id, text, and last_update fields CREATE TABLE streamkap.streamkap_heartbeat ( id INT IDENTITY(1,1) PRIMARY KEY, text NVARCHAR(MAX), last_update DATETIME2 DEFAULT SYSUTCDATETIME() ); -- Enable change tracking on the heartbeat table EXEC sys.sp_cdc_enable_table @source_schema = N'streamkap', @source_name = N'streamkap_heartbeat', @role_name = N'streamkap_role', @filegroup_name = N'Streamkap_ChangeTracking', -- Not applicable for Azure SQL Databases @supports_net_changes = 0 GO -- Grant permission on the heartbeat table to the Streamkap role GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO streamkap_role; -- Insert the first row into the heartbeat table INSERT INTO streamkap.streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **SQL Server**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Endpoint**: Specify the endpoint of the SQL Server database. * **Port**: Default is `1433`. * **Connect via SSH Tunnel**: The Connector will connect to an SSH server in your network which has access to your database. This is necessary if the Connector cannot connect directly to your database. * See [SSH Tunnel](/ssh-tunnel) for setup instructions. * **Username**: Username to access the database. By default, Streamkap scripts use `streamkap_user`. * **Password**: Password to access the database. * **Source Database**: Specify the database to stream data from. * **Heartbeats**: * **Heartbeat Table Schema**: Streamkap will use a table in this schema to manage heartbeats. Usually this is the same as the Signal Table. See [Heartbeats](#5-heartbeats) for setup instructions. ### 3. Snapshot Settings * **Signal Table**: Full path to the signal table including schema and table name (e.g., `streamkap.streamkap_signal`). This table is used for incremental snapshotting. See [Enable Snapshots](#4-enable-snapshots) for setup instructions. ### 4. Advanced Parameters * **Represent binary data as**: Specifies how the data for binary columns e.g. `blob`, `binary`, `varbinary` should be interpreted. Your destination for this data can impact which option you choose. Default is `bytes`. * **Capture Only Captured Databases DDL** (Default `false`) - Specifies whether the connector records schema structures from all databases defined in the server (the default) or only those databases for which you've explicitly configured the connector. Specify `true` to capture schema history only for the specific databases you've configured. This is particularly valuable when databases are large, to reduce the volume of DDL stored in the schema history topic. It also improves startup times when the connector restarts or recovers from failures. See [Schema History Optimization](/schema-history-optimization) for details. * **Capture Only Captured Tables DDL** (Default `false`) - Specifies whether the connector records the schema structure for all tables in the configured databases (the default) or only the tables whose changes the connector captures. Specify `true` to capture schema history only for the specific tables you've configured. This is particularly valuable when tables are large, to reduce the volume of DDL statements stored in the schema history topic. It also improves startup times when the connector restarts or recovers from failures. See [Schema History Optimization](/schema-history-optimization) for details. Click **Next**. ### 5. Schema and Table Capture * **Add Schemas/Tables**: Specify the schema(s) and table(s) for capture * You can bulk upload here. The format is a simple list of schemas and tables, with each entry on a new row. Save as a `.csv` file without a header. **CDC only captures base tables, not Views** Change Data Capture reads SQL Server CDC change tables, which only track changes to physical base tables. Database Views are query-time computations with no physical storage—they don't have CDC change tables. **What you cannot capture:** Views, indexed views (materialized views), table variables, temporary tables, or system tables. **Memory-optimized tables**: While technically possible with special configuration (SQL Server 2016 SP2+, compatibility level 130+), CDC on memory-optimized tables introduces severe performance penalties that negate In-Memory OLTP benefits. Not recommended—use disk-based tables for CDC instead. **Solution:** Specify only the underlying base tables that feed your views. You can recreate the view logic in your destination or transformation layer. Click **Save**. ## Troubleshooting When CDC is enabled for a table and changes occur to it, change events are persisted to a 'change table' on the SQL Server database server. If you introduce a change in the structure of the source table, for example, by adding a new column, that change is **not** reflected in the change table. For as long as the change table continues to use the outdated table structure, the Streamkap SQL Server Source is unable to capture change events for the table correctly. You **must** intervene to refresh the change table structure because of the way that CDC is implemented in SQL Server. ### Refresh change table structure (Online) **Online refresh limitation** In the interval between the source table structure changing in the source database, and \_before \_the change table structure is refreshed, change events continue to be captured with the **outdated** table structure. For example, if you added a new column to a source table, change events that are captured before the change table is refreshed will not contain the new column. If this cannot be tolerated, an **Offline** refresh has to be performed. However, that means downtime for whatever system made structural changes to your tables **and** your Streamkap pipelines. Please contact us for assistance if an Offline refresh is required. The procedure for completing an **Online** refresh is simpler than running it Offline, and you can complete it without any downtime to your systems and Streamkap pipelines. For every source table that has changed, copy paste the script below into SQL Server Management Studio, change placeholders as required, and then run all queries. If you're not sure what `{refresh_table}` name to use, use `{schema}_{table}_v{N}`. For example, if the source table is `sales.orders` then you'd use `sales_orders_v2` ```SQL SQL theme={null} -- Replace {database}, {schema} and {table} placeholders -- {database}: name of the CDC enabled database -- {schema}: name of the schema with tables to refresh -- {table}: name of the table to refresh -- {refresh_table}: a unique name for the refreshed change table USE {database}; GO EXEC sys.sp_cdc_enable_table @source_schema = N'{schema}', @source_name = N'{table}', @role_name = N'streamkap_role', @filegroup_name = N'Streamkap_ChangeTracking', @supports_net_changes = 0, @capture_instance = N'{refresh_table}' GO ``` ### Refresh table limitation There cannot be more than 2 change tables for every source table. After refreshing a change table using the above script, confirm with Streamkap Support that your SQL Server Source has started streaming from the refreshed change table. Once confirmed, disable CDC on the outdated change table. Use the script below to do that. ```sql SQL theme={null} -- Replace {database}, {schema}, {table} and {refresh_table} placeholders -- {database}: name of the CDC enabled database -- {schema}: name of the schema with the table refreshed earlier -- {table}: name of the table refreshed earlier -- {refresh_table}: name of the previous refresh table, usually {schema}_{table} USE {database}; GO EXEC sys.sp_cdc_disable_table @source_schema = N'{schema}', @source_name = N'{table}', @capture_instance = N'{refresh_table}' GO ``` If you're getting this error message when refreshing the change table structure, it's because there cannot be more than 2 change tables for every source table. To fix the problem, 1 of the 2 change tables for the source table need to be disabled. The scripts below can help you do that. ```SQL SQL theme={null} -- Replace {database}, {schema} and {table} placeholders -- {database}: name of the CDC enabled database -- {schema}: name of the schema with the table refreshed earlier -- {table}: name of the table refreshed earlier USE {database}; GO EXEC sys.sp_cdc_help_change_data_capture @source_schema = N'{schema}', @source_name = N'{table}' GO ``` The above script should return 2 results, the 2 change tables for the `{table}` specified. Typically you would disable the oldest change table, so use the `create_date` column to identify the oldest one. When you've identified the change table to disable, use its`source_schema`, `source_table` and `capture_instance` names in the query below and execute. Then, try your refresh table script again. ```SQL SQL theme={null} -- Replace {database}, {schema}, {table} and {refresh_table} placeholders -- {database}: name of the CDC enabled database USE {database}; GO EXEC sys.sp_cdc_disable_table @source_schema = N'{source_schema}', @source_name = N'{source_table}', @capture_instance = N'{capture_instance}' GO ``` There can be a number of reasons. The most common are misconfiguration of CDC and privilege grants. To diagnose, run the following script: ```SQL SQL theme={null} -- Replace {database} placeholder -- {database}: name of the CDC enabled database USE {database}; GO EXEC sys.sp_cdc_help_change_data_capture GO ``` If the query returns an **error** or **no results** check: * you are connected to the SQL Server database with a user that has `DB_OWNER` role privileges. * if the `EXEC sys.sp_cdc_help_change_data_capture` stored procedure returns **no results**, the source tables you want Streamkap to capture may not be CDC enabled. If the query returns **results**, check: * the table(s) captured by the Connector are listed. * there are `capture_instance`s for the capture tables. * the database user or role used by the Connector are members of the `rolename` for the capture table(s). If you are still having issues after following the above steps, please don't hesitate to reach out to us. There can be many reasons for the Setup scripts to fail, but the scripts below can help you diagnose the issues. ```SQL SQL theme={null} -- Replace {database} placeholder -- {database}: name of the CDC enabled database USE {database}; GO SELECT name, database_id, source_database_id, compatibility_level, is_read_only, state, state_desc, is_in_standby, is_cleanly_shutdown, is_cdc_enabled, is_encrypted, replica_id FROM sys.databases WHERE name = '{database}' AND is_cdc_enabled=1; EXEC sys.sp_cdc_help_change_data_capture GO ``` If any of the queries return an **error** or **no results**: * Check you connected to the SQL Server database with a user that has `DB_OWNER` role privileges * If the `SELECT ... FROM sys.databases ...` returns **no results**, the database you want Streamkap to capture may not be CDC enabled * If the `EXEC sys.sp_cdc_help_change_data_capture` stored procedure returns **no results**, the source tables you want Streamkap to capture may not be CDC enabled If you are still having issues after following the above steps, please don't hesitate to reach out to us. If the SQL Server Agent isn't running the Connector won't be able to stream any data and an equivalent warning or error will be shown in the app. You can start the agent using SQL Server Management Studio. * In Object Explorer, select the plus sign to expand the server where you want to manage SQL Server Agent Service. * Right-click SQL Server Agent, and then select **Start**. * In the User Account Control dialog box, select **Yes**. * When prompted if you want to perform the action, select **Yes**. # Azure SQL Database Source: https://docs.streamkap.com/sql-server-on-azure-sql-database Azure SQL Database Change Data Capture with Streamkap ## Prerequisites * A database user granted the `db_owner` role (required to enable change data capture for Azure SQL Database) to configure the database, including enabling CDC, CT and creating users * An Azure SQL Database on any service tier within the vCore-based purchasing model, for both single databases and elastic pools. For databases in the DTU purchasing model, CDC is supported for databases in the S3 tier or higher. Subcore tiers (Basic, S0, S1, S2) aren't supported for CDC ## Azure SQL Database Setup ### 1. Grant Database Access * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your database. ### 2. Enable Change Data Capture Before you can create a capture instance for individual tables, you must enable CDC for your Azure SQL Database. To enable CDC, connect to your Azure SQL Database through Azure Data Studio or SQL Server Management Studio (SSMS). Open a new query window, then enable CDC by running the following T-SQL: ```SQL SQL theme={null} EXEC sys.sp_cdc_enable_db; GO ``` After CDC is enabled on the database, for every table you want Streamkap to capture, enable change tracking on that using the script below: ```SQL SQL theme={null} -- Replace { ... } placeholders as required EXEC sys.sp_cdc_enable_table @source_schema = N'{schema}', @source_name = N'{table}', @role_name = N'streamkap_role', @supports_net_changes = 0 GO ``` ### 3. Create Database User It's recommended to create a separate user and role for Streamkap to access your SQL Server database. Below is an example script that does that. ```SQL SQL theme={null} -- Replace {database}, {schema} and {password} placeholders as required USE {database}; GO CREATE LOGIN streamkap_user WITH PASSWORD = '{password}'; CREATE USER streamkap_user FOR LOGIN streamkap_user; CREATE ROLE streamkap_role; ALTER ROLE streamkap_role ADD MEMBER streamkap_user; GRANT SELECT ON SCHEMA::{schema} TO streamkap_role; GRANT SELECT ON SCHEMA::cdc TO streamkap_role; GO USE master; GO GRANT VIEW SERVER STATE TO streamkap_user; -- Used for monitoring the SQL Server Agent service state GO ``` ### 4. Enable Snapshots To backfill your data, the Connector needs to be able to perform **snapshots** (See [Snapshots & Backfilling](/snapshots) for more information). To enable this process, a table must be created for the Connector to use. The examples below use `streamkap_signal` as the signal table name, but you can choose any name. During [Streamkap Setup](#3-snapshot-settings), provide the full path to your signal table in `schema.table` format (e.g., `streamkap.streamkap_signal`). ```SQL SQL theme={null} CREATE SCHEMA streamkap; -- Create the table within the schema CREATE TABLE streamkap.streamkap_signal ( id VARCHAR(255) PRIMARY KEY, type VARCHAR(32) NOT NULL, data VARCHAR(2000) NULL ); -- Enable change tracking on the signal table EXEC sys.sp_cdc_enable_table @source_schema = N'streamkap', @source_name = N'streamkap_signal', @role_name = N'streamkap_role', @filegroup_name = N'Streamkap_ChangeTracking', @supports_net_changes = 0 GO -- Grant permission on the signal table to the Streamkap role GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_signal TO streamkap_role; ``` ### 5. Heartbeats Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. For read-write connections (when **Read only** is **No** during Streamkap Setup), the Connector writes to the heartbeat table directly. ```SQL SQL theme={null} -- Create the heartbeat table with id, text, and last_update fields CREATE TABLE streamkap.streamkap_heartbeat ( id INT IDENTITY(1,1) PRIMARY KEY, text NVARCHAR(MAX), last_update DATETIME2 DEFAULT SYSUTCDATETIME() ); -- Enable change tracking on the heartbeat table EXEC sys.sp_cdc_enable_table @source_schema = N'streamkap', @source_name = N'streamkap_heartbeat', @role_name = N'streamkap_role', @filegroup_name = N'Streamkap_ChangeTracking', -- Not applicable for Azure SQL Databases @supports_net_changes = 0 GO -- Grant permission on the heartbeat table to the Streamkap role GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO streamkap_role; -- Insert the first row into the heartbeat table INSERT INTO streamkap.streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **SQL Server**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Endpoint**: Specify the endpoint of the SQL Server database. * **Port**: Default is `1433`. * **Connect via SSH Tunnel**: The Connector will connect to an SSH server in your network which has access to your database. This is necessary if the Connector cannot connect directly to your database. * See [SSH Tunnel](/ssh-tunnel) for setup instructions. * **Username**: Username to access the database. By default, Streamkap scripts use `streamkap_user`. * **Password**: Password to access the database. * **Source Database**: Specify the database to stream data from. * **Heartbeats**: * **Heartbeat Table Schema**: Streamkap will use a table in this schema to manage heartbeats. Usually this is the same as the Signal Table. See [Heartbeats](#5-heartbeats) for setup instructions. ### 3. Snapshot Settings * **Signal Table**: Full path to the signal table including schema and table name (e.g., `streamkap.streamkap_signal`). This table is used for incremental snapshotting. See [Enable Snapshots](#4-enable-snapshots) for setup instructions. ### 4. Advanced Parameters * **Represent binary data as**: Specifies how the data for binary columns e.g. `blob`, `binary`, `varbinary` should be interpreted. Your destination for this data can impact which option you choose. Default is `bytes`. * **Capture Only Captured Databases DDL** (Default `false`) - Specifies whether the connector records schema structures from all databases defined in the server (the default) or only those databases for which you've explicitly configured the connector. Specify `true` to capture schema history only for the specific databases you've configured. This is particularly valuable when databases are large, to reduce the volume of DDL stored in the schema history topic. It also improves startup times when the connector restarts or recovers from failures. See [Schema History Optimization](/schema-history-optimization) for details. * **Capture Only Captured Tables DDL** (Default `false`) - Specifies whether the connector records the schema structure for all tables in the configured databases (the default) or only the tables whose changes the connector captures. Specify `true` to capture schema history only for the specific tables you've configured. This is particularly valuable when tables are large, to reduce the volume of DDL statements stored in the schema history topic. It also improves startup times when the connector restarts or recovers from failures. See [Schema History Optimization](/schema-history-optimization) for details. Click **Next**. ### 5. Schema and Table Capture * **Add Schemas/Tables**: Specify the schema(s) and table(s) for capture * You can bulk upload here. The format is a simple list of schemas and tables, with each entry on a new row. Save as a `.csv` file without a header. Click **Save**. ## Troubleshooting When CDC is enabled for a table and changes occur to it, change events are persisted to a 'change table' on the SQL Server database server. If you introduce a change in the structure of the source table, for example, by adding a new column, that change is **not** reflected in the change table. For as long as the change table continues to use the outdated table structure, the Streamkap SQL Server Source is unable to capture change events for the table correctly. You **must** intervene to refresh the change table structure because of the way that CDC is implemented in SQL Server. ### Refresh change table structure (Online) **Online refresh limitation** In the interval between the source table structure changing in the source database, and \_before \_the change table structure is refreshed, change events continue to be captured with the **outdated** table structure. For example, if you added a new column to a source table, change events that are captured before the change table is refreshed will not contain the new column. If this cannot be tolerated, an **Offline** refresh has to be performed. However, that means downtime for whatever system made structural changes to your tables **and** your Streamkap pipelines. Please contact us for assistance if an Offline refresh is required. The procedure for completing an **Online** refresh is simpler than running it Offline, and you can complete it without any downtime to your systems and Streamkap pipelines. For every source table that has changed, copy paste the script below into SQL Server Management Studio, change placeholders as required, and then run all queries. If you're not sure what `{refresh_table}` name to use, use `{schema}_{table}_v{N}`. For example, if the source table is `sales.orders` then you'd use `sales_orders_v2` ```SQL SQL theme={null} -- Replace {database}, {schema} and {table} placeholders -- {database}: name of the CDC enabled database -- {schema}: name of the schema with tables to refresh -- {table}: name of the table to refresh -- {refresh_table}: a unique name for the refreshed change table USE {database}; GO EXEC sys.sp_cdc_enable_table @source_schema = N'{schema}', @source_name = N'{table}', @role_name = N'streamkap_role', @supports_net_changes = 0, @capture_instance = N'{refresh_table}' GO ``` ### Refresh table limitation There cannot be more than 2 change tables for every source table. After refreshing a change table using the above script, confirm with Streamkap Support that your SQL Server Source has started streaming from the refreshed change table. Once confirmed, disable CDC on the outdated change table. Use the script below to do that. ```sql SQL theme={null} -- Replace {database}, {schema}, {table} and {refresh_table} placeholders -- {database}: name of the CDC enabled database -- {schema}: name of the schema with the table refreshed earlier -- {table}: name of the table refreshed earlier -- {refresh_table}: name of the previous refresh table, usually {schema}_{table} USE {database}; GO EXEC sys.sp_cdc_disable_table @source_schema = N'{schema}', @source_name = N'{table}', @capture_instance = N'{refresh_table}' GO ``` If you're getting this error message when refreshing the change table structure, it's because there cannot be more than 2 change tables for every source table. To fix the problem, 1 of the 2 change tables for the source table need to be disabled. The scripts below can help you do that. ```SQL SQL theme={null} -- Replace {database}, {schema} and {table} placeholders -- {database}: name of the CDC enabled database -- {schema}: name of the schema with the table refreshed earlier -- {table}: name of the table refreshed earlier USE {database}; GO EXEC sys.sp_cdc_help_change_data_capture @source_schema = N'{schema}', @source_name = N'{table}' GO ``` The above script should return 2 results, the 2 change tables for the `{table}` specified. Typically you would disable the oldest change table, so use the `create_date` column to identify the oldest one. When you've identified the change table to disable, use its`source_schema`, `source_table` and `capture_instance` names in the query below and execute. Then, try your refresh table script again. ```SQL SQL theme={null} -- Replace {database}, {schema}, {table} and {refresh_table} placeholders -- {database}: name of the CDC enabled database USE {database}; GO EXEC sys.sp_cdc_disable_table @source_schema = N'{source_schema}', @source_name = N'{source_table}', @capture_instance = N'{capture_instance}' GO ``` There can be a number of reasons. The most common are misconfiguration of CDC and privilege grants. To diagnose, run the following script: ```SQL SQL theme={null} -- Replace {database} placeholder -- {database}: name of the CDC enabled database USE {database}; GO EXEC sys.sp_cdc_help_change_data_capture GO ``` If the query returns an **error** or **no results** check: * you are connected to the SQL Server database with a user that has `DB_OWNER` role privileges. * if the `EXEC sys.sp_cdc_help_change_data_capture` stored procedure returns **no results**, the source tables you want Streamkap to capture may not be CDC enabled. If the query returns **results**, check: * the table(s) captured by the Connector are listed. * there are `capture_instance`s for the capture tables. * the database user or role used by the Connector are members of the `rolename` for the capture table(s). If you are still having issues after following the above steps, please don't hesitate to reach out to us. There can be many reasons for the Setup scripts to fail, but the scripts below can help you diagnose the issues. ```SQL SQL theme={null} -- Replace {database} placeholder -- {database}: name of the CDC enabled database USE {database}; GO SELECT name, database_id, source_database_id, compatibility_level, is_read_only, state, state_desc, is_in_standby, is_cleanly_shutdown, is_cdc_enabled, is_encrypted, replica_id FROM sys.databases WHERE name = '{database}' AND is_cdc_enabled=1; EXEC sys.sp_cdc_help_change_data_capture GO ``` If any of the queries return an **error** or **no results**: * Check you connected to the SQL Server database with a user that has `DB_OWNER` role privileges * If the `SELECT ... FROM sys.databases ...` returns **no results**, the database you want Streamkap to capture may not be CDC enabled * If the `EXEC sys.sp_cdc_help_change_data_capture` stored procedure returns **no results**, the source tables you want Streamkap to capture may not be CDC enabled If you are still having issues after following the above steps, please don't hesitate to reach out to us. # SQL Server on Google Cloud SQL Source: https://docs.streamkap.com/sql-server-on-google-cloud-sql SQL Server Change Data Capture on Google Cloud SQL with Streamkap ## Prerequisites * SQL Server version ≥ 2016 Service Pack 1 Standard or Enterprise Edition * A database user with sufficient privileges to configure the database, including enabling CDC, CT and creating users ## SQL Server Setup ### 1. Grant Database Access * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your database. ### 2. Enable Change Data Capture Before you can use CDC with your Google Cloud SQL instance, you must enable it on the database. ```SQL SQL theme={null} -- Replace { ... } placeholders as required EXEC msdb.dbo.gcloudsql_cdc_enable_db '{database}' ``` Microsoft recommends keeping CDC data files separate from your primary database files. ```SQL SQL theme={null} -- Replace { ... } placeholders as required ALTER DATABASE [{database}] ADD FILEGROUP Streamkap_ChangeTracking; ALTER DATABASE [{database}] ADD FILE ( NAME = Streamkap_ChangeTracking_Data, FILENAME = N'{path}\Streamkap_ChangeTracking_Data.ndf' ) TO FILEGROUP Streamkap_ChangeTracking; GO ``` After CDC is enabled on the database, for every table you want Streamkap to capture, enable change tracking on that using the script below: ```SQL SQL theme={null} -- Replace { ... } placeholders as required EXEC sys.sp_cdc_enable_table @source_schema = N'{schema}', @source_name = N'{table}', @role_name = N'streamkap_role', @filegroup_name = N'Streamkap_ChangeTracking', @supports_net_changes = 0 GO ``` ### 3. Create Database User It's recommended to create a separate user and role for Streamkap to access your SQL Server database. Below is an example script that does that. ```SQL SQL theme={null} -- Replace {database}, {schema} and {password} placeholders as required USE {database}; GO CREATE LOGIN streamkap_user WITH PASSWORD = '{password}'; CREATE USER streamkap_user FOR LOGIN streamkap_user; CREATE ROLE streamkap_role; ALTER ROLE streamkap_role ADD MEMBER streamkap_user; GRANT SELECT ON SCHEMA::{schema} TO streamkap_role; GRANT SELECT ON SCHEMA::cdc TO streamkap_role; GO USE master; GO GRANT VIEW SERVER STATE TO streamkap_user; -- Used for monitoring the SQL Server Agent service state GO ``` ### 4. Enable Snapshots To backfill your data, the Connector needs to be able to perform **snapshots** (See [Snapshots & Backfilling](/snapshots) for more information). To enable this process, a table must be created for the Connector to use. The examples below use `streamkap_signal` as the signal table name, but you can choose any name. During [Streamkap Setup](#3-snapshot-settings), provide the full path to your signal table in `schema.table` format (e.g., `streamkap.streamkap_signal`). ```SQL SQL theme={null} CREATE SCHEMA streamkap; -- Create the table within the schema CREATE TABLE streamkap.streamkap_signal ( id VARCHAR(255) PRIMARY KEY, type VARCHAR(32) NOT NULL, data VARCHAR(2000) NULL ); -- Enable change tracking on the signal table EXEC sys.sp_cdc_enable_table @source_schema = N'streamkap', @source_name = N'streamkap_signal', @role_name = N'streamkap_role', @filegroup_name = N'Streamkap_ChangeTracking', @supports_net_changes = 0 GO -- Grant permission on the signal table to the Streamkap role GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_signal TO streamkap_role; ``` ### 5. Heartbeats Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. For read-write connections (when **Read only** is **No** during Streamkap Setup), the Connector writes to the heartbeat table directly. ```SQL SQL theme={null} -- Create the heartbeat table with id, text, and last_update fields CREATE TABLE streamkap.streamkap_heartbeat ( id INT IDENTITY(1,1) PRIMARY KEY, text NVARCHAR(MAX), last_update DATETIME2 DEFAULT SYSUTCDATETIME() ); -- Enable change tracking on the heartbeat table EXEC sys.sp_cdc_enable_table @source_schema = N'streamkap', @source_name = N'streamkap_heartbeat', @role_name = N'streamkap_role', @filegroup_name = N'Streamkap_ChangeTracking', -- Not applicable for Azure SQL Databases @supports_net_changes = 0 GO -- Grant permission on the heartbeat table to the Streamkap role GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO streamkap_role; -- Insert the first row into the heartbeat table INSERT INTO streamkap.streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **SQL Server**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Endpoint**: Specify the endpoint of the SQL Server database. * **Port**: Default is `1433`. * **Connect via SSH Tunnel**: The Connector will connect to an SSH server in your network which has access to your database. This is necessary if the Connector cannot connect directly to your database. * See [SSH Tunnel](/ssh-tunnel) for setup instructions. * **Username**: Username to access the database. By default, Streamkap scripts use `streamkap_user`. * **Password**: Password to access the database. * **Source Database**: Specify the database to stream data from. * **Heartbeats**: * **Heartbeat Table Schema**: Streamkap will use a table in this schema to manage heartbeats. Usually this is the same as the Signal Table. See [Heartbeats](#5-heartbeats) for setup instructions. ### 3. Snapshot Settings * **Signal Table**: Full path to the signal table including schema and table name (e.g., `streamkap.streamkap_signal`). This table is used for incremental snapshotting. See [Enable Snapshots](#4-enable-snapshots) for setup instructions. ### 4. Advanced Parameters * **Represent binary data as**: Specifies how the data for binary columns e.g. `blob`, `binary`, `varbinary` should be interpreted. Your destination for this data can impact which option you choose. Default is `bytes`. * **Capture Only Captured Databases DDL** (Default `false`) - Specifies whether the connector records schema structures from all databases defined in the server (the default) or only those databases for which you've explicitly configured the connector. Specify `true` to capture schema history only for the specific databases you've configured. This is particularly valuable when databases are large, to reduce the volume of DDL stored in the schema history topic. It also improves startup times when the connector restarts or recovers from failures. See [Schema History Optimization](/schema-history-optimization) for details. * **Capture Only Captured Tables DDL** (Default `false`) - Specifies whether the connector records the schema structure for all tables in the configured databases (the default) or only the tables whose changes the connector captures. Specify `true` to capture schema history only for the specific tables you've configured. This is particularly valuable when tables are large, to reduce the volume of DDL statements stored in the schema history topic. It also improves startup times when the connector restarts or recovers from failures. See [Schema History Optimization](/schema-history-optimization) for details. Click **Next**. ### 5. Schema and Table Capture * **Add Schemas/Tables**: Specify the schema(s) and table(s) for capture * You can bulk upload here. The format is a simple list of schemas and tables, with each entry on a new row. Save as a `.csv` file without a header. **CDC only captures base tables, not Views** Change Data Capture reads SQL Server CDC change tables, which only track changes to physical base tables. Database Views are query-time computations with no physical storage—they don't have CDC change tables. **What you cannot capture:** Views, indexed views (materialized views), table variables, temporary tables, or system tables. **Memory-optimized tables**: While technically possible with special configuration (SQL Server 2016 SP2+, compatibility level 130+), CDC on memory-optimized tables introduces severe performance penalties that negate In-Memory OLTP benefits. Not recommended—use disk-based tables for CDC instead. **Solution:** Specify only the underlying base tables that feed your views. You can recreate the view logic in your destination or transformation layer. Click **Save**. ## Troubleshooting When CDC is enabled for a table and changes occur to it, change events are persisted to a 'change table' on the SQL Server database server. If you introduce a change in the structure of the source table, for example, by adding a new column, that change is **not** reflected in the change table. For as long as the change table continues to use the outdated table structure, the Streamkap SQL Server Source is unable to capture change events for the table correctly. You **must** intervene to refresh the change table structure because of the way that CDC is implemented in SQL Server. ### Refresh change table structure (Online) **Online refresh limitation** In the interval between the source table structure changing in the source database, and \_before \_the change table structure is refreshed, change events continue to be captured with the **outdated** table structure. For example, if you added a new column to a source table, change events that are captured before the change table is refreshed will not contain the new column. If this cannot be tolerated, an **Offline** refresh has to be performed. However, that means downtime for whatever system made structural changes to your tables **and** your Streamkap pipelines. Please contact us for assistance if an Offline refresh is required. The procedure for completing an **Online** refresh is simpler than running it Offline, and you can complete it without any downtime to your systems and Streamkap pipelines. For every source table that has changed, copy paste the script below into SQL Server Management Studio, change placeholders as required, and then run all queries. If you're not sure what `{refresh_table}` name to use, use `{schema}_{table}_v{N}`. For example, if the source table is `sales.orders` then you'd use `sales_orders_v2` ```SQL SQL theme={null} -- Replace {database}, {schema} and {table} placeholders -- {database}: name of the CDC enabled database -- {schema}: name of the schema with tables to refresh -- {table}: name of the table to refresh -- {refresh_table}: a unique name for the refreshed change table USE {database}; GO EXEC sys.sp_cdc_enable_table @source_schema = N'{schema}', @source_name = N'{table}', @role_name = N'streamkap_role', @filegroup_name = N'Streamkap_ChangeTracking', @supports_net_changes = 0, @capture_instance = N'{refresh_table}' GO ``` **Refresh table limitation** There cannot be more than 2 change tables for every source table. After refreshing a change table using the above script, confirm with Streamkap Support that your SQL Server Source has started streaming from the refreshed change table. Once confirmed, disable CDC on the outdated change table. Use the script below to do that. ```sql SQL theme={null} -- Replace {database}, {schema}, {table} and {refresh_table} placeholders -- {database}: name of the CDC enabled database -- {schema}: name of the schema with the table refreshed earlier -- {table}: name of the table refreshed earlier -- {refresh_table}: name of the previous refresh table, usually {schema}_{table} USE {database}; GO EXEC sys.sp_cdc_disable_table @source_schema = N'{schema}', @source_name = N'{table}', @capture_instance = N'{refresh_table}' GO ``` If you're getting this error message when refreshing the change table structure, it's because there cannot be more than 2 change tables for every source table. To fix the problem, 1 of the 2 change tables for the source table need to be disabled. The scripts below can help you do that. ```SQL SQL theme={null} -- Replace {database}, {schema} and {table} placeholders -- {database}: name of the CDC enabled database -- {schema}: name of the schema with the table refreshed earlier -- {table}: name of the table refreshed earlier USE {database}; GO EXEC sys.sp_cdc_help_change_data_capture @source_schema = N'{schema}', @source_name = N'{table}' GO ``` The above script should return 2 results, the 2 change tables for the `{table}` specified. Typically you would disable the oldest change table, so use the `create_date` column to identify the oldest one. When you've identified the change table to disable, use its`source_schema`, `source_table` and `capture_instance` names in the query below and execute. Then, try your refresh table script again. ```SQL SQL theme={null} -- Replace {database}, {schema}, {table} and {refresh_table} placeholders -- {database}: name of the CDC enabled database USE {database}; GO EXEC sys.sp_cdc_disable_table @source_schema = N'{source_schema}', @source_name = N'{source_table}', @capture_instance = N'{capture_instance}' GO ``` There can be a number of reasons. The most common are misconfiguration of CDC and privilege grants. To diagnose, run the following script: ```SQL SQL theme={null} -- Replace {database} placeholder -- {database}: name of the CDC enabled database USE {database}; GO EXEC sys.sp_cdc_help_change_data_capture GO ``` If the query returns an **error** or **no results** check: * you are connected to the SQL Server database with a user that has `DB_OWNER` role privileges. * if the `EXEC sys.sp_cdc_help_change_data_capture` stored procedure returns **no results**, the source tables you want Streamkap to capture may not be CDC enabled. If the query returns **results**, check: * the table(s) captured by the Connector are listed. * there are `capture_instance`s for the capture tables. * the database user or role used by the Connector are members of the `rolename` for the capture table(s). If you are still having issues after following the above steps, please don't hesitate to reach out to us. There can be many reasons for the Setup scripts to fail, but the scripts below can help you diagnose the issues. ```SQL SQL theme={null} -- Replace {database} placeholder -- {database}: name of the CDC enabled database USE {database}; GO SELECT name, database_id, source_database_id, compatibility_level, is_read_only, state, state_desc, is_in_standby, is_cleanly_shutdown, is_cdc_enabled, is_encrypted, replica_id FROM sys.databases WHERE name = '{database}' AND is_cdc_enabled=1; EXEC sys.sp_cdc_help_change_data_capture GO ``` If any of the queries return an **error** or **no results**: * Check you connected to the SQL Server database with a user that has `DB_OWNER` role privileges * If the `SELECT ... FROM sys.databases ...` returns **no results**, the database you want Streamkap to capture may not be CDC enabled * If the `EXEC sys.sp_cdc_help_change_data_capture` stored procedure returns **no results**, the source tables you want Streamkap to capture may not be CDC enabled If you are still having issues after following the above steps, please don't hesitate to reach out to us. # SQL Server Source FAQ Source: https://docs.streamkap.com/sql-server-source-faq ## SQL Server Sources FAQ for Streamkap This FAQ focuses on using SQL Server as a source in Streamkap, including self-hosted setups and cloud variants (AWS RDS SQL Server, Azure SQL Database, Google Cloud SQL for SQL Server). Streamkap's SQL Server connector provides real-time CDC with managed features like automatic scaling, UI setup, and ETL transformations. A SQL Server source in Streamkap enables real-time Change Data Capture (CDC) from SQL Server databases, capturing row-level inserts, updates, and deletes with sub-second latency. It reads CDC change tables to stream changes to destinations, supporting snapshots for initial loads, schema evolution, and heartbeats for low-traffic databases. Streamkap abstracts complexity, offering a serverless setup via UI or API. * SQL Server 2016+ for basic CDC; 2017+ for enhanced features and Linux support; 2019+ for advanced data types and performance. * Compatible with SQL Server 2014+ in limited modes. * Azure SQL Database (all service tiers with CDC support) Streamkap supports: * Self-hosted (on-prem/VM) * AWS RDS SQL Server (Standard, Enterprise, Web editions) * Azure SQL Database (including Managed Instance) * Google Cloud SQL for SQL Server Streamkap also supports standalone instances, Always On Availability Groups, and failover cluster instances. * **CDC**: Change table-based via SQL Server CDC; captures inserts/updates/deletes * **Snapshots**: Ad-hoc/initial backfills using signal table methods; locking or non-locking modes * **Schema Evolution**: Requires manual refresh of CDC change tables when source table structure changes * **Heartbeats**: Enabled by default to prevent offset staleness in low-traffic databases * **Data Types**: Standard mappings (numerics, temporals, strings, binary as bytes/base64/hex, XML, hierarchyid) * **Ingestion Modes**: Inserts (append) or upserts * **Security**: SSL, Windows/SQL authentication, IP allowlisting * **Monitoring**: Latency, lag, CDC cleanup metrics in-app * Streamkap adds transaction metadata and ad-hoc snapshots with filters Streamkap reads SQL Server's CDC change tables, which are automatically populated by the database engine when CDC is enabled on tables. The SQL Server Agent must be running to process CDC data. Change events are emitted as they're read from these change tables. SQL Server CDC generates change tables that grow over time. The SQL Server Agent runs cleanup jobs to purge old data based on retention settings. * **Default retention**: 3 days * **Monitor cleanup**: Ensure SQL Server Agent is running and cleanup jobs are scheduled * **Storage impact**: Change tables can consume significant disk space if cleanup fails If the SQL Server Agent is stopped or cleanup jobs fail, change tables will grow indefinitely and may cause storage issues. * Trigger ad-hoc at source/table level * Requires a signal table (`streamkap_signal`) with CDC enabled * Methods: Blocking (locks tables briefly) or incremental (chunked reads) * Modes: `initial` (default), `always`, `initial_only`, `no_data`, `when_needed`, `configuration_based`, `custom` Streamkap simplifies triggering via UI. Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. For read-write connections (when **Read only** is **No** during Streamkap Setup), the Connector writes to the heartbeat table directly. ```SQL SQL theme={null} -- Create the heartbeat table with id, text, and last_update fields CREATE TABLE streamkap.streamkap_heartbeat ( id INT IDENTITY(1,1) PRIMARY KEY, text NVARCHAR(MAX), last_update DATETIME2 DEFAULT SYSUTCDATETIME() ); -- Enable change tracking on the heartbeat table EXEC sys.sp_cdc_enable_table @source_schema = N'streamkap', @source_name = N'streamkap_heartbeat', @role_name = N'streamkap_role', @filegroup_name = N'Streamkap_ChangeTracking', -- Not applicable for Azure SQL Databases @supports_net_changes = 0 GO -- Grant permission on the heartbeat table to the Streamkap role GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO streamkap_role; -- Insert the first row into the heartbeat table INSERT INTO streamkap.streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` * **Basics**: Integers (TINYINT, SMALLINT, INT, BIGINT), floats (REAL, FLOAT), strings (CHAR, VARCHAR, NCHAR, NVARCHAR, TEXT), dates/timestamps (DATE, DATETIME, DATETIME2, DATETIMEOFFSET, TIME) * **Advanced**: Binary (BINARY, VARBINARY, IMAGE as bytes/hex), decimals (DECIMAL, NUMERIC, MONEY), XML, uniqueidentifier (GUID) * **Custom**: Hierarchyid, spatial types (GEOMETRY, GEOGRAPHY) with limitations * **Unsupported**: Non-UTF8/UTF16 encodings, deprecated types (TEXT, NTEXT, IMAGE in older modes), sql\_variant without explicit casting When you modify a table structure (add/remove columns, change data types), the CDC change table does **not** automatically update. **You must manually refresh the change table:** 1. Create a new capture instance using `sys.sp_cdc_enable_table` with a new `@capture_instance` name 2. Wait for Streamkap to switch to the new capture instance 3. Disable the old capture instance using `sys.sp_cdc_disable_table` **Limitation**: Maximum 2 capture instances per table. See setup docs for detailed scripts. * Check SQL Server Agent status: `EXEC master.dbo.xp_servicecontrol N'QUERYSTATE', N'SQLSERVERAGENT'` * Monitor change table sizes: `SELECT * FROM sys.dm_cdc_log_scan_sessions` * Check cleanup job status: `SELECT * FROM msdb.dbo.cdc_jobs` * Monitor lag via Streamkap in-app metrics **Best Practices**: Ensure SQL Server Agent is always running; alert on CDC cleanup failures; monitor disk space for change tables **No, CDC cannot capture Views or most virtual database objects.** **Why Views cannot be captured:**\ CDC captures changes by reading the database transaction log (binlog, WAL, oplog, redo log, etc.). Views are query-time computations over base tables—they don't store data or generate transaction log entries. When you query a view, the database engine executes the underlying SELECT statement against the base tables. Since views don't store data, they don't generate transaction log entries. **What cannot be captured:** * **Views**: Virtual tables with no physical storage or change tables * **Indexed Views (Materialized Views)**: While they have physical storage, they don't generate CDC events. Capture the base tables instead. * **Table Variables**: Session-scoped, not logged in transactions * **Temporary Tables (#temp, ##temp)**: Session or connection-scoped, excluded from CDC * **Memory-Optimized Tables** ([In-Memory OLTP](https://learn.microsoft.com/en-us/sql/relational-databases/in-memory-oltp/in-memory-oltp-in-memory-optimization)): Extremely limited CDC support. While technically possible with SQL Server 2016 SP2+ (compatibility level 130+), memory-optimized tables use In-Memory OLTP engine which bypasses traditional transaction logging. CDC can be enabled but with significant performance penalties that negate the benefits of memory optimization. **Not recommended for CDC; capture from traditional disk-based tables instead or use alternative change tracking approaches.** * **System Tables** (sys.\*, information\_schema): Metadata representations, not user data * **CTEs (Common Table Expressions)**: Query-time constructs with no persistent storage * **External Tables**: Reference external data sources (PolyBase), not local storage **Solution:**\ Configure CDC on the underlying base tables that power your views. The view logic can be recreated in your destination or transformation layer. **SQL Server-specific notes:** * **Indexed Views**: Even though they store data physically, changes to indexed views are captured through the base table CDC, not the view itself * **Partitioned Views**: Capture each underlying base table separately * **Memory-Optimized Tables** ([In-Memory OLTP](https://learn.microsoft.com/en-us/sql/relational-databases/in-memory-oltp/in-memory-oltp-in-memory-optimization)): While CDC can technically be enabled on memory-optimized tables in SQL Server 2016 SP2+ with database compatibility level 130+, doing so introduces significant performance overhead. Memory-optimized tables use In-Memory OLTP engine designed for lock-free, log-optimized operations. Enabling CDC forces traditional transaction logging which defeats the performance benefits. **Recommendation**: Use traditional disk-based tables for CDC capture, or consider [Change Tracking](https://learn.microsoft.com/en-us/sql/relational-databases/track-changes/about-change-tracking-sql-server) (lighter weight alternative) or [Temporal Tables](https://learn.microsoft.com/en-us/sql/relational-databases/tables/temporal-tables) for memory-optimized scenarios. **Example:**\ If you have a view `vw_sales_summary` that queries tables `Orders` and `Customers`, enable CDC on the `Orders` and `Customers` tables, then recreate the view logic downstream. * SQL Server Agent must be running for CDC to function * Maximum 2 capture instances per table (impacts schema evolution) * Change table cleanup requires active SQL Server Agent jobs * High transaction volume can cause change table growth * Schema changes require manual intervention * Read replicas may have limitations for CDC * Restores disable CDC (must re-enable after restore) Captures deletes as events. SQL Server CDC records before-images for deletes by default, providing complete record information. Encrypted connections (SSL/TLS), SQL Server or Windows authentication, role-based access, IP allowlisting. * **CDC Not Working**: Verify SQL Server Agent is running; check CDC is enabled on database and tables * **Change Table Growth**: Monitor cleanup job execution; adjust retention settings * **Missing Events**: Ensure capture instance is active; verify role membership for CDC access * **After Database Restore**: CDC is disabled; must re-enable on database and tables * **Schema Changes Not Reflected**: Must manually refresh change tables (see setup docs) When CDC is enabled for a table and changes occur to it, change events are persisted to a 'change table' on the SQL Server database server. If you introduce a change in the structure of the source table, for example, by adding a new column, that change is **not** reflected in the change table. For as long as the change table continues to use the outdated table structure, the Streamkap SQL Server Source is unable to capture change events for the table correctly. You **must** intervene to refresh the change table structure because of the way that CDC is implemented in SQL Server. ### Refresh change table structure (Online) **Online refresh limitation** In the interval between the source table structure changing in the source database, and before the change table structure is refreshed, change events continue to be captured with the **outdated** table structure. For example, if you added a new column to a source table, change events that are captured before the change table is refreshed will not contain the new column. If this cannot be tolerated, an **Offline** refresh has to be performed. However, that means downtime for whatever system made structural changes to your tables **and** your Streamkap pipelines. Please contact us for assistance if an Offline refresh is required. The procedure for completing an **Online** refresh is simpler than running it Offline, and you can complete it without any downtime to your systems and Streamkap pipelines. For every source table that has changed, copy paste the script below into SQL Server Management Studio, change placeholders as required, and then run all queries. If you're not sure what `{refresh_table}` name to use, use `{schema}_{table}_v{N}`. For example, if the source table is `sales.orders` then you'd use `sales_orders_v2` ```sql theme={null} -- Replace {database}, {schema} and {table} placeholders -- {database}: name of the CDC enabled database -- {schema}: name of the schema with tables to refresh -- {table}: name of the table to refresh -- {refresh_table}: a unique name for the refreshed change table USE {database}; GO EXEC sys.sp_cdc_enable_table @source_schema = N'{schema}', @source_name = N'{table}', @role_name = N'streamkap_role', @filegroup_name = N'Streamkap_ChangeTracking', @supports_net_changes = 0, @capture_instance = N'{refresh_table}' GO ``` **Refresh table limitation** There cannot be more than 2 change tables for every source table. After refreshing a change table using the above script, confirm with Streamkap Support that your SQL Server Source has started streaming from the refreshed change table. Once confirmed, disable CDC on the outdated change table. Use the script below to do that. ```sql theme={null} -- Replace {database}, {schema}, {table} and {refresh_table} placeholders -- {database}: name of the CDC enabled database -- {schema}: name of the schema with the table refreshed earlier -- {table}: name of the table refreshed earlier -- {refresh_table}: name of the previous refresh table, usually {schema}_{table} USE {database}; GO EXEC sys.sp_cdc_disable_table @source_schema = N'{schema}', @source_name = N'{table}', @capture_instance = N'{refresh_table}' GO ``` If you're getting this error message when refreshing the change table structure, it's because there cannot be more than 2 change tables for every source table. To fix the problem, 1 of the 2 change tables for the source table need to be disabled. The scripts below can help you do that. ```sql theme={null} -- Replace {database}, {schema} and {table} placeholders -- {database}: name of the CDC enabled database -- {schema}: name of the schema with the table refreshed earlier -- {table}: name of the table refreshed earlier USE {database}; GO EXEC sys.sp_cdc_help_change_data_capture @source_schema = N'{schema}', @source_name = N'{table}' GO ``` The above script should return 2 results, the 2 change tables for the `{table}` specified. Typically you would disable the oldest change table, so use the `create_date` column to identify the oldest one. When you've identified the change table to disable, use its `source_schema`, `source_table` and `capture_instance` names in the query below and execute. Then, try your refresh table script again. ```sql theme={null} -- Replace {database}, {schema}, {table} and {refresh_table} placeholders -- {database}: name of the CDC enabled database USE {database}; GO EXEC sys.sp_cdc_disable_table @source_schema = N'{source_schema}', @source_name = N'{source_table}', @capture_instance = N'{capture_instance}' GO ``` There can be a number of reasons. The most common are misconfiguration of CDC and privilege grants. To diagnose, run the following script: ```sql theme={null} -- Replace {database} placeholder -- {database}: name of the CDC enabled database USE {database}; GO EXEC sys.sp_cdc_help_change_data_capture GO ``` If the query returns an **error** or **no results** check: * you are connected to the SQL Server database with a user that has `DB_OWNER` role privileges. * if the `EXEC sys.sp_cdc_help_change_data_capture` stored procedure returns **no results**, the source tables you want Streamkap to capture may not be CDC enabled. If the query returns **results**, check: * the table(s) captured by the Connector are listed. * there are `capture_instance`s for the capture tables. * the database user or role used by the Connector are members of the `rolename` for the capture table(s). If you are still having issues after following the above steps, please don't hesitate to reach out to us. There can be many reasons for the Setup scripts to fail, but the scripts below can help you diagnose the issues. ```sql theme={null} -- Replace {database} placeholder -- {database}: name of the CDC enabled database USE {database}; GO SELECT name, database_id, source_database_id, compatibility_level, is_read_only, state, state_desc, is_in_standby, is_cleanly_shutdown, is_cdc_enabled, is_encrypted, replica_id FROM sys.databases WHERE name = '{database}' AND is_cdc_enabled=1; EXEC sys.sp_cdc_help_change_data_capture GO ``` If any of the queries return an **error** or **no results**: * Check you connected to the SQL Server database with a user that has `DB_OWNER` role privileges * If the `SELECT ... FROM sys.databases ...` returns **no results**, the database you want Streamkap to capture may not be CDC enabled * If the `EXEC sys.sp_cdc_help_change_data_capture` stored procedure returns **no results**, the source tables you want Streamkap to capture may not be CDC enabled If you are still having issues after following the above steps, please don't hesitate to reach out to us. For large database instances with many databases or tables, schema history can impact performance. Learn about optimization settings that can reduce schema history topic size and improve connector startup times. See the [Schema History Optimization](/schema-history-optimization) guide for detailed information on when and how to use these settings. * Use dedicated CDC user with minimal privileges * Ensure SQL Server Agent is always running and monitored * Set appropriate CDC cleanup retention (default 3 days) * Limit capture to needed tables only to reduce change table size * Test schema evolution procedures in staging * Monitor disk space for change tables * For cloud: Use provider monitoring; enable heartbeats for low traffic * Document schema change procedures for your team Database version upgrades require careful planning to avoid data loss and minimize downtime for your CDC pipelines. See our [Database Upgrade Guide](/database-upgrade-guide) for step-by-step instructions. # SSH Tunnel Source: https://docs.streamkap.com/ssh-tunnel Use an SSH tunnel to connect to databases SSH bastion is a jump server (or gateway server) that gives access to databases within a private network using the SSH protocol. # How to connect via SSH Tunnel * When setting up a connector, choose to Connect via SSH Tunnel. * Copy the unique SSH Public Key * Setup a Streamkap user on your bastion host * Add the ssh-ed25519 key to the streamkap user on the bastion host * Add [Streamkap IP Addresses](/streamkap-ip-addresses) to your allowed hosts. * Input bastion address into SSH Host * Input the username into SSH User # Why ssh-ed25519 keys vs RSA? The Ed25519 algorithm has superior security characteristics and computational efficiency. * Robust Security: The Ed25519 algorithm is based on elliptic curve cryptography (ECC), providing a high level of security with a 128-bit security level. It offers strong resistance against brute force attacks, making it highly suitable for protecting your remote access credentials. * Smaller Key Size: Ed25519 keys are smaller than their RSA counterparts, reducing the storage and transmission overhead. Smaller keys also contribute to faster authentication times, improving overall connection speeds. * Fast Key Generation: Generating Ed25519 keys is faster compared to traditional RSA keys # How to Setup a Bastion Host ## Setup Linux SSH Host Log in to your SSH tunnel host and run the following commands: ```bash Bash theme={null} -- Create group streamkap: sudo groupadd streamkap -- Create user streamkap: sudo useradd -m -g streamkap streamkap -- Switch to the Streamkap user: sudo su - streamkap -- Create the .ssh directory: mkdir ~/.ssh -- Set permissions: chmod 700 ~/.ssh -- Change to the .ssh directory: cd ~/.ssh -- Create the authorized_keys file: touch authorized_keys -- Set permissions: chmod 600 authorized_keys -- Using the key given to you from Streamkap, add this to the authorized_keys file. echo "" >> ~/.ssh/authorized_keys -- Set the SSH daemon configuration for Port Forwarding: sudo nano /etc/ssh/sshd_config AllowTcpForwarding yes -- Restart the SSH Service sudo systemctl restart sshd ``` Once the user is created, you'll need to allow IP and port access in these two locations * Streamkap IP to your tunnel server's SSH port. See [Streamkap IP Addresses](/streamkap-ip-addresses) * Your SSH tunnel server to your connector IP and Port ## Setup Windows SSH Host Log in to your SSH tunnel host and run the following commands: ```bash Bash theme={null} -- Create user streamkap: net user streamkap /add /comment:"Streamkap User" /passwordchg:no /passwordreq:no /logonpasswordchg:no -- Create group streamkap: net localgroup streamkap-group /comment:"Streamkap Group" /add -- In Windows command prompt, switch to the SSH server directory. cd C:\\ProgramData\\ssh\\ && start notepad .\\sshd_config -- Allow password authentication for the Streamkap user. PasswordAuthentication yes -- Allow the Streamkap user to connect to the SSH server. Add the following line to the sshd_config file. AllowUsers streamkap -- If your Windows build is 1809 or later, comment out the following lines in the sshd_config file: # Match Group administrators # AuthorizedKeysFile **PROGRAMDATA**/ssh/administrators_authorized_keys Save the sshd_config file. -- Restart the agent and the sshd service. If the following command fails, you can restart from the Task Manager (Alt + Ctrl + Delete). Restart-Service ssh-agent; Restart-Service sshd ``` ```bash Bash theme={null} -- Go into the SSH server using Windows VM. ssh streamkap@ -- Create an .ssh folder in your home directory. mkdir .ssh - Add an authorized_keys file if it does not exist -- If your client is Windows PowerShell: type nul > authorized_keys echo >> authorized_keys icacls C:\\Users\\streamkap\.ssh\\authorized_keys /inheritance:r -- If your client is Linux: touch authorized_keys chmod 600 authorized_keys echo >> authorized_keys ``` If you're running PowerShell in elevated mode, your setup is complete. If you're not running PowerShell in elevated mode, follow the instructions below. ```bash Bash theme={null} -- Allow public key authentication PubkeyAuthentication yes -- Remove password authentication. PasswordAuthentication no PermitEmptyPasswords no ``` Save the sshd\_config file. Verify that inheritance has been disabled and remove Administrator. * Right click on the authorized\_keys file * Select Properties * Select the Security tab * Select Advanced * Verify that the bottom left reads Enable Inheritance, which means that inheritance is disabled * Remove Administrator from the file security permissions. Restart the agent and the sshd service. ```bash Bash theme={null} Restart-Service ssh-agent; Restart-Service sshd ``` # Federated Authentication and SSO Source: https://docs.streamkap.com/sso Configure single sign-on and authentication methods for your organization Streamkap supports enterprise Single Sign-On (SSO) allowing your organization to use your existing identity provider for authentication. ## Supported Authentication Methods * **Magic Link** (Passwordless email authentication) * **Google** * **GitHub** * **Microsoft** * **Slack** * **Enterprise SSO** (SAML 2.0 and OpenID Connect) ## Enterprise SSO For organizations requiring centralized identity management, Streamkap supports: * [SAML IDP Setup](/saml-idp-setup) - For SAML 2.0 compliant identity providers * [OpenID Connect - IDP Setup](/openid-connect-idp-setup) - For OIDC compliant providers ## SSO with Sub-Accounts When using SSO across multiple [sub-accounts](/sub-accounts), the SAML Entity ID (also called the SP Entity ID or SAML Identifier) is shared by default across your root account and all sub-accounts. This works fine in most cases, but some identity providers require a unique identifier for each enterprise application. If you encounter issues such as: * Your IdP (e.g., Microsoft Entra / Azure AD) **rejects duplicate identifiers** when creating separate enterprise applications per sub-account * SSO authentication **fails** because the IdP cannot distinguish between sub-accounts sharing the same Entity ID * You need **independent SSO configurations** per sub-account with different role mappings or group assignments Then you may need unique Entity IDs assigned to each sub-account. **To request this, contact Streamkap support.** Unique Entity IDs will be assigned following a naming convention like: | Account | Entity ID | | ------------- | ------------------------------------ | | Root Account | `Streamkap-SSO` (unchanged) | | Sub-Account A | `Streamkap-SSO-{sub-account-name-a}` | | Sub-Account B | `Streamkap-SSO-{sub-account-name-b}` | Once assigned, you can create separate enterprise applications in your IdP for each sub-account, each using its corresponding Entity ID. This applies to SAML-based SSO. OpenID Connect configurations may have different requirements — contact support for guidance. ## Setup Guide For detailed instructions on configuring SSO, see [Setup SSO](/setup-sso). # Starburst Source: https://docs.streamkap.com/starburst Starburst Change Data Capture with Streamkap # Requirements * AWS Access Key and Secret Access Key with the following permissions to the destination bucket: * `s3:GetObject` * `s3:PutObject` * `s3:AbortMultipartUpload` * `s3:ListMultipartUploadParts` * `s3:ListBucketMultipartUploads` # Configure S3 Connector * `Name`: A descriptive name for the connector * `AWS Access Key`: An Access Key with the appropriate permissions for the bucket to which Streamkap will load data * `AWS Secret Access Key`: The Secret Access Key with the appropriate permissions for the bucket to which Streamkap will load data * `Region`: Name of the region for bucket to which Streamkap will load data * `Bucket Name`: The name of the bucket to which Streamkap will load data * `Format`: The format of the file. The following options are available: `Avro`, `Byte Array`, `JSON` and `Parquet` * `Directory`: The name of the directory to which Streamkap will load data If the Format Type selected is `JSON` or `Byte Array`: * `Compression Type`: How Streamkap should compress the files. The following options are available: `None` or `gzip` If the Format Type selected is `Avro`: * `Avro Compression Type`: How Streamkap should compress the files. The following options are available: `null`, `deflate`, `snappy`, or `bzip2` If the Format Type selected is `Parquet`: * `Parquet Compression Type`: How Streamkap should compress the files. The following options are available: `None`, `gzip`, `snappy`, `lz4`, `brotli`, `zstd`, or `lzo` * `Partition Field`: How the files should be partitioned. `Automatic` will partition the data automatically and not based on any particular field. `Field` will partition the data by the values of a specified field. `Time` will partition the data by the time of a particular field If the `Partition Field` is set to `Time`: * `Time Partition Directory Path Format`: The format of the partitioned directories. For example, if you set format to`'year'=YYYY/'month'=MM/'day'=dd/'hour'=HH`, the data directories will have the format `/year=2015/month=12/day=07/hour=15/` * `Time Partition Timezone`: The timezone of the partitioned directories. Accepts short and long standard names like: `UTC`, `PST`, `ECT`, `Europe/Berlin`, `Europe/Helsinki`, or `America/New_York` # DB2 Source: https://docs.streamkap.com/stream-into-db2 Stream data into DB2 ## Prerequisites * A database user with sufficient privileges to create users and schemas ## DB2 Setup ### 1. Create Database User It's recommended to create a separate user and role for Streamkap to access your DB2 database. Below is an example script that does that. ```SQL SQL theme={null} -- Connect to the DB2 database as admin -- Create the Streamkap User CREATE USER STREAMKAP_USER IDENTIFIED BY user_password; -- Create the Streamkap schema CREATE SCHEMA STREAMKAP; -- Grant necessary privileges to the user GRANT CREATEIN, ALTERIN, DROPIN, SELECT, INSERT, UPDATE, DELETE ON SCHEMA STREAMKAP TO USER STREAMKAP_USER; ``` ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Destination * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Destinations). * Choose **Db2**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Hostname**: Specify the hostname. * **Port**: Default is `50000`. * **Database**: Name of the database to use. * **Username**: Username to access the database. By default, Streamkap scripts use `STREAMKAP_USER`. * **Password**: Password to access the database. ### 3. Ingestion Settings * **Schema evolution mode**: Controls how schema evolution is handled by the sink connector. **Self-managed schema evolution** If you have created the destination tables and want to manage their schemas instead of the connector, set **Schema evolution mode** to `none`. See [Ingestion Modes](/insertsupserts) for detailed information about insert modes, primary key modes, and delete handling. * **Insert mode**: Specifies the strategy used to insert events into the database. * **Delete mode**: Specifies whether the connector processes DELETE or tombstone events and removes the corresponding row from the database. * **Primary key mode**: Specifies how the connector resolves the primary key columns from the event. * **Custom primary key**: Optional. Either the name of the primary key column or a comma-separated list of fields to derive the primary key from. * **Tasks**: The maximum number of active tasks. This controls the amount of parallelism in writing events. Click **Save**. ## How this Connector Works The DB2 connector supports idempotent write operations by using upsert semantics and basic schema evolution. The following features are supported: * At-least-once delivery * Delete mode * Idempotent writes (Insert/Upsert mode) * Schema evolution ## At-least-once delivery The DB2 connector guarantees that events that is consumes are processed at least once. ### Delete mode The DB2 connector can delete rows in the destination database when a DELETE or tombstone event is consumed. ### Idempotent writes The DB2 connector supports idempotent writes, allowing the same records to be replayed repeatedly and the final database state to remain consistent. In order to support idempotent writes, the DB2 connector must be set to Upsert mode. An upsert operation is applied as either an update or an insert, depending on whether the specified primary key already exists. If the primary key value already exists, the operation updates values in the row. If the specified primary key value doesn’t exist, an insert adds a new row. ### Schema evolution The DB2 connector supports schema evolution. The connector automatically detects fields that are in the event payload but that do not exist in the destination table. The connector alters the destination table to add the new fields. When schema evolution is set to `basic`, the connector automatically creates or alters the destination database table according to the structure of the incoming event. When an event is received from a topic for the first time, and the destination table does not yet exist, the DB2 connector uses the event’s key, or the schema structure of the record to resolve the column structure of the table. If schema evolution is enabled, the connector prepares and executes a `CREATE TABLE` SQL statement before it applies the DML event to the destination table. When the DB2 connector receives an event from a topic, if the schema structure of the record differs from the schema structure of the destination table, the connector uses either the event’s key or its schema structure to identify which columns are new, and must be added to the database table. If schema evolution is enabled, the connector prepares and executes an `ALTER TABLE` SQL statement before it applies the DML event to the destination table. Because changing column data types, dropping columns, and adjusting primary keys can be considered dangerous operations, the connector is prohibited from performing these operations. # MySQL Source: https://docs.streamkap.com/stream-into-mysql Stream data into MySQL ## Prerequisites * A database user with sufficient privileges to create users and schemas ## MySQL Setup ### 1. Create Database User It's recommended to create a separate user and role for Streamkap to access your MySQL database. Below is an example script that does that. ```SQL SQL theme={null} -- Connect to the MySQL server as admin -- Create the Streamkap User CREATE USER 'STREAMKAP_USER'@'%' IDENTIFIED BY 'user_password'; -- Create the Streamkap database and grant privileges CREATE DATABASE STREAMKAPDB; GRANT ALL PRIVILEGES ON STREAMKAPDB.* TO 'STREAMKAP_USER'@'%' IDENTIFIED BY 'user_password'; -- Grant specific table privileges GRANT SELECT, INSERT, UPDATE, DELETE ON STREAMKAPDB.STREAMKAP.* TO 'STREAMKAP_USER'@'%'; ``` ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Destination * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Destinations). * Choose **MySQL**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Hostname**: Specify the hostname. * **Port**: Default is `3306`. * **Database**: Name of the database to use. * **Username**: Username to access the database. By default, Streamkap scripts use `STREAMKAP_USER`. * **Password**: Password to access the database. ### 3. Ingestion Settings * **Schema evolution mode**: Controls how schema evolution is handled by the sink connector. **Self-managed schema evolution** If you have created the destination tables and want to manage their schemas instead of the connector, set **Schema evolution mode** to `none`. See [Ingestion Modes](/insertsupserts) for detailed information about insert modes, primary key modes, and delete handling. * **Insert mode**: Specifies the strategy used to insert events into the database. * **Delete mode**: Specifies whether the connector processes DELETE or tombstone events and removes the corresponding row from the database. * **Primary key mode**: Specifies how the connector resolves the primary key columns from the event. * **Custom primary key**: Optional. Either the name of the primary key column or a comma-separated list of fields to derive the primary key from. * **Tasks**: The maximum number of active tasks. This controls the amount of parallelism in writing events. Click **Save**. ## How this Connector Works The MySQL connector supports idempotent write operations by using upsert semantics and basic schema evolution. The following features are supported: * At-least-once delivery * Delete mode * Idempotent writes (Insert/Upsert mode) * Schema evolution ## At-least-once delivery The MySQL connector guarantees that events that is consumes are processed at least once. ### Delete mode The MySQL connector can delete rows in the destination database when a DELETE or tombstone event is consumed. ### Idempotent writes The MySQL connector supports idempotent writes, allowing the same records to be replayed repeatedly and the final database state to remain consistent. In order to support idempotent writes, the MySQL connector must be set to Upsert mode. An upsert operation is applied as either an update or an insert, depending on whether the specified primary key already exists. If the primary key value already exists, the operation updates values in the row. If the specified primary key value doesn’t exist, an insert adds a new row. ### Schema evolution The MySQL connector supports schema evolution The connector automatically detects fields that are in the event payload but that do not exist in the destination table. The connector alters the destination table to add the new fields. When schema evolution is set to `basic`, the connector automatically creates or alters the destination database table according to the structure of the incoming event. When an event is received from a topic for the first time, and the destination table does not yet exist, the MySQL connector uses the event’s key, or the schema structure of the record to resolve the column structure of the table. If schema evolution is enabled, the connector prepares and executes a `CREATE TABLE` SQL statement before it applies the DML event to the destination table. When the MySQL connector receives an event from a topic, if the schema structure of the record differs from the schema structure of the destination table, the connector uses either the event’s key or its schema structure to identify which columns are new, and must be added to the database table. If schema evolution is enabled, the connector prepares and executes an `ALTER TABLE` SQL statement before it applies the DML event to the destination table. Because changing column data types, dropping columns, and adjusting primary keys can be considered dangerous operations, the connector is prohibited from performing these operations. # Oracle Source: https://docs.streamkap.com/stream-into-oracle Stream data into Oracle ## Prerequisites * A database user with sufficient privileges to create users and schemas ## Oracle Setup ### 1. Create Database User It's recommended to create a separate user and role for Streamkap to access your Oracle database. Below is an example script that does that. ```SQL Non-CDB (Single-tenant) theme={null} -- Connect to the Oracle database as admin -- Create the Streamkap User CREATE USER STREAMKAP_USER IDENTIFIED BY user_password; -- Create the Streamkap schema and grant privileges CREATE USER STREAMKAP_USER IDENTIFIED BY user_password; GRANT CREATE SESSION, CREATE TABLE, CREATE PROCEDURE TO STREAMKAP_USER; GRANT UNLIMITED TABLESPACE TO STREAMKAP_USER; -- Grant specific privileges on tables GRANT SELECT, INSERT, UPDATE, DELETE ON STREAMKAP.STREAMKAP_TABLE TO STREAMKAP_USER; ``` ```SQL CDB (Multi-tenant) theme={null} -- Connect to the Oracle CDB as admin -- Create the Streamkap User CREATE USER STREAMKAP_USER IDENTIFIED BY user_password; -- Create the Streamkap schema in a PDB ALTER SESSION SET CONTAINER = your_pdb_name; CREATE USER STREAMKAP_USER IDENTIFIED BY user_password; GRANT CREATE SESSION, CREATE TABLE, CREATE PROCEDURE TO STREAMKAP_USER; GRANT UNLIMITED TABLESPACE TO STREAMKAP_USER; -- Grant specific privileges on tables in the PDB GRANT SELECT, INSERT, UPDATE, DELETE ON STREAMKAP.STREAMKAP_TABLE TO STREAMKAP_USER; ``` ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Destination * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Destinations). * Choose **Oracle**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Hostname**: Specify the hostname. * **Port**: Default is `1521`. * **Database**: Name of the database to use. * **Username**: Username to access the database. By default, Streamkap scripts use `STREAMKAP_USER`. * **Password**: Password to access the database. ### 3. Ingestion Settings * **Schema evolution mode**: Controls how schema evolution is handled by the sink connector. **Self-managed schema evolution** If you have created the destination tables and want to manage their schemas instead of the connector, set **Schema evolution mode** to `none`. See [Ingestion Modes](/insertsupserts) for detailed information about insert modes, primary key modes, and delete handling. * **Insert mode**: Specifies the strategy used to insert events into the database. * **Delete mode**: Specifies whether the connector processes DELETE or tombstone events and removes the corresponding row from the database. * **Primary key mode**: Specifies how the connector resolves the primary key columns from the event. * **Custom primary key**: Optional. Either the name of the primary key column or a comma-separated list of fields to derive the primary key from. * **Tasks**: The maximum number of active tasks. This controls the amount of parallelism in writing events. Click **Save**. ## How this Connector Works The Oracle connector supports idempotent write operations by using upsert semantics and basic schema evolution. The following features are supported: * At-least-once delivery * Delete mode * Idempotent writes (Insert/Upsert mode) * Schema evolution ## At-least-once delivery The Oracle connector guarantees that events that is consumes are processed at least once. ### Delete mode The Oracle connector can delete rows in the destination database when a DELETE or tombstone event is consumed. ### Idempotent writes The Oracle connector supports idempotent writes, allowing the same records to be replayed repeatedly and the final database state to remain consistent. In order to support idempotent writes, the Oracle connector must be set to Upsert mode. An upsert operation is applied as either an update or an insert, depending on whether the specified primary key already exists. If the primary key value already exists, the operation updates values in the row. If the specified primary key value doesn’t exist, an insert adds a new row. ### Schema evolution The Oracle connector supports schema evolution. The connector automatically detects fields that are in the event payload but that do not exist in the destination table. The connector alters the destination table to add the new fields. When schema evolution is set to `basic`, the connector automatically creates or alters the destination database table according to the structure of the incoming event. When an event is received from a topic for the first time, and the destination table does not yet exist, the Oracle connector uses the event’s key, or the schema structure of the record to resolve the column structure of the table. If schema evolution is enabled, the connector prepares and executes a `CREATE TABLE` SQL statement before it applies the DML event to the destination table. When the Oracle connector receives an event from a topic, if the schema structure of the record differs from the schema structure of the destination table, the connector uses either the event’s key or its schema structure to identify which columns are new, and must be added to the database table. If schema evolution is enabled, the connector prepares and executes an `ALTER TABLE` SQL statement before it applies the DML event to the destination table. Because changing column data types, dropping columns, and adjusting primary keys can be considered dangerous operations, the connector is prohibited from performing these operations. # PostgreSQL Source: https://docs.streamkap.com/stream-into-postgresql Stream data into PostgreSQL ## Prerequisites * A database user with sufficient privileges to create users and schemas ## PostgreSQL Setup ### 1. Create Database User It's recommended to create a separate user and role for Streamkap to access your PostgreSQL database. Below is an example script that does that. ```SQL SQL theme={null} -- Connect to the PostgreSQL server as admin -- Create the Streamkap User CREATE USER STREAMKAP_USER WITH PASSWORD 'user_password'; -- Create the Streamkap schema and grant privileges CREATE SCHEMA STREAMKAP; GRANT USAGE, CREATE ON SCHEMA STREAMKAP TO STREAMKAP_USER; -- Grant specific privileges on tables within the schema GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA STREAMKAP TO STREAMKAP_USER; ``` ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Destination * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Destinations). * Choose **PostgreSQL**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Hostname**: Specify the hostname. * **Port**: Default is `5432`. * **Database**: Name of the database to use. * **Username**: Username to access the database. By default, Streamkap scripts use `STREAMKAP_USER`. * **Password**: Password to access the database. ### 3. Ingestion Settings * **Schema**: The target schema where tables should be created. * **Schema evolution mode**: Controls how schema evolution is handled by the sink connector. **Self-managed schema evolution** If you have created the destination tables and want to manage their schemas instead of the connector, set **Schema evolution mode** to `none`. See [Ingestion Modes](/insertsupserts) for detailed information about insert modes, primary key modes, and delete handling. * **Insert mode**: Specifies the strategy used to insert events into the database. * **Delete mode**: Specifies whether the connector processes DELETE or tombstone events and removes the corresponding row from the database. * **Primary key mode**: Specifies how the connector resolves the primary key columns from the event. * **Custom primary key**: Optional. Either the name of the primary key column or a comma-separated list of fields to derive the primary key from. * **Tasks**: The maximum number of active tasks. This controls the amount of parallelism in writing events. Click **Save**. ## How this Connector Works The PostgreSQL connector supports idempotent write operations by using upsert semantics and basic schema evolution. The following features are supported: * At-least-once delivery * Delete mode * Idempotent writes (Insert/Upsert mode) * Schema evolution ### At-least-once delivery The PostgreSQL connector guarantees that events that is consumes are processed at least once. ### Delete mode The PostreSQL connector can delete rows in the destination database when a DELETE or tombstone event is consumed. ### Idempotent writes The PostgreSQL connector supports idempotent writes, allowing the same records to be replayed repeatedly and the final database state to remain consistent. In order to support idempotent writes, the PostgreSQL connector must be set to Upsert mode. An upsert operation is applied as either an update or an insert, depending on whether the specified primary key already exists. If the primary key value already exists, the operation updates values in the row. If the specified primary key value doesn’t exist, an insert adds a new row. ### Schema evolution The SQL Server connector supports schema evolution. The connector automatically detects fields that are in the event payload but that do not exist in the destination table. The connector alters the destination table to add the new fields. When schema evolution is set to `basic`, the connector automatically creates or alters the destination database table according to the structure of the incoming event. When an event is received from a topic for the first time, and the destination table does not yet exist, the PostgreSQL connector uses the event’s key, or the schema structure of the record to resolve the column structure of the table. If schema evolution is enabled, the connector prepares and executes a `CREATE TABLE` SQL statement before it applies the DML event to the destination table. When the PostgreSQL connector receives an event from a topic, if the schema structure of the record differs from the schema structure of the destination table, the connector uses either the event’s key or its schema structure to identify which columns are new, and must be added to the database table. If schema evolution is enabled, the connector prepares and executes an `ALTER TABLE` SQL statement before it applies the DML event to the destination table. Because changing column data types, dropping columns, and adjusting primary keys can be considered dangerous operations, the connector is prohibited from performing these operations. # SQL Server Source: https://docs.streamkap.com/stream-into-sqlserver Stream data into SQL Server ## Prerequisites * A database user with sufficient privileges to create users and schemas ## SQL Server Setup ### 1. Create Database User It's recommended to create a separate user, schema and database for Streamkap in your SQL Server instance to avoid potential conflicts with existing data. Below is an example script that does that. ```SQL SQL theme={null} -- Connect to the SQL Server instance as admin -- Create the Streamkap User CREATE LOGIN STREAMKAP_USER WITH PASSWORD = 'user_password'; CREATE USER STREAMKAP_USER FOR LOGIN STREAMKAP_USER; -- Create the Streamkap database and grant permissions CREATE DATABASE STREAMKAPDB; USE STREAMKAPDB; -- Create the Streamkap schema CREATE SCHEMA STREAMKAP; -- Set the Streamkap User's default schema ALTER USER STREAMKAP_USER WITH DEFAULT_SCHEMA = STREAMKAP; -- Grant specific permissions to the user on the schema and tables GRANT CREATE TABLE, CREATE FUNCTION, CREATE PROCEDURE TO STREAMKAP_USER; GRANT ALTER, SELECT, INSERT, UPDATE, DELETE, EXECUTE, REFERENCES, VIEW DEFINITION ON SCHEMA::STREAMKAP TO STREAMKAP_USER; ``` **Default schema required** Currently there is no option to specify the schema to use in the Streamkap setup for this Destination. The schema to use is determined by the user's `DEFAULT_SCHEMA`. If not set, it'll default to `dbo` schema. You can set the `DEFAULT_SCHEMA` using the following script: ```SQL SQL theme={null} -- Replace {...} placeholders as required ALTER USER {USERNAME} WITH DEFAULT_SCHEMA = {SCHEMA}; ``` ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Destination * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Destinations). * Choose **SQL Server**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Hostname**: Specify the hostname. * **Port**: Default is `1433`. * **Database**: Name of the database to use. * **Username**: Username to access the database. By default, Streamkap scripts use `STREAMKAP_USER`. * **Password**: Password to access the database. ### 3. Ingestion Settings * **Schema**: The target schema where tables should be created. * **Schema evolution mode**: Controls how schema evolution is handled by the sink connector. **Self-managed schema evolution** If you have created the destination tables and want to manage their schemas instead of the connector, set **Schema evolution mode** to `none`. See [Ingestion Modes](/insertsupserts) for detailed information about insert modes, primary key modes, and delete handling. * **Insert mode**: Specifies the strategy used to insert events into the database. * **Delete mode**: Specifies whether the connector processes DELETE or tombstone events and removes the corresponding row from the database. * **Primary key mode**: Specifies how the connector resolves the primary key columns from the event. * **Custom primary key**: Optional. Either the name of the primary key column or a comma-separated list of fields to derive the primary key from. * **Tasks**: The maximum number of active tasks. This controls the amount of parallelism in writing events. Click **Save**. ## How this Connector Works The SQL Server connector supports idempotent write operations by using upsert semantics and basic schema evolution. The following features are supported: * At-least-once delivery * Delete mode * Idempotent writes (Insert/Upsert mode) * Schema evolution ### At-least-once delivery The SQL Server connector guarantees that events that is consumes are processed at least once. ### Delete mode The SQL Server connector can delete rows in the destination database when a `DELETE` or tombstone event is consumed. ### Idempotent writes The SQL Server connector supports idempotent writes, allowing the same records to be replayed repeatedly and the final database state to remain consistent. In order to support idempotent writes, the SQL Server connector must be set to Upsert mode. An upsert operation is applied as either an update or an insert, depending on whether the specified primary key already exists. If the primary key value already exists, the operation updates values in the row. If the specified primary key value doesn’t exist, an insert adds a new row. ### Schema evolution The SQL Server connector supports schema evolution. The connector automatically detects fields that are in the event payload but that do not exist in the destination table. The connector alters the destination table to add the new fields. When schema evolution is set to `basic`, the connector automatically creates or alters the destination database table according to the structure of the incoming event. When an event is received from a topic for the first time, and the destination table does not yet exist, the SQL Server connector uses the event’s key, or the schema structure of the record to resolve the column structure of the table. If schema evolution is enabled, the connector prepares and executes a `CREATE TABLE` SQL statement before it applies the DML event to the destination table. When the SQL Server connector receives an event from a topic, if the schema structure of the record differs from the schema structure of the destination table, the connector uses either the event’s key or its schema structure to identify which columns are new, and must be added to the database table. If schema evolution is enabled, the connector prepares and executes an `ALTER TABLE` SQL statement before it applies the DML event to the destination table. Because changing column data types, dropping columns, and adjusting primary keys can be considered dangerous operations, the connector is prohibited from performing these operations. # Streaming Agents Source: https://docs.streamkap.com/streaming-agents Run LLM-powered agents on your Kafka streams — process each record with a model and tools, validate against a schema, and write results back to a topic **Beta.** Streaming Agents is in beta. The feature is in active development, and behavior may change. In the app it appears in the project sidebar as **Agentic**, marked *Beta*. A streaming agent is a Streamkap resource type alongside sources, destinations, pipelines, and transforms. It reads records from a Kafka topic, runs each one through an LLM (optionally calling tools), validates the result against a schema you declare, and writes the output to another topic. Records it can't process are routed to a dead-letter topic. Agents are independent of sources and destinations. They bind to a Kafka **topic**, whichever way that topic was produced: by a source connector, a transform, another agent, or your own producer. Use them to classify, enrich, redact, or summarize streaming records in real time without hand-building a Kafka consumer and an LLM loop. Not to be confused with [Agents](/agents), which covers connecting *external* AI assistants (Claude, Cursor, and others) to operate your Streamkap infrastructure via MCP, CLI, or REST. **Streaming Agents run inside Streamkap and process your streaming data.** ## How It Works 1. An agent consumes records from **one** input Kafka topic. 2. For each record, it calls your chosen LLM with your system prompt and the record as input. 3. If the agent has tools, the model can call them and feed the results back to itself, looping until it produces a final answer (up to a maximum number of steps). 4. The response is validated against your optional output schema and written to the output topic. 5. Records that can't be processed — schema mismatch, tool failure, or unparseable output — are routed to a **dead-letter topic** (`dlq.`). Every LLM and tool call runs on **your own credentials** — you bring an LLM connection (your provider API key), and all model usage is billed by your provider. Where available, Streamkap also provides managed **default connections** (an LLM and a vector store) so you can try your first agent without any keys — they're for quick tests, not production data. See [Streamkap Default Connections](/streaming-agents-connections#streamkap-default-connections). ## Where to Find It Streaming Agents is a **project-level** feature. Open **Agentic** (*Beta*) in the project sidebar to reach it. The section is organized into tabs: | Tab | What it's for | | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | **Overview** | Fleet health, recent activity, and a setup checklist for your first agent | | **Agents** | Create, deploy, and manage agents — see [Build an Agent](/streaming-agents-build) and [Manage Agents](/streaming-agents-manage) | | **Knowledge Bases** | Streaming embedding pipelines for retrieval — see [Knowledge Bases](/streaming-agents-knowledge-bases) | | **Observability** | Traces, tool calls, and logs — see [Observability](/streaming-agents-observability) | | **Connections** | Shared LLM, MCP, HTTP, and vector store credentials — see [Connections](/streaming-agents-connections) | | **Query Playground** | Ad-hoc SQL over your Kafka topics to inspect data before building an agent — see [Query Playground](/streaming-agents-observability#query-playground) | ## Key Concepts * **Agent** — a deployed job that processes one input topic. Agents come in two shapes, selected automatically from whether you add tools: * **Workflow** — no tools; a single LLM pass per record (classify, summarize, redact). * **ReAct** — one or more tools; the model reasons, calls a tool, feeds the result back, and repeats until it produces an answer. * **Connection** — a saved, encrypted credential shared across every agent in your organization: an LLM provider, an external MCP server, an HTTP endpoint, or a vector store. See [Connections](/streaming-agents-connections). * **Tool** — something the model can call during a run: an **HTTP** request, a **Script** (JavaScript or Python), an **External MCP** server, or **Streamkap MCP** (the agent operating your Streamkap platform). * **Knowledge Base** — a streaming pipeline that embeds a Kafka topic into a vector store so agents can retrieve from live data. See [Knowledge Bases](/streaming-agents-knowledge-bases). * **Memory** — optional short-term (time-boxed) and long-term (vector store) context carried across records. * **Dead-letter topic (DLQ)** — where unprocessable records land, derived as `dlq.`. ## Permissions Access is controlled by two permissions on your Streamkap roles: | Permission | Grants | | -------------- | ----------------------------------------------------------------------------------------------------------------------------- | | `read:agents` | View the Agentic section, agents, knowledge bases, observability traces and tool calls, and connections (secrets stay masked) | | `write:agents` | Create, edit, deploy, and manage agents and knowledge bases; save connections; view logs; run knowledge base retrieval | The sidebar entry is hidden if you don't have `read:agents`. Read-only users can view the screens but write actions are disabled. If you don't see the feature or an action, ask an admin for the appropriate permission. ## Limits | Limit | Value | | ---------------------------------- | ---------------------- | | Input topics per agent | 1 | | Knowledge bases attached per agent | 10 | | Max reasoning steps (ReAct) | 25 | | Custom instructions | 8,000 characters | | Filter SQL | 4,000 characters | | Config versions retained | 50 per agent | | Preview run | 15 seconds, one record | ## Next Steps Set up the LLM, MCP, HTTP, and vector store credentials agents use. Walk through the deploy wizard, preview a run, and go live. Lifecycle, savepoints and offsets, config history, and the DLQ. Stream a topic into a vector store for retrieval. Traces, tool calls, logs, and the query playground. # Build an Agent Source: https://docs.streamkap.com/streaming-agents-build Deploy a Streamkap streaming agent step by step — pick an input topic, configure the model, tools, and output schema, preview a run, and deploy Agents are created in a five-step wizard. This guide walks through each step, previewing a run before you deploy. Open **Agentic → Agents** and click **Deploy an agent**. ## Prerequisites * An **LLM connection** with the Chat capability — see [Connections](/streaming-agents-connections). The wizard prompts you to add one if none exist. Where available, you can pick the [Streamkap Default LLM](/streaming-agents-connections#streamkap-default-connections) to try an agent without bringing your own key. * An **input Kafka topic** the agent will read from. Any topic works — from a source, a transform, another agent, or your own producer. * For the **Streamkap MCP** tool, an agentic-enabled [Project Key](/project-keys). Deploying and editing agents requires the `write:agents` permission. ## Step 1 — Template Start from a preset — **Classify**, **Enrich**, **Route**, or **Redact PII** — or choose **Custom Agent** to start from scratch. A preset fills in the system prompt and a matching output schema, so you only name the agent, pick an input topic, and edit from there. Presets are single-pass with no tools; for a multi-step (ReAct) or retrieval (RAG) agent, choose **Custom Agent** and add your own tools or knowledge bases. In edit mode this step is skipped. | Preset | What it does | Output fields | | -------------- | ------------------------------------------------------------------------------------- | ---------------------------------- | | **Classify** | Assigns each record to a single category with a confidence score. | `category`, `confidence` | | **Enrich** | Adds derived fields (sentiment, priority, summary) while keeping the original record. | `sentiment`, `priority`, `summary` | | **Route** | Chooses a destination for each record, with a short reason. | `destination`, `reason` | | **Redact PII** | Masks personally identifiable information and flags whether any was found. | `redacted_text`, `contained_pii` | ## Step 2 — Input Set what the agent reads and how records reach the model. * **Agent name** (required) and an optional **description**. * **Input topic** (required) — select one topic. An agent reads from exactly one topic. * **Filter & fields** (optional) — restrict which records the agent processes and which fields reach the model: * Build a filter with the **Visual Builder** (field / operator / value rules) or write **Raw SQL**. Use **Preview matching records** to test the filter against recent topic data. * Under **Fields sent to the LLM**, choose which fields to include. Fewer fields means lower token cost. * **Input format** — auto-detected, or set the serialization (JSON or Avro) explicitly. * **Knowledge Bases** (optional) — attach up to 10 running knowledge bases; the agent gets a retrieval tool for each. See [Knowledge Bases](/streaming-agents-knowledge-bases). The agent receives each record's value from the input topic. Use **Fields sent to the LLM** to limit which fields reach the model and keep token cost down. ## Step 3 — Agent Configure the model and behavior. Each row opens a side drawer. ### Model Pick a saved **LLM connection** — the provider, model, and key all come from it. Optionally override per-agent tuning: temperature, max tokens, timeout, and (where the model supports it) reasoning effort and extended thinking. ### System Prompt Write your **custom instructions** — the guidance that shapes the agent's behavior (up to 8,000 characters). Streamkap assembles the full system prompt from a base template, your output schema, your tools, and these instructions, so the 8,000-character limit applies to your instructions, not the assembled prompt. Use **Generate** or **Refine with AI** to draft or improve them, and **Show base prompt** to see the full assembled prompt. ### Tools Adding one or more tools turns the agent into a **ReAct** agent — the model can call tools and loop on their results. An agent with no tools is a **Workflow** agent that makes a single LLM pass per record. Click **Add tool** and choose a type: | Tool | What it does | | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **HTTP** | Calls an external API. Optionally references an [HTTP connection](/streaming-agents-connections#http-connections) for the base URL and headers. Set method, URL, headers, body template, and timeout. | | **Script** | Runs a JavaScript or Python transformation you write inline. | | **External MCP** | Calls tools from an [external MCP server](/streaming-agents-connections#external-mcp-servers). Discover tools and choose which to expose. | | **Streamkap MCP** | Lets the agent operate your Streamkap platform via an agentic-enabled [Project Key](/project-keys). | For HTTP and Script tools, give each a **name**, a **description** (shown to the model so it knows when to call the tool), and any **parameters**. For **External MCP**, discover the server's tools and choose which to expose. For **Streamkap MCP**, select a Project Key that has **Use with agents** enabled, and optionally restrict which platform tools the agent can call — see [Streamkap MCP](/streaming-agents-connections#streamkap-mcp). ### Memory Optionally carry context across records, keyed by a record field: * **Short-term** — time-boxed context with a TTL (1 hour, 6 hours, 24 hours, or 7 days). * **Long-term** — persisted to a vector store connection. ### Processing * **Max reasoning steps** — the tool-use loop cap for ReAct agents (1–25). * **Parallelism** — how many records the agent processes concurrently. * **Checkpoint interval** (advanced) — how often the job checkpoints. ## Step 4 — Output Define what the agent writes. * **Output topic** — where results are written (auto-derived from the agent name; editable). Each output topic can be written by only one agent. * **Output serialization** — JSON or Avro. * **Output schema** (optional) — declare fields and scalar types (String, Double, Integer (32-bit), Long (64-bit), Boolean; nested objects and arrays aren't supported). The model's response is validated against the schema; records that don't match are routed to the dead-letter topic. With no schema, the raw LLM response is written through. The **dead-letter topic** is derived automatically as `dlq.` and isn't an editable field. Set up a consumer on it so failed records aren't missed — see [Manage Agents](/streaming-agents-manage#dead-letter-topic). ## Step 5 — Review & Deploy Review the summary cards for each section. Use the pencil on any card to jump back to that step. Then deploy — or save a draft to finish later. * **Deploy an agent** — deploys the job immediately. * **Save as Draft** — saves the configuration without deploying. The agent appears with a **Draft** status; deploy it later from the [Agents list](/streaming-agents-manage). ## Preview a Run Before deploying, use **Preview** to run a single record end-to-end without deploying. Paste a **record (JSON)** — or **Pull from topic** to grab a recent one — and click **Run preview**. Preview runs on **Anthropic**, **OpenAI**, and **Ollama** connections; other providers (Azure, Azure OpenAI, AWS Bedrock, OpenAI Responses) run only once the agent is deployed. Preview shows the outcome (completed, stopped at the step cap, timed out, or errored), the iterations, tokens in and out, latency, a step-by-step timeline, and whether the output matches your schema. A preview makes **real LLM, MCP, and HTTP tool calls** billed to your provider. **Memory and Script tools are not executed** in preview — they only run once the agent is deployed. Each run is capped at 15 seconds and one record. ## After Deploying The agent starts processing its input topic. Track it from the [Agents list](/streaming-agents-manage), the agent detail page, and [Agent Observability](/streaming-agents-observability). To change a running agent — including offset behavior on redeploy — see [Manage Agents](/streaming-agents-manage). # Connections Source: https://docs.streamkap.com/streaming-agents-connections Configure the shared LLM, external MCP, HTTP, and vector store credentials that Streamkap streaming agents and knowledge bases use Connections are saved, encrypted credentials shared across every agent in your organization (all projects). You define them once on the **Connections** tab, and agents and knowledge bases reference them by name, so keys aren't re-entered per agent, and rotating a credential in one place updates everything that uses it. An **LLM connection is required** before you can build an agent. The other connection types are optional and depend on what your agents do. Where available, Streamkap also provides two managed default connections you can use for quick tests without bringing your own keys — see [Streamkap Default Connections](#streamkap-default-connections). Secrets are encrypted at rest and masked when you view a connection. Read-only users (`read:agents`) can see connections with keys masked but cannot edit them; saving requires `write:agents`. ## LLM Connections An LLM connection holds a model provider's credentials and defaults. It's the identity an agent's model uses, and the embedding model a knowledge base uses. Each connection declares one or both **capabilities** — **Chat** (used by agents to run the model) and **Embedding** (used by knowledge bases and long-term memory to embed text): | Provider | Chat | Embedding | Notes | | ---------------------- | :--: | :-------: | ---------------------------------------------------------------------------------------------------------------------------------------- | | **Anthropic** | ✓ | — | Claude models. Supports a custom base URL for Anthropic-compatible proxies (LiteLLM, enterprise gateways). | | **OpenAI** | ✓ | ✓ | GPT and `text-embedding-*` models. Supports a custom base URL for self-hosted OpenAI-compatible servers (vLLM, LocalAI, TGI, LM Studio). | | **OpenAI (Responses)** | ✓ | — | OpenAI's Responses API for reasoning models. Embeddings still go through the plain OpenAI provider. | | **Azure** | ✓ | ✓ | Azure AI Studio. Requires your Azure base URL. | | **Azure OpenAI** | ✓ | — | Azure OpenAI via the OpenAI SDK. Requires the Azure endpoint and an API version. | | **Ollama** | ✓ | ✓ | Self-hosted. Requires the server base URL. | | **AWS Bedrock** | ✓ | ✓ | No API key — authenticates with AWS IAM credentials; you set the region. | | **Qwen** | — | ✓ | Embedding-only (DashScope-compatible). | | **OpenAI-compatible** | — | ✓ | Embedding-only; any host exposing a `/v1/embeddings` endpoint. | On the **Connections** tab, open **LLM Connections** and add a row. Enter a **Name** and pick a **Provider**, then paste the provider **API key** (Bedrock uses IAM credentials instead). For a self-hosted or proxy endpoint, enable the custom base URL and set it. Click **Test**. Streamkap validates the key against the provider and loads the live model list — the model dropdown is always fetched live from your provider, so new model releases show up without waiting for a Streamkap update. Choose the **Chat** and/or **Embedding** capabilities, then set defaults — chat model, temperature, max tokens, and timeout; embedding model for embedding connections. Agents can override these per agent. ## External MCP Servers An external MCP connection lets an agent call tools exposed by an MCP server you host. Configure a **Name**, an **Auth mode** (None, Bearer token, or Custom header), and the **Server URL**, then click **Test** to discover the tools the server exposes. External MCP server URLs must be **HTTPS on port 443**. Streamkap rejects private, loopback, and cloud-metadata addresses, and blocks unsafe headers (for example `Authorization` in custom-header mode, `Cookie`, and `Host`). To let an agent operate your *own* Streamkap platform instead, use the **Streamkap MCP** tool — see [Streamkap MCP](#streamkap-mcp) below. ## HTTP Connections An HTTP connection stores a reusable base URL and headers for **HTTP tools**. Define it once (name, base URL, headers, description) and select it when adding an HTTP tool to an agent, so the tool inherits the endpoint and auth without re-entering them. ## Vector Stores A vector store connection is where knowledge base embeddings and long-term memory are written and read. Pick a **Provider** and fill in its fields: | Provider | Fields | | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | **Pinecone** | API key, index host, optional default namespace | | **pgvector (Postgres)** | JDBC URL (`jdbc:postgresql://host:5432/db`), username, password, table (auto-created on first deploy), optional dimensions and distance metric | | **Milvus** | URI (or host + port), token (or username + password) | | **Amazon OpenSearch** | Endpoint and index, plus provider properties | | **Elasticsearch** | Index and vector field, plus provider properties | | **Amazon S3 Vectors** | Vector bucket and vector index, plus provider properties | For pgvector, don't embed credentials in the JDBC URL — put them in the username and password fields. Dimensions must match your embedding model's output; leave blank to inherit it. ## Streamkap Default Connections Where available, two Streamkap-managed rows appear alongside your own connections, marked with a **Streamkap Default** badge: * **Streamkap Default LLM** — a managed chat + embedding model. Available for quick tests without bringing your own key; not intended for production data. Rate limits and model choice may change. * **Streamkap Default Vector Store** — a managed vector index. Available for quick tests; not intended for production data. They let you deploy a working agent or knowledge base before you've set up any credentials of your own — select them anywhere a connection is picked (the agent wizard's Model step, a knowledge base's embedding model and vector store). They are read-only: the keys are managed by Streamkap, never displayed, and the rows can't be edited or deleted. Data written to the default vector store is isolated automatically — each organization (and each knowledge base) gets its own namespace on the managed index. You can't choose the namespace yourself. For production workloads, bring your own LLM and vector store connections. ## Streamkap MCP Streamkap MCP lets an agent operate your Streamkap platform — inspecting topics, checking pipeline status, and looking up schemas — as a tool during a run. It isn't a connection on this tab; it's authenticated by an **agentic-enabled [Project Key](/project-keys)**. Enable **Use with agents** on a Project Key (see [Use with Streaming Agents](/project-keys#use-with-streaming-agents)), then select it when adding the **Streamkap MCP** tool to an agent (see [Build an Agent](/streaming-agents-build#tools)). The key's [MCP tool scoping](/project-keys#step-3-mcp-scoping) restricts which platform tools the agent can call. Disabling **Use with agents** on a Project Key breaks any agent bound to that key. Re-enable it or point the agent at another agentic-enabled key. ## Related * [Build an Agent](/streaming-agents-build) — reference these connections when configuring an agent * [Knowledge Bases](/streaming-agents-knowledge-bases) — use embedding and vector store connections * [Project Keys](/project-keys) — credentials and MCP tool scoping # Knowledge Bases Source: https://docs.streamkap.com/streaming-agents-knowledge-bases Stream a Kafka topic into a vector store as embeddings so Streamkap agents can retrieve from live data at run time A knowledge base streams a Kafka topic through an embedding model into a vector store, keeping an always-current index your agents can retrieve from. Attach a knowledge base to an agent and it becomes a retrieval tool: the model decides when to look something up, and gets back the most relevant chunks from your data. Knowledge bases live on the **Knowledge Bases** tab of the Agentic section. Creating and managing them requires the `write:agents` permission. ## Prerequisites * An **LLM connection** with the **Embedding** capability — **OpenAI**, **Azure**, **Ollama**, **AWS Bedrock**, **Qwen**, or any **OpenAI-compatible** endpoint. See [Connections](/streaming-agents-connections#llm-connections). * A **vector store** connection — Pinecone, pgvector, Milvus, Amazon OpenSearch, Elasticsearch, or Amazon S3 Vectors. See [Connections](/streaming-agents-connections#vector-stores). * The **input Kafka topic** to embed. No credentials yet? Where available, the [Streamkap Default Connections](/streaming-agents-connections#streamkap-default-connections) can serve as both the embedding model and the vector store for a quick test — each knowledge base gets its own isolated namespace on the managed index automatically. ## Create a Knowledge Base Creation is a single form (not a multi-step wizard). Open **Knowledge Bases** and click **Create Knowledge Base**. Enter a **Name** (auto-derived from the topic) and select the **input topic** and its **serialization** (auto-detected, JSON, or Avro). Optionally set a **text template** with `${field}` placeholders to control what text gets embedded. Pick the **embedding model** connection and the **vector store** connection, then set the **index name** (auto-derived) — the index or collection the embeddings are written to — and an optional **namespace**. Optionally adjust chunk size and overlap, parallelism, checkpoint interval, and the distance metric (Cosine, Dot product, or Euclidean). Click **Create Knowledge Base** to deploy, or **Save as draft** to finish later. **Knowledge base settings are immutable after deploy.** There is no edit. To change a setting, use **Clone/Recreate** to start a new knowledge base from the existing configuration, then delete the old one. ## Attach to an Agent On the agent wizard's **Input** step, use the **Knowledge Bases** picker to attach up to 10 **running** knowledge bases. The agent gets one retrieval tool per attached knowledge base, and the model retrieves from it when the prompt calls for it. See [Build an Agent](/streaming-agents-build#step-2--input). ## Preview Retrieval From a knowledge base detail page, use **Preview retrieval** to test what the agent would get back. Enter a query and a result count (**n**), and Streamkap returns that many ranked chunks with their similarity scores and metadata. Preview retrieval requires the knowledge base to be **Running** and the `write:agents` permission (retrieved chunks are your data). It's currently supported for **Pinecone** vector stores (including the Streamkap default); knowledge bases on other vector stores still serve retrieval to agents at run time — only the in-app preview is unavailable. ## Lifecycle Knowledge bases reuse the same streaming infrastructure as agents but have a reduced lifecycle: **Cancel**, **Stop**, and **Delete** only. Because settings are immutable, there is no edit or redeploy. Recreate to change configuration. ## Related * [Connections](/streaming-agents-connections) — embedding and vector store credentials * [Build an Agent](/streaming-agents-build) — attach a knowledge base to an agent * [Pinecone](/pinecone) — Pinecone as a Streamkap destination # Manage Agents Source: https://docs.streamkap.com/streaming-agents-manage Deploy, cancel, stop, resume, and redeploy Streamkap streaming agents — including savepoint and Kafka offset behavior, drafts, config history, and the dead-letter topic The **Agents** tab lists every agent in the project and is where you run the agent lifecycle. Managing agents requires the `write:agents` permission; read-only users can view the list but not act on it. ## The Agents List Each row shows the agent, its input topic, tool count, output topic, status, and config version. Expand a row for a health glance, or open the row menu for actions. * **Search** by name, topic, or tool. * **Filter** by status: All, Running, Deploying, Failed, Stopped, or Drafts. * **Bulk actions** (Cancel, Redeploy, Delete) apply to selected rows. ## Statuses | Status | Meaning | | -------------- | --------------------------------------------------- | | **Draft** | Saved but not deployed | | **Deploying** | Deployment in progress | | **Running** | Processing its input topic | | **Cancelling** | Cancel in progress | | **Cancelled** | Stopped by a cancel or stop | | **Failed** | The job failed — open the detail page for the error | Cancelled and stopped agents both show the **Cancelled** status and appear under the **Stopped** filter. While an agent is Deploying or Cancelling, state-change actions are disabled until it settles. ## Lifecycle Actions Cancel and stop differ in whether they keep a **savepoint**, which determines where processing resumes on the Kafka topic. | Action | Available when | Effect | | --------------------------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Deploy** | Draft | Deploys the agent for the first time. | | **Cancel (with savepoint)** | Running | Stops gracefully and writes a savepoint, preserving the topic position. | | **Stop (no savepoint)** | Running | Stops immediately without a savepoint. | | **Resume from savepoint** | Cancelled / Failed | Redeploys from the savepoint, continuing from the last processed offset. Only available when a savepoint exists: after **Cancel (with savepoint)**, not after **Stop**. | | **Redeploy from earliest** | Cancelled / Failed | Redeploys without a savepoint, reprocessing the topic from the earliest offset. | | **Edit & Redeploy** | Any deployed agent | Saves changes and redeploys. See the warning below. | | **Delete** | Any | Permanently removes the agent (requires typing a confirmation). | **Editing and redeploying resets Kafka offsets.** Saving a change cancels the running job and redeploys without a savepoint, so the agent reprocesses from the earliest offset of its input topic. To preserve the position, **Cancel (with savepoint)** first, then **Resume from savepoint**. Some secrets — such as the LLM key or an MCP token — come back **masked** when you edit an agent and don't round-trip. Leave a masked field blank to keep the stored value, or type a new value to replace it. Deploy is blocked if a required secret was cleared and not re-entered. ## Drafts Saving a draft (from the wizard) stores the configuration without deploying. Drafts show a **Draft** status and can be deployed from the list or the detail page once ready. ## Duplicate Use **Duplicate** to clone an agent's configuration into a new one (its name is suffixed with `-copy`). The clone opens in the wizard pre-filled, so you can adjust and deploy it separately. ## Config History & Rollback Every successful save is versioned (up to 50 versions per agent). From the agent detail or edit header, open **History** to view a past version and **roll back** to it with a note. A rollback creates a new version rather than overwriting history. ## Dead-Letter Topic Records an agent can't process are routed to its dead-letter topic, `dlq.`. Records land there on output-schema mismatch, unparseable model output, tool errors, or exhausted LLM retries, with an error header describing why. Streamkap does not consume the dead-letter topic for you. **Set up your own Kafka consumer on `dlq.`** to catch failed records — otherwise failures can go unnoticed. See [DLQ Operations](/dlq-operations). ## Troubleshooting Open the agent detail page for the error message. Common causes are an unavailable input topic, an unreachable tool endpoint, or an LLM connection whose key no longer works. Fix the configuration and redeploy. Check the dead-letter topic (`dlq.`). Records that fail output-schema validation or a tool call are routed there rather than to the output topic. Loosen or correct the output schema, or fix the failing tool. Editing masks stored secrets. If you cleared a required field (for example the LLM key), re-enter it before deploying, or leave it blank to keep the stored value. An output topic can be written by only one agent. If you see a conflict, change the output topic on Step 4 of the wizard. ## Related * [Build an Agent](/streaming-agents-build) — the deploy wizard * [Agent Observability](/streaming-agents-observability) — traces, tool calls, and logs * [DLQ Operations](/dlq-operations) — working with dead-letter topics # Agent Observability Source: https://docs.streamkap.com/streaming-agents-observability Monitor Streamkap streaming agents with execution traces, tool-call sessions, and runtime logs, and inspect topic data with the query playground The **Observability** tab is the single place to see what your agents are doing: per-record execution traces, tool-call activity, and runtime logs. Select an entity in the left sidebar (a streaming agent, an external agent, or a knowledge base), pick a time range, and choose what to show. Viewing traces and tool calls requires `read:agents`; viewing **logs** requires `write:agents`. ## Entity Sidebar The sidebar groups activity into **Streamkap Agents**, **External Agents**, and **Knowledge Bases**, plus an **All activity** view. **External Agents** are external AI assistants calling your Streamkap MCP server (see [Agents](/agents)). They show up here even though they aren't streaming agents. Each agent row shows a status dot and its 24-hour tool-call count: * **Red** — errors in the last 24 hours * **Green** — healthy, with recent activity * **Grey** — idle for over an hour Only entities active in the selected time range appear — widen the range to see more. ## Trace The trace view shows per-record execution. Each input record produces one or more **iterations**, and each iteration lists its operations: * **LLM** — a model call * **Tool** — a tool invocation * **Output** — the record written to the output topic Each operation shows its type, status (**Success**, **Error**, or **Denied** — a tool call blocked by the agent's tool scope), and duration. Use it to see exactly how a record was processed and where a failure occurred. ## Tool Calls The tool-call view covers **MCP** tool calls (Streamkap MCP and external MCP), grouped into **sessions** with a status histogram and per-session p95 latency. Click a span to open its detail — the tool name, timing, parameters, and any error or denial. To trace HTTP or Script tool calls, use the **Trace** view above and the agent's **Logs**. ## Logs The log view tails the agent's runtime logs. Filter by **level** (DEBUG, INFO, WARN, ERROR) and **search** for a substring, click a histogram bar to narrow the window, or enable **Live tail** to follow new lines. Logs require the `write:agents` permission because log lines can contain record payloads. Log lines are scoped to the shared runtime, so an agent's logs may include lines from your other agents on the same infrastructure (never another organization's). A banner flags this where it applies. ## Query Playground The **Query Playground** tab runs ad-hoc SQL over your Kafka topics — useful for inspecting an input topic's shape and sample records before you build an agent, or checking an output topic afterward. Pick a topic from the tree to insert a starter query, edit the SQL, and **Run Query**. Results show the matched rows and query metadata (topic, records scanned, records matched). It requires `read:agents` and `read:topics`. ## Related * [Manage Agents](/streaming-agents-manage) — lifecycle and the dead-letter topic * [Observability](/observability) — system health and lineage for sources, destinations, and transforms * [Logs](/logs) — connector and pipeline logs * [Topics](/topics) — browse Kafka topics # Streamkap Cloud Source: https://docs.streamkap.com/streamkap-cloud Fully managed CDC platform hosted and operated by Streamkap Streamkap Cloud is the simplest and fastest way to get going. Everything is hosted by us with 24/7 monitoring and SOC2 security. You will likely need to review [Connection Options](/connection-options) to allow our IP addresses to connect. # Streamkap Encryption Source: https://docs.streamkap.com/streamkap-encryption Streamkap Encryption Streamkap provides data encryption options that you can use to meet strict data management requirements. The certificates that Streamkap uses for encryption must be renewed every 12 months - maintenance window will be arranged during this renewal process ## Encryption in transit Streamkap uses TLS 1.2, which encrypts data in transit. Streamkap brokers uses public AWS Certificate Manager certificates. Therefore, any truststore that trusts Amazon Trust Services also trusts the certificates of Streamkap brokers. ## Encryption at rest Streamkap always encrypts your data at rest. Streamkap uses by default EBS and S3 Encryption at rest. ### Bring Your Own Keys - (BYOK) Streamkap integrates with AWS Key Management Service (KMS) to offer transparent server-side encryption for your Kafka and Flink Cluster. # Streamkap IP Addresses Source: https://docs.streamkap.com/streamkap-ip-addresses To connect to systems behind firewalls, gateways and hosted in virtual private clouds, the following IPs need to be safelisted. | Region | IPs | | ------------ | -------------------------------------------------------- | | us-west | 52.32.238.100, 5.78.187.7, 5.78.188.23, 35.84.205.51 | | us-east | 44.214.80.49, 87.99.130.177, 87.99.128.64, 98.95.94.141 | | eu | 34.242.118.75, 49.13.0.59, 159.69.181.163, 52.16.113.210 | | ap-southeast | 52.62.60.121, 5.223.72.133, 5.223.74.144 | When signing up, us-west is the default region. Let us know if you need it to be elsewhere. # Getting Started Source: https://docs.streamkap.com/streamkap-provider-for-terraform Manage your Streamkap resources as infrastructure-as-code using Terraform. The Streamkap Terraform Provider lets you manage data integration infrastructure using Terraform's declarative configuration language. Define sources, destinations, pipelines, and transforms **as code** for version control, reproducibility, and automation. ## What is Terraform? [Terraform](https://www.terraform.io/) is an infrastructure-as-code (IaC) tool that allows you to define and provision infrastructure using a declarative configuration language. Instead of manually configuring resources through a UI, you write configuration files that describe your desired state, and Terraform handles creating, updating, and deleting resources to match. ## Why Use Terraform with Streamkap? Track changes to your data infrastructure in Git alongside your application code. Easily replicate your Streamkap setup across development, staging, and production environments. Integrate with CI/CD pipelines for automated infrastructure deployment and updates. Your Terraform configuration serves as living documentation of your data infrastructure. ## Available Resources The provider supports all Streamkap connector types — sources, destinations, transforms, pipelines, topics, and data sources. The beta version exposes significantly more connectors and transform resources than the stable version. See the [Resource Reference](/terraform-resources) for the full list with registry links, or browse the [HashiCorp Registry](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs) for complete attribute documentation and examples. ## Quick Example ```hcl theme={null} terraform { required_providers { streamkap = { source = "streamkap-com/streamkap" version = "3.0.0-beta.6" } } } provider "streamkap" {} # Define a PostgreSQL source resource "streamkap_source_postgresql" "orders_db" { name = "orders-database" database_hostname = "db.example.com" database_port = 5432 database_dbname = "orders" database_user = var.db_username database_password = var.db_password database_sslmode = "require" schema_include_list = "public" table_include_list = "public.orders,public.customers" snapshot_read_only = "No" signal_data_collection_schema_or_database = "streamkap.streamkap_signal" heartbeat_enabled = true heartbeat_data_collection_schema_or_database = "streamkap" slot_name = "streamkap_slot" publication_name = "streamkap_pub" } # Define a Snowflake destination resource "streamkap_destination_snowflake" "warehouse" { name = "analytics-warehouse" snowflake_url_name = var.snowflake_url snowflake_user_name = var.snowflake_user snowflake_private_key = var.snowflake_private_key snowflake_database_name = "ANALYTICS" snowflake_schema_name = "PUBLIC" snowflake_role_name = "STREAMKAP_ROLE" sfwarehouse = "COMPUTE_WH" } # Create a pipeline connecting them resource "streamkap_pipeline" "orders_to_snowflake" { name = "orders-pipeline" snapshot_new_tables = true source = { id = streamkap_source_postgresql.orders_db.id name = streamkap_source_postgresql.orders_db.name connector = streamkap_source_postgresql.orders_db.connector topics = ["public.orders", "public.customers"] } destination = { id = streamkap_destination_snowflake.warehouse.id name = streamkap_destination_snowflake.warehouse.name connector = streamkap_destination_snowflake.warehouse.connector } } ``` ## Next Steps Install Terraform CLI on macOS, Windows, or Linux Set up credentials and create your first configuration Browse all available resources with examples ## Additional Resources * [Streamkap Provider on HashiCorp Registry](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs) - Full attribute documentation * [Provider Source Code on GitHub](https://github.com/streamkap-com/terraform-provider-streamkap) - Report issues and contribute * [Terraform Documentation](https://developer.hashicorp.com/terraform/docs) - Learn Terraform fundamentals * [API Tokens](/api-tokens) - Create credentials for the provider * [Agents](/agents) - Use Terraform with AI agents via [Terraform MCP servers](https://developer.hashicorp.com/terraform/mcp-server) # Streamkap Support Source: https://docs.streamkap.com/streamkap-support Overview on Streamkap Support Here is an overview on how support works at Streamkap. | | Starter | Business | Scale | Enterprise | | ------------- | ------------------------- | ------------------------- | ------------------------- | ------------------------- | | Hours | Monday - Friday 9-5 | Monday - Friday 9-5 | Monday - Friday 9-9 | 24/7 | | SLA | 99.9% | 99.9% | 99.99% | 99.99% | | Response Time | 4 hours | 2 hours | 1 hour | 1 hour | | Channels | Email, Chat, Slack, Teams | Email, Chat, Slack, Teams | Email, Chat, Slack, Teams | Email, Chat, Slack, Teams | ## Contacting Support To receive support you can contact us on any channel. ## Pro-Active Support Customers on a paid plan will receive pro-active support to ensure the SLA is met. If a pipeline becomes unstable and our automation is unable to automatically stabilise, our support will be alerted to investigate. This will only happen for Pipelines marked as Production. ### How to mark a pipeline as Production: 1. You must be on a paid plan that is up to date on payments 2. The pipeline exists on a Production [Project](/projects) 3. The pipeline is marked as Production when you created or edited the pipeline. # Sub Accounts Source: https://docs.streamkap.com/sub-accounts Manage multiple accounts with Streamkap # Enable Sub Accounts At present, sub accounts need to be enabled by contacting support. Once enabled, you can access in **Account preferences** under the **Managed** section by clicking **All Accounts**. All Accounts showing table view with sub-accounts # Hierarchy Support It's possible to create multiple sub accounts, which are entities in their own rights isolated from each other. It is also possible to create sub accounts of sub accounts so that you can create the hierarchy you require. You can view your account hierarchy in a graph view: All Accounts graph view showing parent and child accounts # Enable User Virtual Access to Sub Accounts There are two ways to add users to sub-accounts: 1. Specifically add the user to the sub-account 2. Allow virtual access to the sub-account - this is needed at least for the creator of the sub-account as otherwise you wouldn't be able to invite others using option 1 To enable virtual access: 1. Click into the parent account while in the **All Accounts** view 2. Enable the **Sub-account access** toggle for users who need access to sub-accounts Account users showing sub-account access toggle You will now be able to access sub accounts you create virtually, without needing a user in the sub-account to be created. # Billing Each sub account has the option for its own billing or can be rolled up to the parent account. The parent account subscription in that case can cover all the sub accounts too and receive the volume discounts. # User Access Access can be granted on a per account basis or to allow the parent accounts sub account access to any level by using Roles, learn more at [Team Management](/team-management) Switching between accounts is possible via the account selector in the app or via **Account preferences** → **Account Details**. # SSO with Sub-Accounts If your organization uses SAML-based SSO and your identity provider requires unique Entity IDs per enterprise application, you may need unique SAML Identifiers assigned to each sub-account. This is typically needed when your IdP (e.g., Microsoft Entra) rejects duplicate identifiers across multiple SSO configurations. Contact Streamkap support to request this. See [SSO with Sub-Accounts](/sso#sso-with-sub-accounts) for details. # Auto linking accounts based upon domain name Once sub accounts are enabled on your account, it's possible to also auto link accounts based upon the domain name. # Supabase Source: https://docs.streamkap.com/supabase Set up PostgreSQL change data capture from a Supabase project to Streamkap, including logical replication, replication roles, publications, and heartbeats. ## Prerequisites * PostgreSQL version ≥ 10 * (Recommended) Instance size of XL or greater * Supabase IPv4 add-on [enabled](https://supabase.com/docs/guides/platform/ipv4-address#enabling-the-ipv4-add-on) * A database user with sufficient privileges for creating users ## Supabase Setup ### 1. Grant Database Access * Configure one of the [Connection Options](/connection-options) to ensure Streamkap can reach your database. ### 2. Create Database User If you are running a PostgreSQL version earlier than 17, you will need to use the `postgres` user for replication and continue to the next step: [Enable Snapshots](/supabase#3-enable-snapshots). This is because, in pre-17 versions, non-superusers cannot create or manage replication slots. It's recommended to create a separate user and role for the Connector to access your PostgreSQL database. Below is an example script that does that. ```SQL SQL theme={null} -- Replace { ... } placeholders as required CREATE USER streamkap_user PASSWORD '{password}'; -- Create a role for Streamkap CREATE ROLE streamkap_role REPLICATION LOGIN; GRANT streamkap_role TO streamkap_user; -- Grant Streamkap permissions on the database, schema and all tables to capture GRANT CONNECT ON DATABASE "{database}" TO streamkap_role; GRANT CREATE, USAGE ON SCHEMA "{schema}" TO streamkap_role; GRANT SELECT ON ALL TABLES IN SCHEMA "{schema}" TO streamkap_role; -- Grant replication role to the user ALTER USER streamkap_user WITH REPLICATION; ``` ### 3. Enable Snapshots To backfill your data, the Connector needs to be able to perform snapshots. See [Snapshots & Backfilling](/snapshots) for more information. To enable this feature, there are 2 methods available: #### Method 1: Enable read only connection Requires PostgreSQL version 13 or higher. This method is recommended if you cannot create a table in the source database and grant the Connector read/write privileges to that. * Set **Read only** to **Yes** during Streamkap Setup. No other configuration should be necessary. #### Method 2: Create a table in the source database Not supported on read replicas. Please use method 1 instead. You will need to create the table. The Connector will use this collection for managing snapshots. Below is an example script that does that. The examples below use `streamkap_signal` as the signal table name, but you can choose any name. During [Streamkap Setup](#3-snapshot-settings), provide the full path to your signal table in `schema.table` format (e.g., `streamkap.streamkap_signal`). ```SQL SQL theme={null} -- Create the schema CREATE SCHEMA streamkap; -- Switch to the newly created schema SET search_path TO streamkap; -- Create the table CREATE TABLE streamkap_signal ( id VARCHAR(255) PRIMARY KEY, type VARCHAR(32) NOT NULL, data VARCHAR(2000) NULL ); -- Grant necessary privileges on the table to the role GRANT CREATE, USAGE ON SCHEMA streamkap TO streamkap_role; GRANT SELECT ON ALL TABLES IN SCHEMA streamkap TO streamkap_role; GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE streamkap_signal TO streamkap_role; ``` **Publications and signal tables** When you create the PostgreSQL publication in the next step, if you choose to specify tables for capture instead of all tables, you **must** include your signal table. ### 5. Heartbeats Connectors use "offsets"—like bookmarks—to track their position in the database's log or change stream. When no changes occur for long periods, these offsets may become outdated, and the Connector might lose its place or stop capturing changes. Heartbeats ensure the Connector stays active and continues capturing changes. There are two layers of heartbeat protection: #### Layer 1: Connector heartbeats (enabled by default) The Connector periodically emits heartbeat messages to an internal topic, even when no actual data changes are detected. This keeps offsets fresh and prevents staleness. No configuration is necessary for this layer; it is automatically enabled. We recommend keeping this layer enabled for all deployments. #### Layer 2: Source database heartbeats (recommended) **Why we recommend configuring Layer 2** While Layer 2 is **crucial** for low-traffic or intermittent databases, we recommend configuring it for all deployments. It provides additional resilience and helps prevent issues during periods of inactivity. You can configure regular updates to a dedicated heartbeat table in the source database. This simulates activity, ensuring change events are generated consistently, maintaining log progress and providing additional resilience. How this layer is configured depends on the connection type (if supported by the Source): * **Read-write connections** (when **Read only** is **No** during Streamkap Setup): The Connector updates the heartbeat table directly. * **Read-only connections** (when **Read only** is **Yes** during Streamkap Setup): A scheduled job on the **primary** database updates the heartbeat table, and these changes replicate to the read replica for the Connector to consume. This layer requires you to set up a heartbeat table—and for read-only connections, a scheduled job (e.g., `pg_cron` for PostgreSQL, `event_scheduler` for MySQL)—on your source database. For read-write connections (when **Read only** is **No** during Streamkap Setup), the Connector writes to the heartbeat table directly. ```SQL SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Switch to the streamkap schema SET search_path TO streamkap; -- Create the heartbeat table with id, text, and last_update fields CREATE TABLE streamkap_heartbeat ( id SERIAL PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Grant permission to the Streamkap user GRANT USAGE ON SCHEMA streamkap TO streamkap_user; GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE streamkap_heartbeat TO streamkap_user; -- Insert the first row into the heartbeat table INSERT INTO streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` **Heartbeat tables and PostgreSQL publications** If the `streamkap_pub` publication created during PostgreSQL Setup was for specific tables e.g. `CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, table3, ...;` instead of `FOR ALL TABLES;`, you **must** add the heartbeat table to the publication: `ALTER PUBLICATION streamkap_pub ADD TABLE streamkap.streamkap_heartbeat;`. For read-only connections (when **Read only** is **Yes** during Streamkap Setup), the Connector cannot write to the heartbeat table directly. Instead, you must configure a scheduled job on the **primary** database to generate artificial traffic. These changes will replicate to the read replica, which the Connector then consumes. **Run these commands on the primary database, not the read replica.** The heartbeat table and scheduled job must be created on the primary database. The changes will automatically replicate to the read replica. The `pg_cron` extension must be allowed in your database's configuration. See your provider's documentation for enabling extensions: Scheduling maintenance with pg\_cron Scheduling maintenance with pg\_cron PostgreSQL extensions PostgreSQL extensions pg\_cron extension pg\_cron extension Supported extensions Once allowed, create the extension: ```sql SQL theme={null} CREATE EXTENSION IF NOT EXISTS pg_cron; ``` ```sql SQL theme={null} -- Create the streamkap schema CREATE SCHEMA IF NOT EXISTS streamkap; -- Create the heartbeat table CREATE TABLE streamkap.streamkap_heartbeat ( id SERIAL PRIMARY KEY, text TEXT, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Insert the initial row INSERT INTO streamkap.streamkap_heartbeat (text) VALUES ('test_heartbeat'); ``` ```sql SQL theme={null} -- Schedule a job to update the heartbeat table every minute SELECT cron.schedule( 'streamkap_heartbeat_job', '*/1 * * * *', $$UPDATE streamkap.streamkap_heartbeat SET text = 'updated_heartbeat', last_update = CURRENT_TIMESTAMP WHERE id = 1;$$ ); ``` Whichever database user is used to create and run the cron jobs (often the `postgres` user or a dedicated cron user) needs appropriate permissions on the heartbeat table. Additionally, the Streamkap user also needs permissions to monitor the heartbeat table. ```sql SQL theme={null} GRANT USAGE ON SCHEMA streamkap TO {cron user}; GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO {cron user}; -- Grant permissions to the Streamkap user for monitoring and diagnostics GRANT USAGE ON SCHEMA streamkap TO streamkap_user; GRANT SELECT, UPDATE, INSERT, DELETE ON streamkap.streamkap_heartbeat TO streamkap_user; ``` The `pg_cron` extension stores job execution history in the table `cron.job_run_details`. To prevent this table from growing indefinitely: ```sql SQL theme={null} SELECT cron.schedule( 'streamkap_cron_cleanup', '0 0 * * *', $$DELETE FROM cron.job_run_details WHERE end_time < now() - interval '7 days';$$ ); ``` ```sql SQL theme={null} -- View all scheduled jobs SELECT * FROM cron.job; -- View recent job execution history SELECT * FROM cron.job_run_details ORDER BY start_time DESC LIMIT 10; -- Unschedule a job (replace {jobid} with the actual job ID) SELECT cron.unschedule({jobid}); ``` **Heartbeat tables and PostgreSQL publications** If the `streamkap_pub` publication was created for specific tables (e.g., `CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, ...;`) instead of `FOR ALL TABLES`, you **must** add the heartbeat table to the publication: ```sql SQL theme={null} ALTER PUBLICATION streamkap_pub ADD TABLE streamkap.streamkap_heartbeat; ``` ### 6. Create Publication & Slot Publications contain a set of change events for the tables you want the Connector to capture. * Create a publication for your tables. You can create a publication for all tables or selected tables. ```sql SQL - All Tables theme={null} -- Create a publication for all tables to capture CREATE PUBLICATION streamkap_pub FOR ALL TABLES; ``` ```sql SQL - Selected Tables theme={null} -- Create a publication for specific tables to capture CREATE PUBLICATION streamkap_pub FOR TABLE table1, table2, table3, ...; -- Verify the tables to capture were added to the publication SELECT * FROM pg_publication_tables where pubname = 'streamkap_pub'; ``` **Altering publications** You cannot alter `FOR ALL TABLES` publications to include/exclude tables. If you set up a `FOR ALL TABLES` publication and later decide to change that, you have to drop the publication and create another to include specific tables e.g. `CREATE PUBLICATION ... TABLE table1, table2, table3, ...`. However, any change events that occur before the new publication's created will not be included in it, so a snapshot's required to ensure they are not missed by your Streamkap pipelines. You should also stop the [Source](https://app.streamkap.com/connectors/sources) *before* changing the publication. A replication slot represents a stream of change events the Connector reads from. * Create a replication slot. ```SQL SQL theme={null} -- Create a logical replication slot SELECT pg_create_logical_replication_slot('streamkap_pgoutput_slot', 'pgoutput'); -- Verify the replication slot is working (this may take a few moments to return the count) SELECT count(*) FROM pg_logical_slot_peek_binary_changes('streamkap_pgoutput_slot', null, null, 'proto_version', '1', 'publication_names', 'streamkap_pub'); ``` **Replication slot auto-creation** If the Connector cannot find the replication slot named in [Replication Settings](/supabase#4-replication-settings) during Streamkap Setup, it will attempt to create one automatically as it cannot read the database transaction logs without one. #### Excluding columns from replication By default a publication streams every column of each captured table. On **PostgreSQL 15+** you can attach a *column list* to a table in the publication so that only the listed columns are replicated — any column you leave out is excluded at the source and never enters the replication stream. ```SQL SQL - Selected Columns theme={null} -- Only the listed columns are published; every other column is excluded. -- Each table's list MUST include its replica identity (primary key), -- otherwise UPDATE/DELETE for that table cannot be replicated. CREATE PUBLICATION streamkap_pub FOR TABLE public.users (id, email, created_at, updated_at), public.orders (id, user_id, status, total), public.products (id, sku, name, price); -- Add or change a table's column list on an existing publication ALTER PUBLICATION streamkap_pub SET TABLE public.users (id, email, updated_at); -- Verify the published columns per table SELECT pubname, schemaname, tablename, attnames FROM pg_publication_tables WHERE pubname = 'streamkap_pub'; ``` * Requires **PostgreSQL 15 or later**. On earlier versions a publication always streams all columns. * Each table's column list **must include the table's replica identity** — its primary key, or the columns from `REPLICA IDENTITY USING INDEX` / `FULL`. Otherwise `UPDATE` and `DELETE` events for that table are not replicated. * Column lists are only valid with `FOR TABLE`, not `FOR ALL TABLES`. A table listed without parentheses publishes all of its columns, so you can mix restricted and unrestricted tables in one publication. A publication column list only governs **streamed (CDC) changes**. The initial snapshot reads columns directly from the table, so an excluded column can still appear in snapshot records. If a column must never be captured, exclude it from the snapshot as well, not only from the publication. *** ## Streamkap Setup Follow these steps to configure your new connector: ### 1. Create the Source * Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources). * Choose **Supabase**. ### 2. Connection Settings * **Name**: Enter a name for your connector. * **Hostname**: Specify the hostname. **Connection modes** Supabase offers several connection modes, including connection poolers. However, Streamkap requires a [**direct connection**](https://supabase.com/docs/guides/database/connecting-to-postgres#direct-connection) because logical replication relies on the `IDENTIFY_SYSTEM` command to retrieve essential replication information from the database. If another mode is used, the Connector will fail with errors related to `IDENTIFY_SYSTEM`. * **Port**: Default is `5432`. * **Connect via SSH Tunnel**: The Connector will connect to an SSH server in your network which has access to your database. This is necessary if the Connector cannot connect directly to your database. * See [SSH Tunnel](/ssh-tunnel) for setup instructions. * **Username**: Username to access the database. * **Password**: Password to access the database. * **Database**: Specify the database to stream data from. * **Read only**: Whether or not to use a read-only connection. Requires PostgreSQL version 13 or higher. * When connecting to a read replica, set this to **Yes** to support Streamkap snapshots. * **Heartbeats**: Enabled by default. * For **read-write** connections, configure a heartbeat table in the source database and set **Heartbeat Table Schema**. See [Heartbeats](#5-heartbeats) for setup instructions. * For **read-only** connections, configure a scheduled heartbeat job on the primary database using `pg_cron`, and include the heartbeat table in [Schema and Table Capture](#6-schema-and-table-capture). See [Heartbeats](#5-heartbeats) for setup instructions. ### 3. Snapshot Settings If you set Read only to **No**, you will need to create a snapshot signal table and give permissions to the `streamkap_user`. See [Enable Snapshots](/supabase#3-enable-snapshots) for setup instructions. * **Signal Table**: Full path to the signal table including schema and table name (e.g., `streamkap.streamkap_signal`). This table is used for incremental snapshotting. See [Enable Snapshots](#3-enable-snapshots) for setup instructions. ### 4. Replication Settings * **Replication Slot Name**: The name of the replication slot for the connector to use. Default is `streamkap_pgoutput_slot`. * **Publication Name**: The name of the publication for the connector to use. Default is `streamkap_pub`. ### 5. Advanced Parameters * **SSL mode**: Whether to use an encrypted connection to the PostgreSQL server. By default, it's required. * **Prefix with Database Name?**: Changes the format of topics to `DatabaseName_TopicName` * **Represent binary data as**: Specifies how the data for binary columns e.g. `blob`, `binary`, `varbinary` should be interpreted. Your destination for this data can impact which option you choose. Default is `bytes`. Click **Next**. ### 6. Schema and Table Capture * **Add Schemas/Tables**: Specify the schema(s) and table(s) for capture * You can bulk upload here. The format is a simple list of schemas and tables, with each entry on a new row. Save as a `.csv` file without a header. **CDC only captures base tables, not Views** Change Data Capture reads the PostgreSQL write-ahead log (WAL), which only records changes to physical tables. Database Views are query-time computations with no physical storage—they don't generate WAL entries. **What you cannot capture:** Views, temporary tables, unlogged tables, foreign tables (FDW), or system tables (information\_schema, pg\_catalog). **Solution:** Specify only the underlying base tables that feed your views. You can recreate the view logic in your destination or transformation layer. **Generated columns are not kept up to date** PostgreSQL [generated columns](https://www.postgresql.org/docs/current/ddl-generated-columns.html) (defined with `GENERATED ALWAYS AS (...) STORED`) are calculated by the database from other columns. PostgreSQL does **not** include them in the replication stream Streamkap reads, so live streaming cannot keep their values current. **We recommend excluding generated columns from capture.** In the Source's **Settings → Advanced** section, set **Column Selection Mode** to **Exclusion** and add the column to the **Column Exclusion List** (fully-qualified, e.g. `schema.table.column`). If you need the value at your destination, calculate it there — or with a [transform](/transforms) — from the columns it is derived from. **Snapshots:** An incremental snapshot (the watermarked **Filtered** and **Full** options) of a table that contains a generated column will **fail** unless the column is excluded. **Blocking** and **Parallel** snapshots are not affected, though any value they capture will become stale because streaming cannot update it. See [Snapshots & Backfilling](/snapshots). PostgreSQL 18 and later can optionally stream stored generated columns when the publication is created with `publish_generated_columns = stored`; earlier versions cannot. Click **Save**. ## Troubleshooting When upgrading the PostgreSQL database used by Streamkap, there are specific steps to prevent data loss and ensure continued operation. Streamkap handles network failures and outages well. If a monitored database stops, the connector resumes from the last recorded log sequence number (LSN) once communication is restored. It retrieves this offset and queries PostgreSQL for a matching LSN in the replication slot. A replication slot is required for change capture, but PostgreSQL removes slots during upgrades and doesn’t restore them. When the connector restarts, it requests the last known offset, but PostgreSQL cannot return it. Creating a new replication slot isn't enough to prevent data loss. New slots only track changes from their creation point and lack earlier offsets. The connector fetches its last known offset from Kafka but can't retrieve corresponding data from the new slot. It skips older change events and resumes from the latest log position, causing silent data loss with no warnings. ### Procedure Follow these steps to minimize data loss. Note that a few steps may require support from Streamkap. We recommend notifying us about your database upgrade ahead of time to ensure you have the necessary support. * Using your database's upgrade procedure, ensure writes to it have stopped. * Allow the connector to capture all change events before starting the upgrade procedure. Ask Streamkap to confirm this for you. **Interrupting change data capture** If all the events were not captured before stopping the Source and upgrading the database, you can perform a snapshot (after the procedure is completed) in Streamkap for that Source to ensure no change events were missed. * Assuming all events are captured, stop the Source in the Streamkap app. This flushes the last records and saves the last offset. * Stop the database and upgrade it using your upgrade procedure. Once the database is upgraded, and **before** allowing writes again: * Recreate the logical replication slot, otherwise, Streamkap will miss changes. See [Create Publication & Slot](/supabase#4-create-publication--slot). * Verify the publication for Streamkap exists; recreate it if necessary: [Create Publication & Slot](/supabase#4-create-publication--slot). **Update the Source in the Streamkap app** If you have chosen different names for the logical replication slot or publication, make sure to update them in the Streamkap setup page for the relevant PostgreSQL Source. In this case, contact Streamkap, as they may need to reset your Connector's offsets in this scenario. * Restore write access to the database. * Resume or restart the Source in the Streamkap app. Introduced in PostgreSQL 13, the `REPLICA IDENTITY` table setting controls what data is logged for row updates and deletes. By default, only the primary key and Streamkap metadata column values are retained for deleted records. All other columns will be empty. This leaves you with an incomplete record. If you require - for auditing and historical tracking purposes - **all** column values for deleted records, or if your deletion strategy for your destination is 'soft deletes' (retain the deleted record with a deletion flag), you should set the `REPLICA IDENTITY` to `FULL` for all capture tables. ```SQL SQL theme={null} ALTER TABLE {table} REPLICA IDENTITY FULL; ``` This ensures complete data retention. For capturing partitioned tables, it's essential to enable `publish_via_partition_root` on the publication. By default, changes to partitions are published from the partition itself. The connector expects changes to come from the root table. To ensure compatibility and consistent replication, enable this setting: `ALTER PUBLICATION streamkap_pub SET (publish_via_partition_root = true);` # Tagging Source: https://docs.streamkap.com/tagging Organize and categorize entities easily in Streamkap Tags help streamline workflows by grouping related items based on custom-defined attributes. ## Tag Management The **Tag Management** feature allows you to create, categorize, and manage tags for organizing entities in the Streamkap app. Start by navigating to the [Tag Management](https://app.streamkap.com/tags) section. ### Tag Categories and Types Tags are categorized into different types, which can be used to organize entities effectively. Currently available **categories** are: * `System`: Applies to any tags and types predefined by Streamkap. * `Custom`: Applies (automatically) to any tag you create. Currently available **types** are: * `Pipelines`: Specific to your data pipelines. * `Environment`: Predefined by Streamkap, for indicating different environments (e.g. `Production` and `Development`). ### Browsing Tags 1. Use the **Search Bar** to find existing tags by name, ID, type, or description. 2. Use the **Filter by Tag Type** dropdown to refine your view of tags based on their categories. Tag Management list showing tags with name, description, type and creation date ### Creating a New Tag To create a new tag, follow these steps: 1. Click the **Add** button in the top-right corner. 2. A modal titled **Add Tag** will appear. 3. Fill in the required fields: * **Tag Name (Required)**: Enter a unique name for the tag. * **Description (*Optional*)**: Provide additional details about the tag. * **Tag Types (Required)**: Select one or more predefined tag types from the dropdown. 4. Click `Save` to create the tag, or `Cancel` to discard changes. Add Tag dialog with name, description, and tag type fields ### Managing Existing Tags * Tags are displayed in a list format with their type and description. * Some system-defined tags may be **Read-Only** and cannot be modified. * To edit or delete a tag, use the **actions menu (⋮)** next to the tag entry. ## Pipeline Tagging The **Update Tags** feature allows you to efficiently update tags on multiple pipelines at once. You can also search and filter pipelines by their tags. Start by navigating to the [Pipelines](https://app.streamkap.com/pipelines) section. ### Searching and Filtering Pipelines by Tags 1. Select one or more tags from the **Tags** filter. 2. Reduce or expand the results by changing the **Operator** `AND` or `OR` (e.g. `Production AND Management`, `Development OR Finance OR Analytics`). 3. Click `Apply` to apply the filter. Pipelines tags filter showing tag selection with AND/OR operator options ### Updating Tags for Multiple Pipelines 1. Select one or more pipelines by checking the box next to their name. 2. The **Update Tags** button will become active (it is greyed out until at least one pipeline is selected). 3. Click **Update Tags** to open the bulk update modal. 4. In the modal: * Use the **Select Tags** dropdown to add new tags. * Tags already applied will be visible and can be removed if needed. * A warning indicates that saving will overwrite all existing tags on the selected pipelines with the new selection. 5. Click **Save** to apply the changes, or **Cancel** to discard them. **Bulk updates overwrite existing tags**, meaning all previous tags will be removed and replaced with the newly selected ones. Bulk Update Tags dialog for updating tags on selected pipelines ### Updating Tags for a Single Pipeline * Click the **actions menu (⋮)** next to a pipeline. * Select **Update Tags** from the menu. * This will open the same tag update modal, but only for the selected pipeline. ## Best Practices * Use a **consistent naming convention** for tags to ensure clarity. * Leverage the **description field** to provide context for each tag. # Users Source: https://docs.streamkap.com/team-management Manage team members, roles, and access permissions for your organization Team Management can be found in **Account preferences** under the **Workspace** section. Click your avatar in the top navigation bar and select **Account preferences** to access these settings. ## Users Invite team members and manage their access to your Streamkap organization. Users list showing team members with their email, roles, and status ### Inviting Users 1. Click **Invite User** to add new team members 2. Enter their email address 3. Assign appropriate roles 4. The user will receive an email invitation ## Roles The following roles are currently supported: * **Admin** - Full access to everything * **Data Admin** - Can create/edit sources/destinations/pipelines but does not have admin access * **Read Only** - Read only access to everything * **Sub-Accounts** - Can create/edit sub accounts * **Team Management** - Can add/edit users * **Billing** - Access to subscription page only ## Groups Groups can be used for organizing collections of users, such as Admins, Read Only, or external collaborators. Groups management page for organizing users To create a new group, click **Add new group** and configure the group name and members. ## Deleting Users ### Standard Users Users that were manually invited through the Streamkap UI can be deleted directly from the Users page by an Admin. ### SCIM-Provisioned Users If your organization uses [SSO](/sso) with SCIM provisioning, some users may show as **Managed externally** in the **Managed by** column. These users were provisioned via SCIM (System for Cross-domain Identity Management), which means your Identity Provider (IdP) is the source of truth for their lifecycle. SCIM-provisioned users cannot be deleted through the standard admin flow in Streamkap. The delete option will not appear for these users. To identify which users are SCIM-managed, enable the **Managed by** column in the Users table by clicking the column settings icon. SCIM-provisioned users will show as **Managed externally** in this column. **To remove SCIM-provisioned users, you have two options:** 1. **Deprovision or delete the users in your IdP** — the change will sync through SCIM and remove the user from Streamkap automatically 2. **Contact your Streamkap administrator** to manually remove the user via the Backoffice portal ## Log Out Sessions It's possible to force logout a user from their sessions. Learn more at [Personal Privacy & Security](/personal-privacy-security) and [Organisation Security](/organisation-security). Privacy & Security settings showing session management # Configuration Source: https://docs.streamkap.com/terraform-configuration Configure the Streamkap Terraform Provider with credentials and create your first resources. This guide walks you through configuring the Streamkap Terraform Provider and creating your first infrastructure-as-code configuration. ## Prerequisites Before you begin, ensure you have: * [Terraform installed](/terraform-installation) (version 1.0 or later) * A Streamkap account with access to a Project * API credentials (Client ID and Secret) from Streamkap ## Step 1: Create API Credentials Log in to your Streamkap account and navigate to your project's **Project Settings**. Click on the **API** tab to access API Tokens. Click **Create API Token**, enter a description (e.g., "Terraform automation"), select the appropriate role(s), and click **Create Token**. Copy the **Client ID** and **Token** immediately. The token is only shown once. Store your credentials securely. Never commit them to version control. ## Step 2: Configure Credentials Set your credentials as environment variables. This is the recommended approach for security. ```bash theme={null} export STREAMKAP_CLIENT_ID="your-client-id" export STREAMKAP_SECRET="your-token-secret" ``` To persist these across terminal sessions, add them to your `~/.bashrc`, `~/.zshrc`, or equivalent shell configuration file. ```powershell theme={null} $env:STREAMKAP_CLIENT_ID = "your-client-id" $env:STREAMKAP_SECRET = "your-token-secret" ``` For persistent environment variables, set them via System Properties or your PowerShell profile. ```batch theme={null} set STREAMKAP_CLIENT_ID=your-client-id set STREAMKAP_SECRET=your-token-secret ``` ## Step 3: Create Your Terraform Configuration Create a new directory for your Terraform project and add the following `main.tf` file. The beta version exposes significantly more resources — all source and destination connectors, transform resources, and more. ```hcl theme={null} terraform { required_providers { streamkap = { source = "streamkap-com/streamkap" version = "3.0.0-beta.6" } } } provider "streamkap" { # Credentials are loaded from environment variables: # STREAMKAP_CLIENT_ID and STREAMKAP_SECRET } ``` Pre-release versions require an exact version constraint — Terraform does not auto-select pre-release versions with `>=` or `~>` operators. The stable version supports a subset of connectors. See the [Resource Reference](/terraform-resources) for details. ```hcl theme={null} terraform { required_providers { streamkap = { source = "streamkap-com/streamkap" version = "~> 2.1.19" } } } provider "streamkap" { # Credentials are loaded from environment variables: # STREAMKAP_CLIENT_ID and STREAMKAP_SECRET } ``` The provider automatically reads credentials from the `STREAMKAP_CLIENT_ID` and `STREAMKAP_SECRET` environment variables. You can also set them directly in the provider block, but this is not recommended for security reasons. ## Step 4: Initialize and Apply Run the following commands in your project directory: ```bash theme={null} # Initialize Terraform (downloads the provider) terraform init # Preview what will be created terraform plan # Apply the configuration (when you have resources defined) terraform apply ``` ## Provider Configuration Reference The Streamkap provider supports the following configuration options: | Parameter | Environment Variable | Default | Description | | ----------- | --------------------- | --------------------------- | ------------------------------- | | `client_id` | `STREAMKAP_CLIENT_ID` | - | Your Streamkap API client ID | | `secret` | `STREAMKAP_SECRET` | - | Your Streamkap API token/secret | | `host` | `STREAMKAP_HOST` | `https://api.streamkap.com` | API endpoint URL | ## Project Structure Recommendation For larger projects, organize your Terraform files like this: ``` streamkap-terraform/ ├── main.tf # Provider configuration ├── variables.tf # Variable definitions ├── sources.tf # Source connector resources ├── destinations.tf # Destination connector resources ├── pipelines.tf # Pipeline resources ├── outputs.tf # Output values ├── terraform.tfvars # Variable values (add to .gitignore!) └── .gitignore # Exclude sensitive files ``` Here is an example `.gitignore` file you can use to exclude sensitive files: ``` # Terraform state files (contain sensitive data) *.tfstate *.tfstate.* # Terraform variable files with secrets terraform.tfvars *.auto.tfvars # Terraform cache .terraform/ # Crash logs crash.log crash.*.log ``` ## Next Steps * Browse the [Resource Reference](/terraform-resources) for all available Streamkap resources * Browse the [Streamkap Terraform Provider](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs) on the HashiCorp Registry for full attribute documentation * Learn about [Terraform state management](https://developer.hashicorp.com/terraform/language/state) for team workflows * See [API Tokens](/api-tokens) for more details on managing Streamkap credentials # Installing Terraform Source: https://docs.streamkap.com/terraform-installation How to install Terraform on macOS, Windows, and Linux using popular package managers. Before using the Streamkap Terraform Provider, you need to install the Terraform CLI on your machine. ## macOS ### Homebrew (Recommended) [Homebrew](https://brew.sh/) is the most popular package manager for macOS. ```bash theme={null} # Add the HashiCorp tap brew tap hashicorp/tap # Install Terraform brew install hashicorp/tap/terraform # Verify installation terraform --version ``` To upgrade Terraform later, run `brew upgrade hashicorp/tap/terraform` ### MacPorts If you use [MacPorts](https://www.macports.org/), Terraform is available in the ports collection: ```bash theme={null} sudo port install terraform # Verify installation terraform --version ``` ## Windows ### Chocolatey (Recommended) [Chocolatey](https://chocolatey.org/) is a popular package manager for Windows. ```powershell theme={null} # Run in an elevated PowerShell prompt (Run as Administrator) choco install terraform # Verify installation terraform --version ``` ### Scoop [Scoop](https://scoop.sh/) is another package manager for Windows: ```powershell theme={null} scoop install terraform # Verify installation terraform --version ``` ### Manual Installation 1. Download the appropriate package from the [Terraform downloads page](https://developer.hashicorp.com/terraform/downloads) 2. Extract the zip archive 3. Move `terraform.exe` to a directory in your system PATH (e.g., `C:\Windows\System32` or create a dedicated directory) 4. Open a new command prompt and verify: `terraform --version` ## Linux ### Ubuntu/Debian (APT) ```bash theme={null} # Add HashiCorp GPG key wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg # Add the official HashiCorp repository echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list # Update and install sudo apt update && sudo apt install terraform # Verify installation terraform --version ``` ### RHEL/CentOS/Fedora (DNF/YUM) ```bash theme={null} # Add HashiCorp repository sudo dnf config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo # Install Terraform sudo dnf install terraform # Verify installation terraform --version ``` ## Troubleshooting Ensure the Terraform binary is in your system `PATH`. You may need to restart your terminal or shell session after installation. If you encounter permission errors, ensure you're using `sudo` where required, or check that your user has write access to the installation directory. If you have an older version installed via a different method, uninstall it first before using a package manager. For Homebrew: `brew unlink terraform` before installing the HashiCorp tap version. # Resource Reference Source: https://docs.streamkap.com/terraform-resources Quick reference for all Streamkap Terraform Provider resources. All resources are documented with full attribute details and examples on the [HashiCorp Registry](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs). ## Sources | Resource | Terraform Type | Registry Docs | | -------------- | -------------------------------- | -------------------------------------------------------------------------------------------------------------------- | | PostgreSQL | `streamkap_source_postgresql` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/source_postgresql) | | MySQL | `streamkap_source_mysql` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/source_mysql) | | MongoDB | `streamkap_source_mongodb` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/source_mongodb) | | DynamoDB | `streamkap_source_dynamodb` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/source_dynamodb) | | SQL Server | `streamkap_source_sqlserver` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/source_sqlserver) | | Kafka Direct | `streamkap_source_kafkadirect` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/source_kafkadirect) | | AlloyDB | `streamkap_source_alloydb` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/source_alloydb) | | DB2 | `streamkap_source_db2` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/source_db2) | | DocumentDB | `streamkap_source_documentdb` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/source_documentdb) | | Elasticsearch | `streamkap_source_elasticsearch` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/source_elasticsearch) | | MariaDB | `streamkap_source_mariadb` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/source_mariadb) | | MongoDB Hosted | `streamkap_source_mongodbhosted` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/source_mongodbhosted) | | Oracle | `streamkap_source_oracle` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/source_oracle) | | Oracle AWS | `streamkap_source_oracleaws` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/source_oracleaws) | | PlanetScale | `streamkap_source_planetscale` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/source_planetscale) | | Redis | `streamkap_source_redis` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/source_redis) | | S3 | `streamkap_source_s3` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/source_s3) | | Supabase | `streamkap_source_supabase` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/source_supabase) | | Vitess | `streamkap_source_vitess` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/source_vitess) | | Webhook | `streamkap_source_webhook` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/source_webhook) | ### Adding Tables to an Existing Source To add tables to an existing CDC source via Terraform, use the `table_include_list_user_defined` attribute. This corresponds to the `table.include.list.user.defined` API parameter and lets you append tables without modifying the original `table_include_list`. When you apply the updated configuration, Streamkap adds the specified tables to the source. If the pipeline has `snapshot_new_tables = true`, the new tables are automatically snapshotted. ```hcl theme={null} resource "streamkap_source_postgresql" "example" { name = "my-postgres-source" database_hostname = var.hostname database_port = 5432 database_user = var.username database_password = var.password database_dbname = "mydb" database_sslmode = "require" schema_include_list = "public" table_include_list = "public.orders,public.customers" # Add new tables to an existing source without modifying the original list table_include_list_user_defined = "public.products,public.inventory" signal_data_collection_schema_or_database = "streamkap.streamkap_signal" slot_name = "streamkap_slot" publication_name = "streamkap_pub" } ``` The same approach works for MySQL, SQL Server, Oracle, and Db2 sources -- use `table_include_list_user_defined` on the corresponding resource type. For **Kafka Direct** sources, use `topic_include_list_user_defined` instead. ## Destinations | Resource | Terraform Type | Registry Docs | | ------------ | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | Snowflake | `streamkap_destination_snowflake` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/destination_snowflake) | | Databricks | `streamkap_destination_databricks` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/destination_databricks) | | ClickHouse | `streamkap_destination_clickhouse` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/destination_clickhouse) | | PostgreSQL | `streamkap_destination_postgresql` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/destination_postgresql) | | S3 | `streamkap_destination_s3` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/destination_s3) | | Iceberg | `streamkap_destination_iceberg` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/destination_iceberg) | | Kafka | `streamkap_destination_kafka` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/destination_kafka) | | Azure Blob | `streamkap_destination_azblob` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/destination_azblob) | | BigQuery | `streamkap_destination_bigquery` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/destination_bigquery) | | CockroachDB | `streamkap_destination_cockroachdb` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/destination_cockroachdb) | | DB2 | `streamkap_destination_db2` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/destination_db2) | | GCS | `streamkap_destination_gcs` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/destination_gcs) | | HTTP Sink | `streamkap_destination_httpsink` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/destination_httpsink) | | Kafka Direct | `streamkap_destination_kafkadirect` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/destination_kafkadirect) | | Motherduck | `streamkap_destination_motherduck` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/destination_motherduck) | | MySQL | `streamkap_destination_mysql` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/destination_mysql) | | Oracle | `streamkap_destination_oracle` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/destination_oracle) | | Pinecone | `streamkap_destination_pinecone` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/destination_pinecone) | | R2 | `streamkap_destination_r2` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/destination_r2) | | Redis | `streamkap_destination_redis` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/destination_redis) | | Redshift | `streamkap_destination_redshift` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/destination_redshift) | | SQL Server | `streamkap_destination_sqlserver` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/destination_sqlserver) | | Starburst | `streamkap_destination_starburst` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/destination_starburst) | | Weaviate | `streamkap_destination_weaviate` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/destination_weaviate) | ## Transforms | Resource | Terraform Type | Registry Docs | | ------------ | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | | Map Filter | `streamkap_transform_map_filter` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/transform_map_filter) | | Enrich | `streamkap_transform_enrich` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/transform_enrich) | | Enrich Async | `streamkap_transform_enrich_async` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/transform_enrich_async) | | SQL Join | `streamkap_transform_sql_join` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/transform_sql_join) | | Rollup | `streamkap_transform_rollup` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/transform_rollup) | | Fan Out | `streamkap_transform_fan_out` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/transform_fan_out) | ## Pipelines & Topics | Resource | Terraform Type | Registry Docs | | -------- | -------------------- | -------------------------------------------------------------------------------------------------------- | | Pipeline | `streamkap_pipeline` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/pipeline) | | Topic | `streamkap_topic` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/topic) | | Tag | `streamkap_tag` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/resources/tag) | ## Data Sources | Data Source | Terraform Type | Registry Docs | | ------------- | ------------------------------ | ---------------------------------------------------------------------------------------------------------------- | | Tag | `data.streamkap_tag` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/data-sources/tag) | | Transform | `data.streamkap_transform` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/data-sources/transform) | | Topic | `data.streamkap_topic` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/data-sources/topic) | | Topics (list) | `data.streamkap_topics` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/data-sources/topics) | | Topic Metrics | `data.streamkap_topic_metrics` | [View →](https://registry.terraform.io/providers/streamkap-com/streamkap/latest/docs/data-sources/topic_metrics) | # Topics Source: https://docs.streamkap.com/topics Browse topics, inspect data, and configure partitions Topics hold data flowing between your sources and destinations. The Topics page lets you browse them, inspect their data, view metrics, and configure partition settings. ## Overview The Topics page displays a searchable list of all Kafka topics in your project with filtering options and metrics: The left sidebar provides multiple ways to filter and organize your topics: * **Collections**: View All Topics, or group By Source, By Destination, or By Transform * **Format**: Filter by message serialization format (JSON, Avro, etc.) * **Error Status**: Filter by error state to quickly identify topics with issues The top toolbar provides additional controls: * **Search Box**: Filter topics by name * **Include DLQ**: Toggle to show or hide dead letter queue topics * **All Entities**: Filter by specific source or destination connectors * **Chart Button**: Toggle the metrics chart display Click the **Chart** button to display an interactive time series chart: * **Time Range Options**: Last 24 hours, Last 7 days, Last 30 days * **Metric Selection**: Toggle between Volume, Errors, and Events * **Data Granularity**: Hourly data points for 24-hour view, daily aggregates for longer periods * **Hover Tooltips**: View exact values at specific time points The main table lists all topics with these columns: **Topic**, **Source**, **Destination**, **Volume**, **Errors**, **Written**, **Lag**, **Latency**, and **Last Message**. Click any topic row to open the details panel. Navigate through topics with configurable page size (10, 20, 50, or 100 per page). Streamkap automatically creates a dead letter queue topic for each destination connector with the naming pattern: `destination_{entity_id}.streamkap.deadletterqueue` **Purpose:** * Captures messages that fail processing at the destination * Prevents pipeline stoppage due to individual message failures * Enables inspection and recovery of failed messages **Common Failure Causes:** * Schema mismatches between source and destination * Data type conversion errors * Constraint violations in destination tables * Malformed message payloads **Monitoring:** * Check the Errors metric on the main Topics page * Inspect DLQ topic messages in the Messages tab * Set up alerts for DLQ activity (see [Alerts](/alerts)) If messages appear in a DLQ topic, investigate the pipeline logs and destination connector settings to identify the root cause. Fix the issue, then consider replaying the failed messages if needed. For detailed information on managing DLQ records, see [DLQ Operations](/dlq-operations). ## Topic Naming Conventions Streamkap automatically names topics based on the source type and configuration. Understanding the naming convention helps you identify topics in the list view, configure filters, and set up destination mappings. | Source Type | Topic Name Format | Example | | ------------------------------------------------------ | ------------------------------------------------------------------------------------- | ------------------------------------------- | | CDC Sources (PostgreSQL, MySQL, Oracle, MariaDB, etc.) | `source_{entity_id}.{schema}.{table}` | `source_123.public.users` | | SQL Server | `source_{entity_id}.{database}.{schema}.{table}` | `source_123.mydb.dbo.orders` | | MongoDB / DocumentDB | `source_{entity_id}.{database}.{collection}` | `source_123.mydb.customers` | | Kafka Direct | `{topic_prefix}.{topic_name}` | `myprefix.events` | | Transforms | `transform_{id}_{version}.{topic_suffix}` (`topic_suffix` inherited from input topic) | `transform_456_1.public.users` | | DLQ (Destination) | `destination_{entity_id}.streamkap.deadletterqueue` | `destination_789.streamkap.deadletterqueue` | | DLQ (Transform) | `{transform_output_prefix}deadletterqueue` | `transform_456_1.deadletterqueue` | `entity_id` is the internal ID assigned by Streamkap when you create the source or destination. You can find it in the URL when viewing the connector in the Streamkap UI or by using the Quick Actions Menu -> Copy ID action. SQL Server uses a 4-part naming format because it supports multiple databases per connection, requiring the database name as an additional segment. ### Custom Topic-to-Table Mapping Destinations can map topics to custom table names using regex patterns. The `changeTopicName` transform setting allows you to rename topics before they reach the destination, which is useful when you want the destination table name to differ from the default topic-derived name. ## Topic Details Click any topic row to open the Topic Details panel: The panel displays the topic name, source link, key metrics (Written, Volume, Errors, Events, Lag, Latency), serialization format, and quick actions. Click **Browse Messages** to access the full topic detail page with three tabs. ### Messages Tab The Messages tab displays topic-specific metrics and a message browser for inspecting individual messages. **Metric Cards:** Written, Volume, Errors, Events, Lag, Latency, Last Message **Message Browser Controls:** * **Filter**: Apply seek or regex filters (see [Filtering Messages](#filtering-messages)) * **Refresh Button**: Reload messages from Kafka * **Sort Order**: Toggle between "Oldest First" and "Newest First" **Message Table Columns:** Offset, Partition, Timestamp, Key, Value (click to expand) Dead letter queue topics are typically empty unless messages have failed processing and been sent to the DLQ. ### Metadata Tab The Metadata tab displays comprehensive Kafka topic configuration and health information: Monitor the overall health and replication status of your topic: * **Status**: Overall topic health (Healthy, Degraded, or Unhealthy) * **Total Replicas**: Total number of replicas across all partitions * **In-Sync Replicas**: Number of replicas that are caught up with the leader * **Under-Replicated**: Number of replicas that are lagging behind the leader * **Offline Partitions**: Number of partitions without an active leader A healthy topic should have all replicas in-sync (In-Sync Replicas = Total Replicas) and zero under-replicated or offline partitions. View partition-level configuration and status: * **Total Partitions**: Number of partitions in the topic * **Replication Factor**: Number of replicas for each partition * **Under-Replicated**: Count of under-replicated partitions * **Offline Count**: Count of offline partitions **Individual Partitions Table** displays each partition with: Partition ID, Replicas (broker IDs), In-Sync Replicas, and Status. Control how long Kafka retains messages: * **Retention Time**: Time-based retention (e.g., `7d` for 7 days, `-1` for unlimited) * **Retention Bytes**: Size-based retention per partition (in bytes, `-1` for unlimited) * **Delete Retention**: Time to retain delete markers for log compaction * **Cleanup Policy**: Message cleanup strategy (`delete`, `compact`, or `delete,compact`) For CDC topics, a retention time of 7 days is typically sufficient for most use cases. Adjust based on your downstream consumer lag tolerance and recovery requirements. View message handling and compression settings: * **Max Message Size**: Maximum size of a single message (default: 64 MB) * **Compression Type**: Compression algorithm (`lz4`, `gzip`, `snappy`, `zstd`, or `none`) * **Min In-Sync Replicas**: Minimum replicas that must acknowledge writes for success * **Timestamp Type**: Source of message timestamps (`CreateTime` or `LogAppendTime`) Setting Min In-Sync Replicas too high can cause write failures if brokers are unavailable. A value of 2 balances durability and availability for a replication factor of 3. **Segment Configuration:** * **Segment Time**: Maximum time before creating a new log segment (e.g., `1h`) * **Segment Size**: Maximum size of a log segment before rolling (e.g., `1024 MB`) **Other Configuration:** * **Unclean Leader Election**: Whether to allow out-of-sync replicas to become leaders (`true` prioritizes availability with data loss risk, `false` prioritizes durability) Smaller segments enable finer-grained retention and compaction but increase overhead. For high-throughput CDC topics, segment times of 1 hour and sizes around 1 GB are typical. ### Settings Tab The Settings tab provides partition configuration management: **Partition Count Configuration:** * **Slider Control**: Drag to increase partition count * **Current Value Display**: Shows the active partition count * **Minimum Value**: Cannot be lower than current count (Kafka limitation) * **Save Button**: Apply partition changes **Increasing partitions will cause messages to be read out of order and may reorganize destination tables.** See [Partition Management](#partition-management) for the safe procedure. ## Filtering Messages The Filter Messages feature allows you to navigate to specific positions in the topic or search for messages matching patterns. Click the **Filter** button to open the filter dialog. Topic Messages Filter Dialog The filter dialog provides two types of filters that can be used independently or combined: Jump to a specific position in the topic: **Offset Seek:** * Select **Offset** from the dropdown and enter a numeric offset value * Useful for investigating specific message sequences or resuming from a known position Seek by Offset **Timestamp Seek:** * Select **Timestamp** from the dropdown and choose a date/time * Jumps to the first message at or after the selected timestamp Seek by Timestamp Seek filters navigate to a position in the topic but do not filter out messages. All messages from that position onward will be displayed. Search for messages matching a regular expression pattern: **Search In** - Select which message fields to search: * **key**: Search in message keys only * **value**: Search in message payload only * **headers**: Search in Kafka headers (op, \_\_db, etc.) * **Select All**: Search in all fields Select Search Fields **Regex Pattern** - Enter a regular expression pattern to match (e.g., `.*cancelled.*`): Regex Pattern Example **Performance Consideration:** Regex search requires scanning messages sequentially and may take significantly longer or timeout for topics with millions of messages. Use Seek Filter first to narrow the search range. **Apply Filter:** 1. Configure your desired Seek Filter and/or Regex Filter 2. Click **Apply** to activate the filter 3. A "Filter applied" badge appears with filter management buttons **Remove Filter:** * Click the **X** button next to "Filter applied" to clear all active filters * Removing the filter returns to the default view **Filter Combinations:** * **Seek Only**: Navigate to a position without filtering messages * **Regex Only**: Search from the beginning of the topic * **Seek + Regex**: Navigate to a position, then search from there (recommended for large topics) **Best Practices:** Use Offset Seek when you know the exact message position. Use Timestamp Seek for time-based investigations. Combine Timestamp Seek with Regex Filter to search within specific time windows. ## Partition Management ### When to Increase Partitions Consider increasing partitions when: * **Throughput bottlenecks**: Single partition cannot handle message volume * **Parallelism needed**: Want multiple consumers to process messages concurrently * **Scaling destinations**: Destination can benefit from parallel writes * **Faster snapshots/backfills**: More partitions allow the destination to write snapshot data in parallel (must also increase destination tasks to match). See [Topic Partitioning](/performance-tuning#topic-partitioning) for details. Follow this 8-step procedure to safely increase partitions: Pause all source connectors writing to this topic to prevent new messages. Ensure all in-flight messages have been consumed and processed. Pause destination connectors consuming from this topic. For most destinations, truncate the target table to prepare for potential re-ingestion. **Skip this step if using ClickHouse or Snowflake.** Use the Settings tab slider to increase the partition count and click Save. Resume source connectors to begin writing messages to the new partition layout. For some destinations, trigger a snapshot to re-sync data. **Skip this step if using ClickHouse or Snowflake.** Resume destination connectors to consume messages from all partitions. Failing to follow the safe partition increase procedure can result in duplicate records, out-of-order data processing, inconsistent table state, and data quality issues. **ClickHouse:** * Uses `ReplacingMergeTree` engine with background merging * Handles out-of-order records automatically through de-duplication * **Does not require** table truncation or snapshot triggering **Snowflake:** * Uses Dynamic Tables or dbt with sorting by `_streamkap_ts_ms DESC, _streamkap_offset DESC` * Post-ingestion de-duplication handles out-of-order records * **Does not require** table truncation or snapshot triggering **Other Destinations (BigQuery, Databricks, Redshift, etc.):** * **Require the full 8-step procedure** to maintain data consistency If you are experiencing performance issues or lag with your topics, adjustments to poll records, partition counts, and task parallelism can help. See [Performance Tuning](/performance-tuning) for detailed guidance on optimizing pipeline throughput and latency. ## Troubleshooting 1. **Check Connector Status**: Ensure the source or destination connector is running 2. **Verify Topic Creation**: Some topics are created only after first message write 3. **Refresh the Page**: Click the refresh button to reload the topic list 4. **Check Kafka**: Use Kafka Access to verify the topic exists in Kafka directly 1. **Inspect DLQ Messages**: Navigate to the dead letter queue topic and review failed messages 2. **Check Pipeline Logs**: Review logs for the associated pipeline ([Logs](/logs)) 3. **Review Destination Settings**: Verify destination schema and configuration 4. **Check for Alerts**: Review alerts for this topic and associated connectors ([Alerts](/alerts)) 5. **Validate Schema**: Ensure source schema matches destination expectations 1. **Check Save Button**: Ensure you clicked Save after adjusting the slider 2. **Verify Kafka State**: Use Kafka Access to confirm partition count in Kafka 3. **Review Logs**: Check project logs for partition update errors 4. **Kafka Limitations**: Remember that partition count can only increase, never decrease 5. **Wait for Propagation**: Some changes may take a few moments to reflect in the UI 1. **Check Time Range**: Ensure messages exist in the displayed time range 2. **Verify Message Production**: Confirm the source is actively writing messages 3. **Check Partition Filter**: Remove any partition filters that may be hiding messages 4. **Refresh Data**: Click the refresh button to reload messages from Kafka 5. **Inspect Kafka Directly**: Use Kafka Access to verify messages exist in the topic 1. **Monitor DLQ Topics**: Set up alerts for dead letter queue activity to catch processing failures quickly 2. **Plan Partition Changes**: Schedule partition increases during maintenance windows when data pipelines can be paused 3. **Use Appropriate Partition Counts**: Start with fewer partitions and scale up based on throughput needs 4. **Inspect Failed Messages**: Regularly review DLQ topics to identify recurring issues 5. **Document Partition Changes**: Record partition increase operations and their impact on downstream systems 6. **Test Before Scaling**: If possible, test partition increases in a non-production environment first 7. **Monitor Topic Metrics**: Regularly check topic metrics to detect lag or errors early # Enrich Source: https://docs.streamkap.com/transform-enrich Lookup values from a cached topic to enrich records. Handles schema evolution and pass-through fields. ## Setup * Navigate to [Create Transform](https://app.streamkap.com/transforms/create). * Choose **Enrich**. ### 1. Settings * **Name:** Enter a name for the transform e.g. filter out empty records * **Language:** Select the preferred (if supported) language to define the transform. * **Input Pattern:** Enter a regular expression pattern to list the topics this transform is applied to e.g. `source_abcd1234.public.orders`,`source_abcd1234\.public\.order.*`. * **Output Pattern:** Enter the desired output topic name including schema e.g. `enriched.orders`. * **Transform Parallelism:** The degree of parallelism for this transform. Determines number of tasks this transform should use when executing the transform logic. The default should be more than sufficient in most cases. ### 2. Advanced Parameters * **Input Serialization:** The format (e.g. `JSON`, `AVRO`, `STRING`, `BYTES`) of the input data. The default (`Any`) will try determine the format automatically. * **Output Serialization:** The format (e.g. `JSON`, `AVRO`, `STRING`, `BYTES`) of the output data. The default (`Any`) will try determine the format automatically. Click **Save**. ## Implementation Now you need to define the logic for your transform and deploy it. * Go to the **Implementation** tab. ### 1. Objective / Goal * Describe in a short sentence the purpose of the transform and its expected output. ### 2. Input / Output Topics These take the **Input Pattern** and **Output Pattern** entered earlier. You can also amend the input tables, and see a preview of the input topics captured by the input pattern as well as the corresponding output topic. **No topics in preview** If no topics are shown, review and amend your input topic patterns. The [topics page](https://app.streamkap.com/topics) shows the full names of topics, usually in three parts. Starting your pattern with `.*` ignores the first part (usually the Connector's ID), so you can focus your pattern on matching the schema (middle) and table (last) parts. * **Output Topic Replacement:** Amend the desired output topic name using a regular expression pattern. * **Input Topic Pattern for Main Table:** Enter a regular expression pattern to list the main (to be enriched) topic * **Input Topic Pattern for Lookup Table:** Enter a regular expression pattern to list the lookup (for enrichment) topic Click **Save Topics Pattern / Replacement**. ### 3. Transform Logic Here is where you define the logic for the transform. #### Enrich A number of editable SQL scripts are generated. There are 2 types of SQL script involved: the first type defines for the main table and lookup table the field(s) needed to join them together, and the other is the `ENRICH` query itself. * Click on each `.sql` script to edit it. Once you're happy with them, continue to the next section. ### 4. Deploy & Check Here you deploy your transform, but you can also perform diagnostic and validation checks at a click of a button. #### 4.0 Clear Output Topic This purges the output topic of data (if any) as well as any cached job state. A "reset" of the transform's previous outputs so you can start fresh. * **Output Topic pattern to clear:** Enter a regular expression pattern that captures the output topics to be cleared. Click **Clear Output Topics and Job State**. #### 4.1 Deploy When deploying a transform, you can decide from what point the transform starts processing the input topics data-assuming the data's still available. * **Replay Window:** * **Time-based**: Enter the **last** number of days, hours or minutes to process. * **Recency-based**: Enter `0` to process from the **latest** data, or, leave it blank/empty to process from the **earliest** data. Click **Deploy Transform**. #### 4.2 Job Status Check on the current status of the transform job. Click **Check Job Status**. #### 4.3 Output Topics Check what output topics were created (if any) and populated by the transform. Click **Check Output Topics**. #### 4.4 Errors Check if the transform jobs are failing and if so, what the problem is. Click **Check for Errors**. ## See Also * [Transform Ordering](/transform-ordering) - Understand how transforms are ordered and executed within a pipeline * [Transform Examples](/transform-examples) - Practical examples and recipes for common transform use cases # Enrich (Async) Source: https://docs.streamkap.com/transform-enrich-async Asynchronously call REST APIs to enrich incoming records using dynamic payloads. ## Setup * Navigate to [Create Transform](https://app.streamkap.com/transforms/create). * Choose **Enrich (Async)**. ### 1. Settings * **Name:** Enter a name for the transform e.g. filter out empty records * **Language:** Select the preferred (if supported) language to define the transform. * **Input Pattern:** Enter a regular expression pattern to list the topics this transform is applied to e.g. `source_abcd1234.public.orders`,`source_abcd1234\.public\.order.*`. * **Output Pattern:** Enter the desired output topic name including schema e.g. `enriched.orders`. * **Transform Parallelism:** The degree of parallelism for this transform. Determines number of tasks this transform should use when executing the transform logic. The default should be more than sufficient in most cases. ### 2. Advanced Parameters * **Input Serialization:** The format (e.g. `JSON`, `AVRO`, `STRING`, `BYTES`) of the input data. The default (`Any`) will try determine the format automatically. * **Output Serialization:** The format (e.g. `JSON`, `AVRO`, `STRING`, `BYTES`) of the output data. The default (`Any`) will try determine the format automatically. Click **Save**. ## Implementation Now you need to define the logic for your transform and deploy it. * Go to the **Implementation** tab. ### 1. Objective / Goal * Describe in a short sentence the purpose of the transform and its expected output. ### 2. Input / Output Topics These take the **Input Pattern** and **Output Pattern** entered earlier. You can also amend the input tables, and see a preview of the input topic(s) captured by the input pattern as well as the corresponding output topic. **No topics in preview** If no topics are shown, review and amend your input topic patterns. The [topics page](https://app.streamkap.com/topics) shows the full names of topics, usually in three parts. Starting your pattern with `.*` ignores the first part (usually the Connector's ID), so you can focus your pattern on matching the schema (middle) and table (last) parts. * **Input Topic Pattern:** Amend the desired input topic using a regular expression pattern. * **Output Topic Replacement:** Amend the desired output topic name using a regular expression pattern. Click **Save Topics Pattern / Replacement**. ### 3. Transform Logic Here is where you define the logic for the transform. #### Enrich (Async) When a record is processed by Streamkap from a data source, it's usually made up of at least 2 parts: key and value. * The **key** usually represents the primary key of the record i.e. a unique record identifier. * The **value** usually represents the rest of the record i.e. all other record fields/columns. Depending on your enrichment goal, you can define the logic for one or both of these functions: * "Value Transform" changes the message value * "Key Transform" changes the message key, in most pipelines the message key drives the primary/unique keys on the destination side * "Topic Transform" allows changing of the output topic name using more complex logic, for Enrich Async transform this should not be needed * "Common Transform" used to set up a common object that is passed between the different functions allowing sharing of functionality and avoid repetition If more exact output schema needs to be specified and the inferred schema does not exactly match your needs use: * "Value Schema Transform" to tweak the schema for some or all the output value fields * "Key Schema Transform" to tweak the schema for some or all the output key fields Please see [the list of data types supported by schema transforms](/transform-list-of-data-types) Once you're happy with them, continue to the next section. ### 4. Deploy & Check Here you deploy your transform, but you can also perform diagnostic and validation checks at a click of a button. #### 4.0 Clear Output Topic This purges the output topic of data (if any) as well as any cached job state. A "reset" of the transform's previous outputs so you can start fresh. * **Output Topic pattern to clear:** Enter a regular expression pattern that captures the output topics to be cleared. Click **Clear Output Topics and Job State**. #### 4.1 Deploy When deploying a transform, you can decide from what point the transform starts processing the input topics data-assuming the data's still available. * **Replay Window:** * **Time-based**: Enter the **last** number of days, hours or minutes to process. * **Recency-based**: Enter `0` to process from the **latest** data, or, leave it blank/empty to process from the **earliest** data. Click **Deploy Transform**. #### 4.2 Job Status Check on the current status of the transform job. Click **Check Job Status**. #### 4.3 Output Topics Check what output topics were created (if any) and populated by the transform. Click **Check Output Topics**. #### 4.4 Errors Check if the transform jobs are failing and if so, what the problem is. Click **Check for Errors**. ## See Also * [Transform Ordering](/transform-ordering) - Understand how transforms are ordered and executed within a pipeline * [Transform Examples](/transform-examples) - Practical examples and recipes for common transform use cases # Transform Examples Source: https://docs.streamkap.com/transform-examples Practical before-and-after examples for pipeline transforms and destination-side transforms. This page provides concrete data examples for each transform type. For setup instructions and configuration details, see the individual transform pages linked in each section. ## Pipeline Transforms (Flink) Pipeline transforms run in the Apache Flink streaming layer and are defined by you. They execute in the order you specify. See [Transform Ordering](/transform-ordering) for sequencing guidance. ### Filter Use [Transform / Filter Records](/transform-filter-records) to keep or discard records based on custom logic. In this example, only orders with `status` equal to `"completed"` pass through. **Before -- input record:** ```json theme={null} { "key": { "id": 1001 }, "value": { "id": 1001, "customer_id": 55, "status": "completed", "amount": 249.99, "created_at": "2025-11-02T14:30:00Z" } } ``` **After -- record passes the filter (status is "completed"):** ```json theme={null} { "key": { "id": 1001 }, "value": { "id": 1001, "customer_id": 55, "status": "completed", "amount": 249.99, "created_at": "2025-11-02T14:30:00Z" } } ``` A record that does **not** match the filter is dropped entirely and never reaches downstream transforms or the destination. **Dropped -- record does not pass the filter (status is "pending"):** ```json theme={null} { "key": { "id": 1002 }, "value": { "id": 1002, "customer_id": 78, "status": "pending", "amount": 59.00, "created_at": "2025-11-02T15:10:00Z" } } ``` This record is discarded. No output is produced for it. *** ### Enrich Use [Enrich (Async)](/transform-enrich-async) to call an external REST API and add data to each record. In this example, a product record is enriched with inventory data from a warehouse API. **Before -- input record:** ```json theme={null} { "key": { "product_id": "SKU-4421" }, "value": { "product_id": "SKU-4421", "name": "Wireless Keyboard", "category": "peripherals", "price": 79.99 } } ``` **After -- enriched with external API data:** ```json theme={null} { "key": { "product_id": "SKU-4421" }, "value": { "product_id": "SKU-4421", "name": "Wireless Keyboard", "category": "peripherals", "price": 79.99, "warehouse_stock": 342, "warehouse_location": "US-WEST-2", "last_restocked": "2025-10-28T08:00:00Z" } } ``` The three new fields (`warehouse_stock`, `warehouse_location`, `last_restocked`) were returned by the external API and merged into the record. You can also use the SQL-based [Enrich](/transform-enrich) transform to look up values from a cached Kafka topic instead of calling an external API. *** ### Fan Out Use [Fan Out](/transform-fanout) to route a single input record to different output topics based on field values. In this example, event records are routed to separate topics by their `event_type` field. **Before -- input record:** ```json theme={null} { "key": { "event_id": "evt-9001" }, "value": { "event_id": "evt-9001", "user_id": 204, "event_type": "purchase", "item": "Annual Plan", "amount": 199.00, "timestamp": "2025-11-03T09:22:00Z" } } ``` **After -- routed to the `events.purchases` output topic:** ```json theme={null} { "key": { "event_id": "evt-9001" }, "value": { "event_id": "evt-9001", "user_id": 204, "event_type": "purchase", "item": "Annual Plan", "amount": 199.00, "timestamp": "2025-11-03T09:22:00Z" } } ``` A different record with `"event_type": "page_view"` would be routed to a different output topic (e.g., `events.page_views`) based on the topic transform logic. The record data itself may remain unchanged -- it is the **output topic** that changes. *** ## Destination-Side Transforms (SMTs) Destination-side transforms run automatically on the destination connector as Single Message Transforms (SMTs). They execute in a fixed system-managed order after all pipeline transforms. See [Transform Ordering](/transform-ordering) for details on execution sequence. ### ToJsonJ / ToJsonbJ Converts a structured field (nested object, map, or array) into a JSON string representation. This is useful when your destination column expects a flat JSON string rather than a nested structure. **Before -- record with a nested `address` object:** ```json theme={null} { "customer_id": 55, "name": "Alice Martin", "address": { "street": "742 Evergreen Terrace", "city": "Springfield", "state": "IL", "zip": "62704" } } ``` **After -- `address` field converted to a JSON string:** ```json theme={null} { "customer_id": 55, "name": "Alice Martin", "address": "{\"street\":\"742 Evergreen Terrace\",\"city\":\"Springfield\",\"state\":\"IL\",\"zip\":\"62704\"}" } ``` The `address` field is now a single string containing the JSON representation of the original nested object. ToJsonJ and ToJsonbJ both convert structured fields to JSON strings. The ToJsonbJ variant produces PostgreSQL-compatible JSONB output. The differences may vary by destination -- choose the variant that matches your destination's expected column type. *** ### ToIntJ / ToFloatJ / ToStringJ Casts a field value from one type to another. Use these transforms when the source data type does not match the destination column type. **ToIntJ -- string to integer:** Before: ```json theme={null} { "order_id": 3001, "quantity": "42", "product": "Widget" } ``` After: ```json theme={null} { "order_id": 3001, "quantity": 42, "product": "Widget" } ``` The `quantity` field changed from the string `"42"` to the integer `42`. **ToFloatJ -- string to float:** Before: ```json theme={null} { "order_id": 3001, "total_price": "149.95", "currency": "USD" } ``` After: ```json theme={null} { "order_id": 3001, "total_price": 149.95, "currency": "USD" } ``` The `total_price` field changed from the string `"149.95"` to the float `149.95`. **ToStringJ -- integer to string:** Before: ```json theme={null} { "order_id": 3001, "zip_code": 62704, "city": "Springfield" } ``` After: ```json theme={null} { "order_id": 3001, "zip_code": "62704", "city": "Springfield" } ``` The `zip_code` field changed from the integer `62704` to the string `"62704"`. *** ### RenameFields Renames one or more fields to match destination naming conventions or resolve naming conflicts. **Before -- original field names:** ```json theme={null} { "custId": 55, "custName": "Alice Martin", "custEmail": "alice@example.com", "created": "2025-11-01T10:00:00Z" } ``` **After -- fields renamed to match destination schema:** ```json theme={null} { "customer_id": 55, "customer_name": "Alice Martin", "customer_email": "alice@example.com", "created": "2025-11-01T10:00:00Z" } ``` Three fields were renamed: `custId` to `customer_id`, `custName` to `customer_name`, and `custEmail` to `customer_email`. The `created` field was not included in the rename configuration and remains unchanged. *** ### DropFields Removes specified fields from the record before it reaches the destination. Use this to exclude sensitive data, internal metadata, or fields that are not needed in the destination. **Before -- record with internal metadata fields:** ```json theme={null} { "order_id": 3001, "customer_id": 55, "amount": 249.99, "status": "completed", "__source_ts_ms": 1730500200000, "__deleted": false, "__op": "c" } ``` **After -- internal metadata fields removed:** ```json theme={null} { "order_id": 3001, "customer_id": 55, "amount": 249.99, "status": "completed" } ``` The three fields prefixed with `__` were dropped from the record. The remaining fields are delivered to the destination unchanged. *** ### CopyField Copies the value of an existing field into a new field. The original field is preserved. **Before -- record with a single `email` field:** ```json theme={null} { "user_id": 204, "email": "alice@example.com", "name": "Alice Martin", "role": "admin" } ``` **After -- `email` copied to `contact_email`:** ```json theme={null} { "user_id": 204, "email": "alice@example.com", "name": "Alice Martin", "role": "admin", "contact_email": "alice@example.com" } ``` The original `email` field remains intact. A new `contact_email` field was added with the same value. This is useful when a destination requires the same data under a different field name while the original must also be preserved. *** ### RenameKeyFields Renames one or more fields in the **message key** (as opposed to the record value). This is useful when the destination expects specific key field names for primary key matching or partitioning, but the source key field names differ. **Before -- original key field names:** ```json theme={null} { "key": { "id": 1001 }, "value": { "id": 1001, "customer_id": 55, "status": "completed" } } ``` **After -- key field renamed to match destination convention:** ```json theme={null} { "key": { "order_id": 1001 }, "value": { "id": 1001, "customer_id": 55, "status": "completed" } } ``` The key field `id` was renamed to `order_id`. The record value is unchanged -- only the message key is affected. *** ### AddStringSuffix Appends a suffix string to the value of a specified field. This is useful for adding units, environment tags, or other fixed suffixes to field values before they reach the destination. **Before -- field without suffix:** ```json theme={null} { "order_id": 3001, "region": "us-east", "status": "completed" } ``` **After -- `region` field with `"-prod"` suffix appended:** ```json theme={null} { "order_id": 3001, "region": "us-east-prod", "status": "completed" } ``` The suffix `"-prod"` was appended to the `region` field value. All other fields remain unchanged. *** ### StringReplace Replaces occurrences of a pattern within string field values. Use this when you need to normalize or sanitize field values before they reach the destination. **Before -- field with unwanted characters:** ```json theme={null} { "user_id": 204, "phone": "+1 (555) 123-4567", "name": "Alice Martin" } ``` **After -- `phone` field with non-digit characters replaced:** ```json theme={null} { "user_id": 204, "phone": "15551234567", "name": "Alice Martin" } ``` The pattern matching non-digit characters in the `phone` field was replaced with empty strings, leaving only digits. The transform uses regex-based pattern matching. *** ### HeaderToFieldCustom Copies a Kafka message header value into a record field. This is useful when metadata is carried in Kafka headers (e.g., source connector name, event timestamp, or correlation IDs) and you need it available as a regular field in the destination. **Before -- record without header data in fields:** ```json theme={null} { "key": { "id": 1001 }, "headers": { "source_connector": "mysql-prod-01", "event_time": "2025-11-03T09:22:00Z" }, "value": { "id": 1001, "amount": 249.99, "status": "completed" } } ``` **After -- header value copied into a record field:** ```json theme={null} { "key": { "id": 1001 }, "value": { "id": 1001, "amount": 249.99, "status": "completed", "source_connector": "mysql-prod-01" } } ``` The `source_connector` header value was copied into the record value as a new field. The original header remains on the message. *** ### MarkColumnsAsOptional (ToOptional) Marks specified columns as optional (nullable) in the schema. This is useful when your source schema defines fields as `NOT NULL`, but the destination schema requires them to be nullable -- for example, when certain columns are only populated for some record types, or when schema evolution introduces new columns that existing rows do not have values for. **Before -- schema marks `middle_name` as required (non-null):** ```json theme={null} { "schema": { "fields": [ { "field": "user_id", "type": "int32", "optional": false }, { "field": "first_name", "type": "string", "optional": false }, { "field": "middle_name", "type": "string", "optional": false }, { "field": "last_name", "type": "string", "optional": false } ] }, "payload": { "user_id": 204, "first_name": "Alice", "middle_name": "Marie", "last_name": "Martin" } } ``` **After -- `middle_name` marked as optional (nullable):** ```json theme={null} { "schema": { "fields": [ { "field": "user_id", "type": "int32", "optional": false }, { "field": "first_name", "type": "string", "optional": false }, { "field": "middle_name", "type": "string", "optional": true }, { "field": "last_name", "type": "string", "optional": false } ] }, "payload": { "user_id": 204, "first_name": "Alice", "middle_name": "Marie", "last_name": "Martin" } } ``` The `middle_name` field changed from `"optional": false` to `"optional": true`. The payload data is unchanged -- only the schema metadata is modified. This prevents schema enforcement errors at the destination when a record has a `null` value for that field. *** ## Pipeline Transform Examples ### Join Use [Join](/transform-join) to combine records from two or more input topics into a single output record using SQL join logic. In this example, an `orders` topic is joined with a `customers` topic on a shared `customer_id` field. **Input -- record from `orders` topic:** ```json theme={null} { "key": { "order_id": 3001 }, "value": { "order_id": 3001, "customer_id": 55, "amount": 249.99, "status": "completed", "created_at": "2025-11-02T14:30:00Z" } } ``` **Input -- record from `customers` topic:** ```json theme={null} { "key": { "customer_id": 55 }, "value": { "customer_id": 55, "name": "Alice Martin", "email": "alice@example.com", "tier": "gold" } } ``` **After -- joined output record:** ```json theme={null} { "key": { "order_id": 3001 }, "value": { "order_id": 3001, "customer_id": 55, "amount": 249.99, "status": "completed", "created_at": "2025-11-02T14:30:00Z", "customer_name": "Alice Martin", "customer_email": "alice@example.com", "customer_tier": "gold" } } ``` The join matched records from both topics on `customer_id = 55` and merged the customer fields into the order record. The output field names (`customer_name`, `customer_email`, `customer_tier`) are defined in the join SQL logic. Join transforms use SQL syntax to define the join condition and output fields. See [Join](/transform-join) for setup instructions and SQL examples. *** ### Chained Transforms Pipeline transforms execute sequentially -- each transform receives the output of the previous one. This example shows data flowing through three transforms in order: **Filter**, **Enrich**, and **Transform / Filter Records** (used as a map). **Stage 0 -- original input record:** ```json theme={null} { "key": { "order_id": 3001 }, "value": { "order_id": 3001, "customer_id": 55, "amount": 249.99, "status": "completed", "created_at": "2025-11-02T14:30:00Z" } } ``` **Stage 1 -- after Filter (keep only orders with amount > 100):** ```json theme={null} { "key": { "order_id": 3001 }, "value": { "order_id": 3001, "customer_id": 55, "amount": 249.99, "status": "completed", "created_at": "2025-11-02T14:30:00Z" } } ``` Record passes through because `amount` (249.99) exceeds the threshold. Orders with `amount <= 100` are dropped at this stage and never reach subsequent transforms. **Stage 2 -- after Enrich (add customer tier from cached topic):** ```json theme={null} { "key": { "order_id": 3001 }, "value": { "order_id": 3001, "customer_id": 55, "amount": 249.99, "status": "completed", "created_at": "2025-11-02T14:30:00Z", "customer_tier": "gold" } } ``` The `customer_tier` field was added by looking up `customer_id = 55` in the cached customers topic. **Stage 3 -- after Transform / Filter Records (compute a new field):** ```json theme={null} { "key": { "order_id": 3001 }, "value": { "order_id": 3001, "customer_id": 55, "amount": 249.99, "status": "completed", "created_at": "2025-11-02T14:30:00Z", "customer_tier": "gold", "priority": "high" } } ``` The transform logic computed a `priority` field: orders from `"gold"` tier customers with `amount > 200` are tagged `"high"`. This field was only possible to compute because the enrichment step added `customer_tier` first. For guidance on ordering pipeline transforms, see [Transform Ordering](/transform-ordering). *** ## Real-World Patterns These patterns show how to combine transforms to solve common data engineering challenges. ### PII Masking Use **DropFields** to remove sensitive fields before data reaches the destination, or use **Transform / Filter Records** to mask values while preserving the field structure. **Approach 1 -- Drop sensitive fields entirely with DropFields:** Configure DropFields to remove fields such as `ssn`, `credit_card_number`, and `date_of_birth`. The fields are stripped from the record before it lands in the destination. **Before:** ```json theme={null} { "user_id": 204, "name": "Alice Martin", "email": "alice@example.com", "ssn": "123-45-6789", "credit_card_number": "4111111111111111" } ``` **After (DropFields removes ssn and credit\_card\_number):** ```json theme={null} { "user_id": 204, "name": "Alice Martin", "email": "alice@example.com" } ``` **Approach 2 -- Mask field values with Transform / Filter Records:** Use a JavaScript-based Filter transform to replace sensitive values with masked versions while keeping the field present. ```javascript theme={null} // Transform / Filter Records logic function transform(record) { if (record.value.email) { var parts = record.value.email.split("@"); record.value.email = parts[0].substring(0, 2) + "***@" + parts[1]; } if (record.value.ssn) { record.value.ssn = "***-**-" + record.value.ssn.slice(-4); } return record; } ``` **Before:** ```json theme={null} { "user_id": 204, "email": "alice@example.com", "ssn": "123-45-6789" } ``` **After (values masked, fields preserved):** ```json theme={null} { "user_id": 204, "email": "al***@example.com", "ssn": "***-**-6789" } ``` PII masking should be applied as early as possible in your pipeline to prevent sensitive data from reaching intermediate storage. Use pipeline transforms (Flink) rather than destination-side transforms when possible. ### Date Formatting Use **Transform / Filter Records** to convert timestamps between formats. This is common when the source produces ISO 8601 timestamps but the destination expects epoch milliseconds, or when you need to extract date parts. ```javascript theme={null} // Transform / Filter Records logic -- convert ISO timestamp to epoch milliseconds function transform(record) { if (record.value.created_at) { record.value.created_at_epoch = new Date(record.value.created_at).getTime(); } return record; } ``` **Before:** ```json theme={null} { "order_id": 3001, "created_at": "2025-11-02T14:30:00Z" } ``` **After (new epoch field added):** ```json theme={null} { "order_id": 3001, "created_at": "2025-11-02T14:30:00Z", "created_at_epoch": 1730557800000 } ``` You can also use **ToStringJ** to cast numeric timestamp fields to strings if your destination requires string-typed date columns. ### Null Handling Use **Transform / Filter Records** to replace null values with defaults, preventing null-related errors at the destination. ```javascript theme={null} // Transform / Filter Records logic -- apply default values for null fields function transform(record) { if (record.value.region === null || record.value.region === undefined) { record.value.region = "unknown"; } if (record.value.amount === null || record.value.amount === undefined) { record.value.amount = 0.0; } return record; } ``` **Before (region is null):** ```json theme={null} { "order_id": 3002, "region": null, "amount": null, "status": "pending" } ``` **After (nulls replaced with defaults):** ```json theme={null} { "order_id": 3002, "region": "unknown", "amount": 0.0, "status": "pending" } ``` If your destination supports nullable columns, you can also use **MarkColumnsAsOptional** to allow null values through without replacing them. Choose between default values and nullable columns based on your downstream query requirements. *** ## See Also * [Transform Types Overview](/transforms) -- All available transform types * [Transform Ordering](/transform-ordering) -- How pipeline and destination-side transforms are sequenced * [Transform / Filter Records](/transform-filter-records) -- Setup and implementation for filtering * [Enrich](/transform-enrich) -- SQL-based cached topic enrichment * [Enrich (Async)](/transform-enrich-async) -- REST API enrichment * [Join](/transform-join) -- Multi-topic joins * [Fan Out](/transform-fanout) -- Record routing to multiple output topics * [Pipelines](/pipelines) -- How transforms integrate into pipelines # Fan Out Source: https://docs.streamkap.com/transform-fanout Route a single record to multiple output topics based on custom logic. ## Setup * Navigate to [Create Transform](https://app.streamkap.com/transforms/create). * Choose **Fan Out**. ### 1. Settings * **Name:** Enter a name for the transform e.g. filter out empty records * **Language:** Select the preferred (if supported) language to define the transform. * **Input Pattern:** Enter a regular expression pattern to list the topics this transform is applied to e.g. `source_abcd1234.public.orders`,`source_abcd1234\.public\.order.*`. * **Output Pattern:** Enter the desired output topic name including schema e.g. `enriched.orders`. * **Transform Parallelism:** The degree of parallelism for this transform. Determines number of tasks this transform should use when executing the transform logic. The default should be more than sufficient in most cases. ### 2. Advanced Parameters * **Input Serialization:** The format (e.g. `JSON`, `AVRO`, `STRING`, `BYTES`) of the input data. The default (`Any`) will try determine the format automatically. * **Output Serialization:** The format (e.g. `JSON`, `AVRO`, `STRING`, `BYTES`) of the output data. The default (`Any`) will try determine the format automatically. Click **Save**. ## Implementation Here is where you define the logic for your transform. * Go to the **Implementation** tab. ### 1. Objective / Goal * Describe in a short sentence the purpose of the transform and its expected output. ### 2. Input / Output Topics These take the **Input Pattern** and **Output Pattern** entered earlier. You can also amend the input tables, and see a preview of the input topics captured by the input pattern as well as the corresponding output topic. **No topics in preview** If no topics are shown, review and amend your input topic patterns. The [topics page](https://app.streamkap.com/topics) shows the full names of topics, usually in three parts. Starting your pattern with `.*` ignores the first part (usually the Connector's ID), so you can focus your pattern on matching the schema (middle) and table (last) parts. * **Input Topic Pattern:** Amend the desired input topic using a regular expression pattern. * **Output Topic Replacement:** Amend the desired output topic name using a regular expression pattern. Click **Save Topics Pattern / Replacement**. ### 3. Transform Logic Here is where you define the logic for the transform. #### Fan Out When a record is processed by Streamkap from a data source, it's usually made up of at least 2 parts: key and value. * The **key** usually represents the primary key of the record i.e. a unique record identifier. * The **value** usually represents the rest of the record i.e. all other record fields/columns. Depending on your fan out goal, you can define the logic for one or both of these functions: * "Value Transform" changes the message value * "Key Transform" changes the message key, in most pipelines the message key drives the primary/unique keys on the destination side * "Topic Transform" allows changing of the output topic name using more complex logic, for Fan Out transform this will most likely be the main focus * "Common Transform" used to set up a common object that is passed between the different functions allowing sharing of functionality and avoid repetition If more exact output schema needs to be specified and the inferred schema does not exactly match your needs use: * "Value Schema Transform" to tweak the schema for some or all the output value fields * "Key Schema Transform" to tweak the schema for some or all the output key fields Please see [the list of data types supported by schema transforms](/transform-list-of-data-types) Once you're happy with them, continue to the next section. ### 4. Deploy & Check Here you deploy your transform, but you can also perform diagnostic and validation checks at a click of a button. #### 4.0 Clear Output Topic This purges the output topic of data (if any) as well as any cached job state. A "reset" of the transform's previous outputs so you can start fresh. * **Output Topic pattern to clear:** Enter a regular expression pattern that captures the output topics to be cleared. Click **Clear Output Topics and Job State**. #### 4.1 Deploy When deploying a transform, you can decide from what point the transform starts processing the input topics data-assuming the data's still available. * **Replay Window:** * **Time-based**: Enter the **last** number of days, hours or minutes to process. * **Recency-based**: Enter `0` to process from the **latest** data, or, leave it blank/empty to process from the **earliest** data. Click **Deploy Transform**. #### 4.2 Job Status Check on the current status of the transform job. Click **Check Job Status**. #### 4.3 Output Topics Check what output topics were created (if any) and populated by the transform. Click **Check Output Topics**. #### 4.4 Errors Check if the transform jobs are failing and if so, what the problem is. Click **Check for Errors**. ## See Also * [Transform Ordering](/transform-ordering) - Understand how transforms are ordered and executed within a pipeline * [Transform Examples](/transform-examples) - Practical examples and recipes for common transform use cases # Transform / Filter Records Source: https://docs.streamkap.com/transform-filter-records Apply custom logic to modify or filter records. ## Setup * Navigate to [Create Transform](https://app.streamkap.com/transforms/create). * Choose **Transform / Filter Records**. ### 1. Settings * **Name:** Enter a name for the transform e.g. filter out empty records * **Language:** Select the preferred (if supported) language to define the transform. * **Input Pattern:** Enter a regular expression pattern to list the topics this transform is applied to e.g. `source_abcd1234.public.orders`,`source_abcd1234\.public\.order.*`. * **Output Pattern:** Enter the desired output topic name including schema e.g. `enriched.orders`. * **Transform Parallelism:** The degree of parallelism for this transform. Determines number of tasks this transform should use when executing the transform logic. The default should be more than sufficient in most cases. ### 2. Advanced Parameters * **Input Serialization:** The format (e.g. `JSON`, `AVRO`, `STRING`, `BYTES`) of the input data. The default (`Any`) will try determine the format automatically. * **Output Serialization:** The format (e.g. `JSON`, `AVRO`, `STRING`, `BYTES`) of the output data. The default (`Any`) will try determine the format automatically. Click **Save**. ## Implementation Now you need to define the logic for your transform and deploy it. * Go to the **Implementation** tab. ### 1. Objective / Goal * Describe in a short sentence the purpose of the transform and its expected output. ### 2. Input / Output Topics These take the **Input Pattern** and **Output Pattern** entered earlier. You can also amend the input tables, and see a preview of the input topics captured by the input pattern as well as the corresponding output topic. **No topics in preview** If no topics are shown, review and amend your input topic patterns. The [topics page](https://app.streamkap.com/topics) shows the full names of topics, usually in three parts. Starting your pattern with `.*` ignores the first part (usually the Connector's ID), so you can focus your pattern on matching the schema (middle) and table (last) parts. * **Input Topic Pattern:** Amend the desired input topic using a regular expression pattern. * **Output Topic Replacement:** Amend the desired output topic name using a regular expression pattern. Click **Save Topics Pattern / Replacement**. ### 3. Transform Logic Here is where you define the logic for the transform. #### Transform / Filter Records When a record is processed by Streamkap from a data source, it's usually made up of at least 2 parts: key and value. * The **key** usually represents the primary key of the record i.e. a unique record identifier. * The **value** usually represents the rest of the record i.e. all other record fields/columns. Depending on your transform/filter goal, you can define the logic for one or both of these functions: * "Value Transform" changes the message value * "Key Transform" changes the message key, in most pipelines the message key drives the primary/unique keys on the destination side * "Topic Transform" allows changing of the output topic name using more complex logic, for Transform / Filter Records this should not be needed * "Common Transform" used to set up a common object that is passed between the different functions allowing sharing of functionality and avoid repetition If more exact output schema needs to be specified and the inferred schema does not exactly match your needs use: * "Value Schema Transform" to tweak the schema for some or all the output value fields * "Key Schema Transform" to tweak the schema for some or all the output key fields Please see [the list of data types supported by schema transforms](/transform-list-of-data-types) Once you're happy with them, continue to the next section. ### 4. Deploy & Check Here you deploy your transform, but you can also perform diagnostic and validation checks at a click of a button. #### 4.0 Clear Output Topic This purges the output topic of data (if any) as well as any cached job state. A "reset" of the transform's previous outputs so you can start fresh. * **Output Topic pattern to clear:** Enter a regular expression pattern that captures the output topics to be cleared. Click **Clear Output Topics and Job State**. #### 4.1 Deploy When deploying a transform, you can decide from what point the transform starts processing the input topics data-assuming the data's still available. * **Replay Window:** * **Time-based**: Enter the **last** number of days, hours or minutes to process. * **Recency-based**: Enter `0` to process from the **latest** data, or, leave it blank/empty to process from the **earliest** data. Click **Deploy Transform**. #### 4.2 Job Status Check on the current status of the transform job. Click **Check Job Status**. #### 4.3 Output Topics Check what output topics were created (if any) and populated by the transform. Click **Check Output Topics**. #### 4.4 Errors Check if the transform jobs are failing and if so, what the problem is. Click **Check for Errors**. ## See Also * [Transform Ordering](/transform-ordering) - Understand how transforms are ordered and executed within a pipeline * [Transform Examples](/transform-examples) - Practical examples and recipes for common transform use cases # Join Source: https://docs.streamkap.com/transform-join Join multiple topics. Supports schema evolution and pass-through fields. ## Setup * Navigate to [Create Transform](https://app.streamkap.com/transforms/create). * Choose **Join**. ### 1. Settings * **Name:** Enter a name for the transform e.g. filter out empty records * **Language:** Select the preferred (if supported) language to define the transform. * **Input Pattern:** Enter a regular expression pattern to list the topics this transform is applied to e.g. `source_abcd1234.public.orders`,`source_abcd1234\.public\.order.*`. * **Output Pattern:** Enter the desired output topic name including schema e.g. `enriched.orders`. * **Transform Parallelism:** The degree of parallelism for this transform. Determines number of tasks this transform should use when executing the transform logic. The default should be more than sufficient in most cases. ### 2. Advanced Parameters * **Input Serialization:** The format (e.g. `JSON`, `AVRO`, `STRING`, `BYTES`) of the input data. The default (`Any`) will try determine the format automatically. * **Output Serialization:** The format (e.g. `JSON`, `AVRO`, `STRING`, `BYTES`) of the output data. The default (`Any`) will try determine the format automatically. Click **Save**. ## Implementation Now you need to define the logic for your transform and deploy it. * Go to the **Implementation** tab. ### 1. Objective / Goal * Describe in a short sentence the purpose of the transform and its expected output. ### 2. Input / Output Topics These take the **Input Pattern** and **Output Pattern** entered earlier. You can also amend the input tables, and see a preview of the input topics captured by the input pattern as well as the corresponding output topic. ### No topics in preview If no topics are shown, review and amend your input topic patterns. The [topics page](https://app.streamkap.com/topics) shows the full names of topics, usually in three parts. Starting your pattern with `.*` ignores the first part (usually the Connector's ID), so you can focus your pattern on matching the schema (middle) and table (last) parts. * **Output Topic Replacement:** Amend the desired output topic name using a regular expression pattern. #### 2.1 Input Tables Here the input topic pattern is translated into individual table expressions for easier control over which topics are or are not included in the join. * **Add** or **Remove** topics as needed. Click **Save Topics Pattern / Replacement**. ### 3. Transform Logic Here is where you define the logic for the transform. #### SQL Join Depending on the number of input tables, a number of editable SQL scripts are generated. There are 2 types of SQL script involved: the first type defines-per input table-the fields needed to join them together, and the other is the `JOIN` query itself. * Click on each `.sql` script to edit it. Once you're happy with them, continue to the next section. ### 4. Deploy & Check Here you deploy your transform, but you can also perform diagnostic and validation checks at a click of a button. #### 4.0 Clear Output Topic This purges the output topic of data (if any) as well as any cached job state. A "reset" of the transform's previous outputs so you can start fresh. * **Output Topic pattern to clear:** Enter a regular expression pattern that captures the output topics to be cleared. Click **Clear Output Topics and Job State**. #### 4.1 Deploy When deploying a transform, you can decide from what point the transform starts processing the input topics data-assuming the data's still available. * **Replay Window:** * **Time-based**: Enter the **last** number of days, hours or minutes to process. * **Recency-based**: Enter `0` to process from the **latest** data, or, leave it blank/empty to process from the **earliest** data. Click **Deploy Transform**. #### 4.2 Job Status Check on the current status of the transform job. Click **Check Job Status**. #### 4.3 Output Topics Check what output topics were created (if any) and populated by the transform. Click **Check Output Topics**. #### 4.4 Errors Check if the transform jobs are failing and if so, what the problem is. Click **Check for Errors**. ## See Also * [Transform Ordering](/transform-ordering) - Understand how transforms are ordered and executed within a pipeline * [Transform Examples](/transform-examples) - Practical examples and recipes for common transform use cases # Schema Transforms Supported Types Source: https://docs.streamkap.com/transform-list-of-data-types Mapping Kafka Data Types and SQL Types. Very simple example of a JSON payload: ```json theme={null} { "id" : 1, "customer_id" : 1234, "customer_name" : "John Doe" } ``` And the corresponding schema: ```json theme={null} { "type" : "struct", "fields" : [ { "type" : "int64", "optional" : false, "field" : "id" }, { "type" : "int64", "optional" : false, "field" : "customer_id" }, { "type" : "string", "optional" : false, "field" : "customer_name" } ], "optional" : false } ``` To make it easier to use the Kafka types, please find below the list of supported types mapped to usual SQL types that might be found in source database. | Kafka Type | SQL Type (PostgreSQL) | SQL Type (MySQL) | SQL Type (Oracle) | | ---------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | ---------------- | ----------------- | | int8 | SMALLINT | TINYINT | NUMBER(3) | | int16 | INTEGER | SMALLINT | NUMBER(5) | | int32 | INTEGER | INT | NUMBER(10) | | int64 | BIGINT | BIGINT | NUMBER(19) | | float | REAL | FLOAT | NUMBER(7, 2) | | double | DOUBLE PRECISION | DOUBLE | NUMBER(15, 6) | | string | VARCHAR | VARCHAR | VARCHAR2 | | bytes | BYTEA | BLOB | BLOB | | boolean | BOOLEAN | BOOLEAN | NUMBER(1) | | `{"type":"int64","optional":false,"name":"org.apache.kafka.connect.data.Timestamp"}` | TIMESTAMP | TIMESTAMP | TIMESTAMP | | `{"type":"bytes","optional":false,"name":"org.apache.kafka.connect.data.Decimal","parameters":{"scale":"4","connect.decimal.precision":"38"}}` | DECIMAL(38,4) | DECIMAL(38,4) | DECIMAL(38,4) | | `{"type":"int32","optional":false,"name":"org.apache.kafka.connect.data.Date"}` | DATE | DATE | DATE | | `{"type":"int64","optional":false,"name":"org.apache.kafka.connect.data.Time"}` | TIME | TIME | N/A | DECIMAL(38,4) means a decimal number with up to 38 digits in total, of which 4 digits can be after the decimal point, one can adjust the precision if needed. Please see the example of primitive Kafka Schema definitions and their corresponding payloads: ```text theme={null} {"schema":{"type":"boolean","optional":false,"default":false},"payload":false} {"schema":{"type":"int8","optional":false,"default":1},"payload":1} {"schema":{"type":"int16","optional":false,"default":1},"payload":1} {"schema":{"type":"int32","optional":false,"default":1},"payload":1} {"schema":{"type":"int64","optional":false,"default":1},"payload":1} {"schema":{"type":"float","optional":false,"default":1.0},"payload":1.0} {"schema":{"type":"double","optional":false,"default":1.0},"payload":1.0} {"schema":{"type":"bytes","optional":false,"default":"Zm9v"},"payload":"Zm9v"} {"schema":{"type":"string","optional":false,"default":"foo"},"payload":"foo"} {"schema":{"type":"bytes","optional":false,"name":"org.apache.kafka.connect.data.Decimal","parameters":{"scale":"4","connect.decimal.precision":"38"},"default":"EtaH"},"payload":"EtaH"} {"schema":{"type":"int64","optional":false,"name":"org.apache.kafka.connect.data.Timestamp","default":1758635042028},"payload":1758635042028} {"schema":{"type":"int64","optional":false,"name":"org.apache.kafka.connect.data.Time","default":55800000},"payload":55800000} {"schema":{"type":"int32","optional":false,"name":"org.apache.kafka.connect.data.Date","default":20370},"payload":20370} ``` # Transform Ordering Source: https://docs.streamkap.com/transform-ordering Understand how transforms are ordered and how execution sequence affects your data pipeline. Streamkap applies two categories of transforms to your data, each with different ordering behavior. Understanding how these transforms are sequenced helps you build predictable pipelines and avoid subtle data issues. ## Two Categories of Transforms | Category | Where It Runs | Who Controls Order | User Action | | ------------------------------- | ------------------------------------------------- | -------------------- | -------------------------------------------- | | **Destination-side transforms** | On the destination connector (Kafka Connect SMTs) | System (fixed order) | Configure which transforms are active | | **Pipeline transforms (Flink)** | In the streaming layer (Apache Flink) | You | Define transforms and arrange their sequence | These two categories operate at different stages of the pipeline. Destination-side transforms run **after** pipeline transforms. Data flows from the source, through any Flink pipeline transforms, and then through destination-side transforms before landing in the destination. *** ## Destination-Side Transforms Destination-side transforms are applied automatically by the system in a fixed order. You choose which transforms to enable on your destination connector, but the system determines the sequence in which they execute. ### Available Destination-Side Transforms | Transform | What It Does | | ------------------------- | ---------------------------------------------------------------------------- | | **DropFields** | Removes specified fields from the record before it reaches the destination. | | **RenameFields** | Renames fields to match destination naming conventions or resolve conflicts. | | **RenameKeyFields** | Renames fields in the message key (as opposed to the value). | | **CopyField** | Copies the value of one field into a new field, preserving the original. | | **ToJsonJ** | Converts structured fields (maps, arrays, nested objects) into JSON strings. | | **ToJsonbJ** | Converts structured fields into PostgreSQL-compatible JSONB strings. | | **ToIntJ** | Casts compatible field values to integer type. | | **ToFloatJ** | Casts compatible field values to float type. | | **ToStringJ** | Casts field values to string type. | | **AddStringSuffix** | Appends a suffix string to the value of a specified field. | | **StringReplace** | Replaces occurrences of a pattern within string field values. | | **HeaderToFieldCustom** | Copies a Kafka message header value into a record field. | | **MarkColumnsAsOptional** | Marks specified columns as optional in the schema, allowing null values. | ### Fixed Execution Order The system applies active destination-side transforms in a predetermined sequence. You do not need to set or manage this order -- it is handled automatically. Because the order is fixed, certain interactions are predictable: * If you enable both **ToJsonJ** and **DropFields**, JSON conversion runs **before** fields are dropped. If you need a field excluded from JSON output, use DropFields to remove the entire JSON field after conversion. * When both **CopyField** and **RenameFields** are enabled, the copy runs first. Rename operations apply to the already-copied fields. * Enabling both **ToJsonJ** and **ToStringJ** means JSON conversion runs before string casting. Nested objects are converted to JSON strings first, then any remaining type casting to string is applied. You do not need to worry about sequencing destination-side transforms yourself. Focus on selecting the right combination of transforms for your use case, and the system handles the rest. *** ## Pipeline Transforms (Flink) Pipeline transforms run in the Apache Flink streaming layer. Unlike destination-side transforms, **you control the order** in which pipeline transforms execute. Each transform receives the output of the previous one, forming a chain. For the full list of available pipeline transform types, see [Transforms](/transforms). ### Why Order Matters Pipeline transforms execute sequentially. Each transform receives the output of the one before it. This means: 1. A **filter** that removes records will reduce the volume of data that subsequent transforms process. 2. An **enrich** transform adds fields that downstream transforms can reference. 3. A **fan-out** transform routes records to separate output topics — any subsequent transforms must be configured per output branch, so place fan-out at the end of your chain. Changing the order of transforms can change both the **result** and the **performance** of your pipeline. ### Recommended Ordering The following order works well for most pipelines that combine multiple transform types: Apply [Transform / Filter Records](/transform-filter-records) early to remove irrelevant records. This reduces the volume of data that all subsequent transforms must process. Use [Join](/transform-join), [Enrich](/transform-enrich), or [Enrich (Async)](/transform-enrich-async) to combine or augment records. Running these after filtering means fewer records to look up or join, which improves performance and reduces API call volume. Apply [Transform / Filter Records](/transform-filter-records) again (if needed) to reshape, rename, or compute fields using the enriched data. Use [Fan Out](/transform-fanout) at the end of your chain so that all preceding logic (filtering, enrichment, mapping) is applied before records are routed to their final output topics. Not every pipeline needs all of these stages. Use only the transforms your use case requires. The recommended order applies to the transforms you do use. ### Practical Examples #### Example 1: Filter Before Enrich (Async) **Scenario:** You stream order events and want to call an external API to enrich orders with customer details, but only for orders above \$100. | Order | Transform | Effect | | ----- | -------------- | ----------------------------------------------------------------- | | 1 | Filter Records | Keep only orders where `amount > 100`. Removes 80% of records. | | 2 | Enrich (Async) | Call external API to fetch customer details for remaining orders. | **Why this order matters:** Filtering first means you make API calls for only 20% of records instead of 100%. Reversing the order would waste API calls on records you discard. #### Example 2: Enrich Then Map **Scenario:** You want to enrich product records with category data from a lookup table, then compute a new field based on the enriched data. | Order | Transform | Effect | | ----- | -------------------------- | ------------------------------------------------------------------------ | | 1 | Enrich | Add `category_name` from a cached lookup topic. | | 2 | Transform / Filter Records | Compute `display_label` by combining `product_name` and `category_name`. | **Why this order matters:** The map transform references the `category_name` field, which only exists after enrichment. Reversing the order would mean the field is not yet available. #### Example 3: Full Chain with Fan Out **Scenario:** You stream user activity events and want to filter, enrich, and then route to different output topics based on activity type. | Order | Transform | Effect | | ----- | -------------------------- | -------------------------------------------------------------------- | | 1 | Filter Records | Remove bot traffic and test accounts. | | 2 | Enrich | Add user profile data from a cached topic. | | 3 | Transform / Filter Records | Normalize field names and compute session duration. | | 4 | Fan Out | Route `purchase` events to one topic and `browse` events to another. | **Why this order matters:** Fan Out at the end ensures all records are fully processed before being split. If Fan Out ran earlier, you would need to duplicate the enrichment and mapping logic across each output branch. ### Common Ordering Mistakes Avoid these patterns that can cause unexpected behavior or performance issues. | Mistake | Problem | Fix | | ---------------------------- | --------------------------------------------------------------------- | ----------------------------------------- | | Enrich (Async) before Filter | Wastes API calls on records that are later discarded | Move Filter before Enrich (Async) | | Map/Transform before Enrich | References enriched fields that do not exist yet | Move Enrich before the Map/Transform step | | Fan Out before Map | Requires duplicating transform logic in each output branch | Move Fan Out to the end of the chain | | Filter after Join | Joins all records first, then discards some -- wastes join processing | Filter input topics before joining | *** ## End-to-End Data Flow To understand where each category of transforms fits, here is the full data path: 1. **Source** -- Change events are captured from your database. 2. **Pipeline transforms (Flink)** -- Your user-defined transforms (Filter, Enrich, Join, Fan Out) execute in the order you specify. 3. **Destination-side transforms (SMTs)** -- System-managed transforms (DropFields, RenameFields, ToJsonJ, etc.) apply in a fixed order. 4. **Destination** -- Transformed data lands in your data warehouse or lake. If you need a field dropped before it reaches the destination, you can use either a Flink pipeline transform (to remove it from the stream early) or a destination-side DropFields transform (to remove it at the last step). Choose based on whether downstream Flink transforms still need the field. ## See Also * [Transform Types Overview](/transforms) - All available transform types * [Streaming Transforms](/transforms-1) - Managing transforms in the UI # TypeScript Development Kit Source: https://docs.streamkap.com/transform-typescript-development-kit Development environment for building TypeScript transforms with npm dependencies, testing, and bundling Build Streamkap transforms using TypeScript with your favorite npm packages and full testing support. Use as a standalone project or integrate into your existing codebase. **Repository**: [streamkap-com/transform-kit-typescript](https://github.com/streamkap-com/transform-kit-typescript) ## Choose Your Path ### 🆕 **New Project** (Start from scratch) Use the entire repository as your development environment ### 🔄 **Existing Project** (Add to your codebase) Add TypeScript transform capabilities to your current project ## Quick Start - New Project **Prerequisites**: Node.js 16+, npm 8+ ```bash Terminal theme={null} git clone https://github.com/streamkap-com/transform-kit-typescript.git cd transform-kit-typescript npm install && npm run build && npm test ls -la transforms/ # ← Copy these .js files to Streamkap ``` *Note: You may see some npm warnings during install - these don't affect functionality.* 📋 **Next**: See [Which File to Use?](#-which-file-to-use) and [deployment steps](#5-deploy-to-streamkap) below. ## Supported Transform Types | Transform Type | Files Generated | Description | | ------------------ | ---------------------------------------- | -------------------------------- | | **Map/Filter** | `valueTransform.js`, `keyTransform.js` | Transform and filter records | | **Fan Out** | `valueTransform.js`, `topicTransform.js` | Route records to multiple topics | | **Enrich (Async)** | `valueTransform.js` | Async data enrichment | | **Un-nesting** | `valueTransform.js` | Flatten nested objects | ## How to Use - New Project ### 1. Define Your Data Structure Tell the system what your data looks like by editing the TypeScript files: ```typescript TypeScript theme={null} // src/OrderType1.ts export interface OrderType1 { _id: string; order_number: number; total_amount: number; } // src/MergedOrder.ts export interface MergedOrder { _id: string; processed_at: string; formatted_total: string; } ``` ### 2. Write Your Business Logic Open `src/OrderTransformer.ts` and write the code that processes your data: ```typescript TypeScript theme={null} export class OrderTransformer { transform(input: OrderType1): MergedOrder { const moment = require('moment'); return { _id: input._id, processed_at: moment().toISOString(), formatted_total: `$${input.total_amount.toFixed(2)}` }; } } ``` ### 3. Add External Libraries (Optional) You can use popular JavaScript libraries to help with your transforms: ```bash Terminal theme={null} npm install moment lodash uuid # These are already installed in the project ``` **What works**: JavaScript libraries like moment (dates), lodash (utilities), uuid **What doesn't work**: Libraries that require system-level access or Node.js-specific features ### 4. Build and Test ```bash Terminal theme={null} npm run build # Generates JavaScript files npm test # Runs all tests ``` Files are generated in the `transforms/` directory. ### 5. Deploy to Streamkap **Coming soon:** deploy using CLI , deploy using Terraform. 1. **Create your transform** in the Streamkap web interface 2. **Go to the Implementation tab** 3. **Copy the entire contents** of your generated file: * **Always required**: `valueTransform.js` (your main business logic) * **Optional**: `keyTransform.js` (only if you need custom partitioning - leave blank otherwise) * **Optional**: `topicTransform.js` (only for fan-out routing - leave blank otherwise) 4. **Paste into Streamkap's code editor** (this replaces the default code) 5. **Save and Deploy** - your TypeScript code is now running! *The files are completely self-contained, so just copy the entire file content.* ### 🤔 Which File to Use? | Transform Type | File to Copy | Purpose | | ----------------- | ---------------------------------- | --------------------------------------------------- | | **map\_filter** | `valueTransform.js` (**required**) | Transform and filter records | | | `keyTransform.js` (*optional*) | Leave blank in Streamkap if no key changes needed | | **fan\_out** | `valueTransform.js` (**required**) | Transform records for routing | | | `topicTransform.js` (*optional*) | Leave blank in Streamkap if no topic changes needed | | **enrich\_async** | `valueTransform.js` (**required**) | Enrich with external APIs | | **un\_nesting** | `valueTransform.js` (**required**) | Flatten nested objects | 💡 **Key Points**: * **`valueTransform.js`** is **always required** (contains your main business logic) * **`keyTransform.js`** is **optional** - only needed for custom partitioning logic; leave blank in Streamkap if you want to keep the original message key * **`topicTransform.js`** is **optional** - only needed for fan-out transforms to route messages to different topics; leave blank in Streamkap if sending to the original topic ## Generated Files ``` transforms/ ├── map-filter/ │ ├── valueTransform.js │ └── keyTransform.js ├── fan-out/ │ ├── valueTransform.js │ └── topicTransform.js ├── enrich-async/ │ └── valueTransform.js └── un-nesting/ └── valueTransform.js ``` Each file is self-contained with all npm dependencies bundled inside. *** ## How to Use - Existing Project Add Streamkap bundler to your existing codebase in 3 steps: ### 1. Copy Files ```bash Terminal theme={null} # Copy bundler and transform files cp build-multiple.js your-project/ cp test-selective.js your-project/ mkdir -p your-project/src/ cp src/value_transform.ts your-project/src/ cp src/key_transform.ts your-project/src/ cp src/topic_transform.ts your-project/src/ # Optionally copy the example interfaces and transformer as reference cp src/OrderTransformer.ts your-project/src/ cp src/Customer.ts your-project/src/ cp src/OrderType1.ts your-project/src/ cp src/OrderType2.ts your-project/src/ cp src/MergedOrder.ts your-project/src/ ``` ### 2. Update package.json ```json JSON theme={null} { "scripts": { "build": "node build-multiple.js --all", "build:map-filter": "node build-multiple.js --map-filter", "build:fan-out": "node build-multiple.js --fan-out", "build:enrich-async": "node build-multiple.js --enrich-async", "build:un-nesting": "node build-multiple.js --un-nesting", "test": "node test-selective.js", "test:map-filter": "npm run build:map-filter && node test-selective.js --map-filter" }, "dependencies": { "lodash": "^4.17.21", "moment": "^2.30.1", "uuid": "^11.1.0" }, "devDependencies": { "@types/lodash": "^4.17.20", "@types/moment": "^2.11.29", "@types/uuid": "^10.0.0", "esbuild": "^0.8.27", "typescript": "^4.0.0" } } ``` ### 3. Connect Your Logic Create your own OrderTransformer or update the existing one: ```typescript TypeScript theme={null} // src/YourTransformer.ts import { YourService } from './your-existing-logic'; export class YourTransformer { private yourService = new YourService(); public transform(input: any): any { // Use your existing business logic return this.yourService.processData(input); } } ``` Then update the transform files to use your transformer: ```typescript TypeScript theme={null} // src/value_transform.ts import { YourTransformer } from "./YourTransformer"; export function _streamkap_transform(valueObject: any, keyObject: any, topic: any, timestamp: any) { var transformer = new YourTransformer(); var transformedRecord = transformer.transform(valueObject); return transformedRecord; } ``` ### 4. Build & Deploy ```bash Terminal theme={null} # Install dependencies npm install # Build all transforms npm run build # OR build specific transforms only npm run build:map-filter # Build specific type node build-multiple.js --map-filter --enrich-async # Build multiple types node build-multiple.js --all # Build all transforms # Check generated files ls transforms/ # Copy these .js files to Streamkap ``` **That's it!** Your transforms are generated in `transforms/` folder. 📋 **Next**: See [Which File to Use?](#-which-file-to-use) below for deployment guidance. *** ## Real-World Examples *These are just examples - replace the data types and logic with your own use case.* ### Order Processing ```typescript TypeScript theme={null} transform(input: Order): EnrichedOrder { const moment = require('moment'); return { _id: input._id, processed_at: moment().format(), priority: input.total > 100 ? 'high' : 'normal' }; } ``` ### Sensor Data ```typescript TypeScript theme={null} transform(input: SensorReading): ProcessedSensor { const _ = require('lodash'); return { sensor_id: input.sensor_id, average: _.meanBy(input.readings, 'value'), count: input.readings.length }; } ``` ### Order Processing (Actual Implementation) ```typescript TypeScript theme={null} transformOrderType1(inputOrder: OrderType1): MergedOrder { const now = moment(); // Example using lodash for data manipulation const cleanedOrder = _.omitBy(inputOrder, _.isUndefined); const hasValidCustomer = _.has(cleanedOrder, 'customer.name') && !_.isEmpty(cleanedOrder.customer.name); // Example using uuid for unique identifiers const processingId = uuidv4(); return { version: '0.1.4', _id: inputOrder._id, order_number: inputOrder.order_number, processed_at: now.toISOString(), processed_time: now.format('YYYY-MM-DD HH:mm:ss'), processing_id: processingId, // uuid generated has_valid_customer: hasValidCustomer, // lodash validation field_count: _.keys(cleanedOrder).length, // lodash utility // ... other fields }; } ``` ## Key Files You'll Work With **New Project:** ``` src/ ├── OrderTransformer.ts # Your main business logic goes here ├── OrderType1.ts # Define what your input data looks like ├── MergedOrder.ts # Define what your output data looks like ├── *.test.ts # Automated tests (optional to modify) └── index.ts # System file (don't modify) ``` **Existing Project:** ``` src/templates/ ├── commonTransform.ts # Connect your existing services here ├── valueTransform.ts # Main value transformation logic ├── keyTransform.ts # Key transformation for partitioning ├── topicTransform.ts # Topic routing for fan-out └── index.ts # System file (don't modify) ``` **Both approaches generate:** ``` transforms/ # Generated files (copy these to Streamkap) ``` ## What You Get * **Type Safety**: Catch errors before deployment with TypeScript * **Rich Ecosystem**: Use thousands of npm packages in your transforms * **Testing**: Automated tests ensure your code works correctly * **Self-Contained**: Generated files include everything needed - no external dependencies * **All Transform Types**: Works with every Streamkap transform type * **Two Integration Options**: Use as standalone project or integrate into existing codebase * **Standard Workflow**: Familiar git-based development process ## Troubleshooting **Tests failing?** ```bash Terminal theme={null} npm run build # Build first - tests validate generated files npm test -- --verbose ``` **Build errors?** ```bash Terminal theme={null} npm install npm run build 2>&1 | grep -i error ``` **Only need specific transforms?** ```bash Terminal theme={null} # Build individual transform types for faster development npm run build:map-filter # Only builds map/filter transforms npm run test:enrich-async # Builds and tests async enrichment # Test without building (uses existing builds) npm test -- --map-filter # Test only map/filter transforms npm test -- --fan-out --un-nesting # Test multiple specific types # Build multiple specific types node build-multiple.js --fan-out --un-nesting # Available types: --map-filter, --fan-out, --enrich-async, --un-nesting, --all ``` **Streamkap deployment not working?** * Make sure you copy the **entire file contents** (including all the bundled code) * Always run `npm test` locally before deploying * Check the Streamkap error console for specific error messages Always run `npm run build` before `npm test`. The tests check the actual files that get deployed to Streamkap. ## Need More Details? This guide covers the basics. For advanced usage, troubleshooting, and detailed explanations, check the README.md file in the repository. # Overview Source: https://docs.streamkap.com/transforms ## Transform Types Below are the current and upcoming transform types Streamkap offers for data transformation: | Transform Name | Description | Supported Languages | Status | | --------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | --------------- | | [`Transform / Filter Records`](/transform-filter-records) | Apply custom logic to modify or filter records. | JavaScript | ✅ Available | | [`Enrich`](/transform-enrich) | Lookup values from a cached topic to enrich records. Handles schema evolution and pass-through fields. | SQL | ✅ Available | | [`Join`](/transform-join) | Join multiple topics. Supports schema evolution and pass-through fields. | SQL | ✅ Available | | [`Enrich (Async)`](/transform-enrich-async) | Asynchronously call REST APIs to enrich incoming records using dynamic payloads. | JavaScript, Python | ✅ Available | | `Rollup` | Perform in-stream aggregations using familiar SQL `GROUP BY` syntax. Correlate multiple CDC events for the same entity, aggregating by primary key. | SQL | ✅ Available | | `Un Nesting` | Break nested objects or arrays into separate topics. Ideal for unrolling deeply nested documents. | JavaScript, Python | 🛠️ Coming Soon | | [`Fan Out`](/transform-fanout) | Route a single record to multiple output topics based on custom logic. | JavaScript, Python | ✅ Available | | `Topic Router` | Merge, copy, or route topics via the Kafka destination connector with byte pass-through. Rewrites topic names and Avro schema IDs without value deserialization. | — | ✅ Available | | `Toast Handling` | Replace PostgreSQL `TOAST` placeholders with cached, encrypted original values to avoid costly re-selects. | TBC | 🛠️ Coming Soon | ## Development Tools Professional development environments and starter kits for building transforms: | Tool Name | Description | Transform Types Supported | Status | | ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- | ----------- | | [`TypeScript Development Kit`](/transform-typescript-development-kit.mdx) | Professional development environment with TypeScript, npm dependencies, comprehensive testing, and production bundling. | Map/Filter, Fan Out, Enrich (Async), Un-nesting | ✅ Available | If you don't see a transform listed, please contact us as we're happy to help find and provide the transform you need. ## See Also * [Transform Ordering](/transform-ordering) - Understand how transforms are ordered and executed within a pipeline * [Transform Examples](/transform-examples) - Practical examples and recipes for common transform use cases # Streaming Transforms Source: https://docs.streamkap.com/transforms-1 Create and manage Apache Flink-based data transformations for filtering, enrichment, and aggregation Streaming Transforms allow you to apply real-time data transformations using Apache Flink. Transform records as they flow through Kafka topics with custom JavaScript logic for filtering, enrichment, aggregation, and more. ## Overview The Transforms page displays all active transforms with their current status and performance metrics: ### Page Actions * **Create Transform**: Launch the transform creation wizard * **Search**: Filter transforms by name * **Refresh**: Reload the transforms list ### Transforms Table The table displays all transforms with the following columns: * **Name**: User-defined transform name (click to view details) * **Type**: Transform type (e.g., "Transform/Filter Records", "Aggregate", "Join", "Enrich") * **Status**: Current transform job status * `RUNNING`: Transform is actively processing records * `RESTARTING`: Transform is restarting due to failure or configuration change * `FAILED`: Transform has failed and requires attention * `UNKNOWN`: Status cannot be determined * **Start Time**: Timestamp when the current transform job started * **Duration**: How long the current job has been running * **End Time**: Timestamp when the job ended (for completed/failed transforms) * **Latency**: Average time for records to be processed by this transform * **Tasks**: Number of parallel tasks executing the transform logic (degree of parallelism) ### Row Actions Menu Click the actions menu (⋮) on any transform row to access: * **View**: Navigate to transform detail page * **Copy ID**: Copy transform UUID to clipboard (useful for API calls and support tickets) * **Clone**: Create a copy of this transform with the same configuration * **Delete**: Remove the transform (requires confirmation) Row actions menu showing View, Copy ID, Clone, and Delete options ## Bulk Actions Select multiple transforms using the checkboxes on the left side of the table to perform actions on multiple transforms at once. When transforms are selected, the bulk actions bar appears at the top of the table. Available bulk actions: * **Clone**: Create copies of all selected transforms with "\_copy" appended to their names * **Delete**: Remove all selected transforms (requires confirmation) You can select individual transforms or use "Select all X matching transform" to select all transforms in the current view. Deleting transforms is permanent and will stop all transform jobs immediately. Ensure you have backups of transform logic if needed. Use the Clone action to quickly create variations of existing transforms for testing or deploying to different environments. ## Transform Detail Page Click any transform name to view detailed information across three tabs: Status, Settings, and Implementation. ### Status Tab The Status tab displays real-time metrics and output topics: **Stats Cards** Key performance metrics for the transform: * **Status**: Current job status (`RUNNING`, `RESTARTING`, `FAILED`, or `UNKNOWN`) * **Tasks**: Number of parallel tasks (degree of parallelism) * **Latency**: Average record processing time * **Duration**: How long the current job has been running **Transform Information** * **Name**: User-defined transform name * **Type**: Transform type (e.g., "Transform/Filter Records", "Aggregate", "Join") * **Job**: Apache Flink job identifier **Topics Table** Lists all output topics created by this transform: * **Name**: Output topic name (click to view topic details) * **Volume**: Total data volume written to this topic * **Errors**: Number of processing errors * **Written**: Total number of records written Transforms can write to multiple output topics depending on the transform logic and routing rules. ### Settings Tab Configure transform parameters and resource allocation: **Basic Settings** * **Name**: Transform display name (editable) * **Tags**: Custom tags for organizing transforms * **Language**: Transform implementation language. Available options depend on the transform type (JavaScript, Python, or SQL). **Topic Patterns** Define which topics the transform processes and where output is written: * **Input Pattern**: Regular expression matching input topic names * Example: `.*\.db_shard.*` matches all topics containing ".db\_shard." * Supports standard regex syntax * **Output Pattern**: Template for output topic names * Example: `db_users.users_aggregated` creates a single output topic * Can include variables for dynamic topic creation **Performance Tuning** * **Transform Parallelism**: Slider to adjust the number of parallel tasks * Start with 1-5 for most workloads, then scale up based on lag metrics. * For high-throughput transforms, raise parallelism toward the number of input topic partitions (parallelism above partition count yields no extra throughput). * Higher parallelism increases throughput but consumes more resources. ### Implementation Tab The Implementation tab contains the transform logic code and testing interface. **Transform Code Editor** * Write or edit transformation logic in the language selected on the Settings tab (JavaScript, Python, or SQL — depending on the transform type) * Access to Streamkap transform APIs and utilities * Syntax highlighting and error detection **Testing Interface** * Test transform logic with sample records * View transformation output before deploying * Debug transformation errors Refer to the [Transform Types](/transforms) documentation for specific implementation examples and available APIs for each transform type. ## Best Practices 1. **Start with Low Parallelism**: Begin at 1-5 for most workloads (or match input partition count for known high-throughput transforms), then scale up based on lag metrics 2. **Monitor Latency**: Watch the latency metric to identify performance bottlenecks 3. **Use Descriptive Names**: Name transforms clearly to indicate their purpose 4. **Test Before Deploying**: Use the Implementation tab testing interface to validate logic 5. **Handle Errors Gracefully**: Implement error handling in transform code to prevent job failures 6. **Review Output Topics**: Regularly check output topic metrics for unexpected errors or volume 7. **Optimize Input Patterns**: Use specific regex patterns to avoid processing unnecessary topics 8. **Document Transform Logic**: Add comments in Implementation tab to explain complex transformations ## Troubleshooting ### Transform Status Shows RESTARTING If a transform continuously restarts: 1. **Check Logs**: Review transform logs in the [Logs](/logs) page for error messages 2. **Verify Input Topics**: Ensure input pattern matches existing topics 3. **Test Transform Logic**: Use Implementation tab to test with sample data 4. **Check Resources**: Verify project has sufficient resources for the parallelism level 5. **Review Recent Changes**: Revert recent settings or code changes that may have caused the issue ### High Latency If transform latency is increasing: 1. **Increase Parallelism**: Scale up the number of tasks in Settings tab 2. **Optimize Transform Code**: Review and optimize JavaScript logic in Implementation tab 3. **Check Input Lag**: Verify input topics don't have excessive consumer lag 4. **Review Resource Usage**: Ensure project has sufficient CPU and memory 5. **Partition Input Topics**: Increase partitions on input topics for better parallelism ### No Output Records If the transform shows zero written records: 1. **Verify Input Pattern**: Ensure regex pattern matches actual topic names 2. **Check Transform Logic**: Confirm logic doesn't filter out all records 3. **Review Input Topics**: Verify input topics contain data 4. **Check for Errors**: Look at Errors metric and review logs 5. **Test Implementation**: Use Implementation tab testing to validate logic ## Related Documentation * [Transform Types](/transforms) - Detailed guides for each transform type (Filter, Aggregate, Join, Enrich) * [Pipelines](/pipelines) - How transforms integrate into data pipelines * [Topics](/topics) - Understanding input and output topics * [Logs](/logs) - Troubleshooting transform errors * [Projects](/projects) - Managing project resources for transforms # Billing & Usage Source: https://docs.streamkap.com/usage Monitor data usage and understand Streamkap billing metrics Streamkap charges based on data volume (in GB) using the maximum of inbound (from sources) and outbound (to destinations) data. The Usage page provides comprehensive usage tracking across multiple time ranges, helping you understand consumption patterns and optimize costs. ## Pricing Model Streamkap uses a **volume-based pricing model**: * **Charged based on**: GB of data using the maximum of inbound (sources) and outbound (destinations) * **Billing metric**: Maximum of inbound and outbound data * **Volume discounts**: Available for larger consumption tiers When migrating from a Monthly Active Rows model to a per-GB model, use an approximate conversion rate of **5 million rows per GB**. For the latest pricing details, visit the [Streamkap Pricing Page](https://streamkap.com/pricing). ## Usage Overview The Usage page displays usage data across three key metrics: Billable Data, Inbound Data, and Outbound Data. Usage overview showing Billable Data, Inbound Data, and Outbound Data tiles across multiple time ranges ### Billable Data The primary billing metric represents the **maximum of inbound and outbound data** for each time period: * **1 HOUR**: Real-time usage in the past hour * **1 DAY**: Total usage in the past 24 hours * **7 DAYS**: Weekly usage accumulation * **30 DAYS**: Monthly usage (common billing cycle) * **90 DAYS**: Quarterly usage trends * **365 DAYS**: Annual usage for long-term planning **Calculation**: `Billable Data = MAX(Inbound Data, Outbound Data)` for each time period. The billable amount uses the maximum to ensure you're charged for the actual data movement, whether it's ingestion from sources or delivery to destinations. ### Inbound Data Tracks data **read from source connectors** into Streamkap Kafka topics: * Measures raw data ingested from databases (MySQL, PostgreSQL, DynamoDB, etc.) * Includes change data capture (CDC) events and initial snapshots * Source of truth for data entering your Streamkap project ### Outbound Data Tracks data **written from Kafka topics to destination connectors**: * Measures data delivered to data warehouses and lakes * Includes writes to BigQuery, Snowflake, ClickHouse, Databricks, etc. * May differ from inbound if using filtering, transformations, or partial replication Outbound data can be lower than inbound if you filter data before writing to destinations, or higher if you replicate the same source data to multiple destinations. ## Usage Over Time The interactive time series chart visualizes data flow over time: Usage chart displaying time series data for Sources Volume and Destinations Volume over the last 24 hours **Features:** * **Time Range Selector**: Last 24 hours, Last 7 days, Last 30 days * **Metric Toggle**: Select which metrics to display (Destinations - Volume, Sources - Volume) * **Hover Tooltips**: View exact values at specific time points * **Data Granularity**: Hourly for 24h view, daily for longer periods * **Legend**: Click legend items to show/hide metric lines **Use Cases:** * Identify usage spikes and patterns * Correlate usage with business events * Plan capacity and budget based on trends * Detect anomalies or unexpected data volumes ## Connector Usage Tables Track usage per individual connector with detailed breakdowns. ### Sources - Inbound Lists all source connectors with their inbound data usage: Sources Inbound table showing source connector usage data across 1 Hour, 1 Day, and 7 Days time periods **Table Columns:** * **Source Connector**: Connector name with icon (click to navigate to connector detail) * **1 Hour**: Data ingested in the past hour * **1 Day**: Data ingested in the past 24 hours * **7 Days**: Weekly ingestion volume * *Additional time ranges available via column selector* **Features:** * **Search**: Filter sources by name * **Export Usage**: Download usage data as CSV for reporting * **Column Selector**: Choose which time periods to display (1 Hour, 1 Day, 7 Days, 30 Days, 90 Days, 365 Days) * **Show/Hide Table**: Toggle table visibility with checkbox * **Pagination**: Navigate through large connector lists **Sorting:** * Click column headers to sort by usage in that time period * Identify highest-consuming sources quickly ### Destinations - Outbound Lists all destination connectors with their outbound data usage: Destinations Outbound table showing destination connector usage data across 1 Hour, 1 Day, and 7 Days time periods **Table Columns:** * **Destination Connector**: Connector name with icon (click to navigate to connector detail) * **1 Hour**: Data written in the past hour * **1 Day**: Data written in the past 24 hours * **7 Days**: Weekly write volume * *Additional time ranges available via column selector* **Features:** * Same search, export, column selector, and pagination capabilities as Sources table * Helps identify destinations consuming the most bandwidth * Useful for optimizing replication strategy Use the Export Usage button to download CSV files for integration with your accounting systems or for detailed cost analysis. ## Understanding Your Bill ### How Billing is Calculated 1. **Measure Period**: Typically 30 days (monthly billing cycle) 2. **Calculate Billable Data**: MAX(Inbound Data, Outbound Data) for the period 3. **Apply Pricing Tier**: Based on total volume and any volume discounts 4. **Generate Invoice**: Charges based on actual consumption ### Example Calculation If in a 30-day period you have: * **Inbound Data**: 125.5 GB * **Outbound Data**: 148.3 GB Your **Billable Data** would be **148.3 GB** (the maximum), and you'd be charged based on that volume according to your pricing tier. ### Cost Optimization Strategies 1. **Filter Early**: Use table/column filters in source connectors to reduce inbound volume 2. **Optimize Replication**: Only replicate to destinations that need the data 3. **Use Transforms**: Apply data transformations to reduce payload sizes 4. **Monitor Trends**: Watch the metrics chart for unexpected spikes 5. **Review Per-Connector**: Identify high-volume connectors and optimize their configuration 6. **Compress Data**: Use compression settings where available 7. **Schedule Snapshots**: Run full snapshots during off-peak times if possible ## Best Practices 1. **Set Up Alerts**: Monitor usage thresholds and get notified of spikes (see [Alerts](/alerts)) 2. **Regular Exports**: Download connector usage reports monthly for record-keeping 3. **Optimize Sources**: Start with source-side filtering to reduce overall data movement 4. **Review Inactive Connectors**: Pause or delete connectors that aren't actively used 5. **Test in Non-Production**: Use separate projects for testing to avoid unexpected charges 6. **Understand Your Data**: Know which tables/topics generate the most volume 7. **Plan for Growth**: Use historical data to forecast usage as your business scales 8. **Leverage Volume Discounts**: Contact sales if you're approaching higher usage tiers ## Troubleshooting ### Usage Doesn't Match Expectations If usage numbers seem incorrect: 1. **Check Time Zone**: Usage metrics use UTC timestamps 2. **Verify Connectors**: Ensure all connectors are accounted for 3. **Review Recent Changes**: Check if new sources/destinations were added 4. **Inspect High-Volume Sources**: Sort connector tables by usage to find outliers 5. **Check for Snapshots**: Full table snapshots can cause large usage spikes 6. **Review Logs**: Check connector logs for unusual activity ([Logs](/logs)) ### Unexpected Billing Spike If you see a sudden increase in usage: 1. **Check Metrics Chart**: Identify exactly when the spike occurred 2. **Review Connector Tables**: Find which connector(s) caused the spike 3. **Inspect Connector Logs**: Look for error patterns or configuration changes 4. **Verify Data Sources**: Check if source databases had bulk inserts or updates 5. **Contact Support**: Reach out to Streamkap support for billing questions ### Zero Usage Showing If connectors show 0 bytes but should have data: 1. **Check Connector Status**: Ensure connectors are running ([Pipelines](/pipelines)) 2. **Verify Time Range**: Some time periods may not have data yet (e.g., past hour) 3. **Refresh Page**: Reload to fetch latest usage data 4. **Check Date Range**: Ensure you're looking at the correct time window 5. **Review Pipeline Logs**: Confirm data is actually flowing # Audit Logs Source: https://docs.streamkap.com/view-audit-logs View and search audit logs to track activity in your organization Audit Logs can be found in **Account preferences** under the **Workspace** section. Click your avatar in the top navigation bar and select **Account preferences**, then click **Audit Logs**. Audit Logs showing activity history with time, user, action, and severity ## Overview Streamkap maintains a complete audit trail of all activity within your organization, providing visibility for security compliance and troubleshooting. ## Audit Log Details Each log entry includes: * **Time** - When the action occurred * **User Agent** - The browser or application used * **User (email)** - Who performed the action * **Action** - What was done (e.g., User logged in, Created API key, Updated profile) * **Description** - Additional context about the action * **Severity** - The importance level (Info, Warning, Error) ## Features * **Search** - Search for any text across all log entries * **Filter** - Filter by time, user agent, email, action, description, or severity * **Sort** - Sort columns by clicking the column headers * **Download** - Export audit logs for external analysis or compliance reporting * **Expand** - Click on any row to see additional details # Overview Source: https://docs.streamkap.com/vitess Streamkap supports syncing Vitess in real-time using change data capture for: If you don't see your platform listed, please contact us as we're happy to help find and provide the setup documentation you need. # Vitess (Generic) Source: https://docs.streamkap.com/vitess-generic # Prerequisites * Vitess version ≥ 12.0.*N* # Vitess Setup Vitess’s VStream feature was introduced in version 4.0. It is a change event subscription service that provides equivalent information to the MySQL binary logs from the underlying MySQL shards of the Vitess cluster. A user can subscribe to multiple shards in a keyspace, making it a convenient tool to feed downstream CDC processes. To read and process database changes, the Connector subscribes to VTGate's VStream gRPC service. VTGate is a lightweight, stateless gRPC server, which is part of the Vitess cluster setup. The Connector gives you the flexibility to choose to subscribe to the `MASTER` nodes, or to the `REPLICA` nodes for change events. ## Granting Privileges Because the Connector reads change events from the VTGate VStream gRPC server, it does not need to connect directly to MySQL instances. Therefore, no special database user and permissions are needed. ## Enable Change Data Capture The Connector does not require any specific configuration for use with Vitess. However, make sure that these are accessible to Streamkap: * VTGate host and its gRPC port (default is 15991) * VTCtld host and its gRPC port (default is 15999) # Streamkap Setup * Go to [Sources](https://app.streamkap.com/connectors/sources), click Add and choose **Vitess** * Input the following information: * Name for your Connector * Hostname - IP address or hostname of the Vitess database server (VTGate). * Port (default: `15991`) - port number of the Vitess database server. * VTGate Server Username - An optional username of the Vitess database server (VTGate). If not configured, unauthenticated VTGate gRPC is used. * VTGate Server Password - An optional password of the Vitess database server (VTGate). If not configured, unauthenticated VTGate gRPC is used. * Keyspace Name - The name of the keyspace from which to stream the changes. * Tablet Type - The type of Tablet (hence MySQL) from which to stream the changes. * VTCtld Server Hostname - IP address or hostname of the VTCtld server. * VTCtld Server Port (default: `15999`) - port number of the VTCtld server. * VTCtld Server Username - The username of the VTCtld server. * VTCtld Server Password - The password of the VTCtld database server. * Connect via SSH Tunnel. See [SSH Tunnel](/ssh-tunnel) * Add Schemas/Tables. Can also bulk upload here. The format is a simple list of each schema or table per row saved in csv format without a header. **CDC only captures base tables, not Views** Change Data Capture reads the MySQL binary log (binlog) via Vitess VStream, which only records changes to physical tables. Database Views are query-time computations with no physical storage—they don't generate binlog entries. **What you cannot capture:** Views, temporary tables, MEMORY tables, BLACKHOLE tables, or system tables (information\_schema, performance\_schema). **Solution:** Specify only the underlying base tables that feed your views. You can recreate the view logic in your destination or transformation layer. * Click Save # VPN Source: https://docs.streamkap.com/vpn Streamkap supports VPN connections but these are not self setup. Contact your account manager to setup # Weaviate Source: https://docs.streamkap.com/weaviate Stream data from Kafka topics into Weaviate vector collections in real-time ## Overview Use the Weaviate Sink Destination to stream data from your Kafka topics into Weaviate collections. This connector is useful for building real-time vector databases, enabling semantic search, and maintaining synchronized vector embeddings. Whether you're vectorizing text data with Weaviate's built-in vectorizers or bringing pre-computed embeddings, this connector handles the entire ingestion pipeline. ## Prerequisites * A Weaviate instance (self-hosted or Weaviate Cloud Service) * Connection details: REST URL and gRPC URL * Authentication credentials (if required by your Weaviate instance) * Understanding of your ID and vectorization strategy (see below) * Note: Collections do not need to be pre-created if **Schema Evolution** is enabled (default behavior) ## Key Concepts ### Collections & Topics Collections in Weaviate are similar to tables in traditional databases. Each Kafka topic is mapped to a Weaviate collection. By default, a Kafka topic named `users` maps to a collection named `users`. You can customize this mapping using the **Collection Mapping** setting. ### Document IDs & Upsert Operations Weaviate uses UUIDs to uniquely identify objects. The connector supports multiple ID strategies: * **NoIdStrategy** (default) – Generates a new UUID for each record, always creating new objects (INSERT semantics) * **FieldIdStrategy** – Uses a field from your Kafka record as the document ID, enabling upserts * **KafkaIdStrategy** – Uses the Kafka message key as the document ID When using **FieldIdStrategy**, specify the field name (e.g., `id`, `user_id`) in the **Document ID Field** setting. If your Kafka record contains an `id` field and you want to preserve it in Weaviate, note that `id` is a reserved keyword in Weaviate. The connector automatically renames it to `__id` in the stored object. ### Vectorization & Bring Your Own Vectors (BYOV) * **Default (NoVectorStrategy)** – Weaviate generates embeddings using the vectorizer specified in your collection configuration (e.g., OpenAI, Cohere, Hugging Face). For newly created collections via Schema Evolution, the **Default Weaviate Vectorizer** setting determines which vectorizer is used. * **Bring Your Own Vectors (BYOV)** – If embeddings are pre-computed outside Weaviate, use **FieldVectorStrategy** and specify the field containing the embedding vector. **Note:** BYOV supports only one vector field per collection. If your Kafka records contain multiple vector fields, only the configured vector field will be used. ### Delete Operations If **Delete Enabled** is set to true, records with null values are treated as deletes. For example, when a record is deleted in the source and a tombstone record (with null value) is sent to Kafka, the connector will delete the corresponding object from Weaviate. ## Weaviate Preparation Before configuring the connector, prepare your Weaviate instance: ### Gather Connection Details #### For self-hosted Weaviate: * REST URL: `http://:` (default: `http://localhost:8080`) * gRPC URL: `:` (default: `localhost:50051`) #### For Weaviate Cloud Service (WCS): * REST URL: Available in your WCS dashboard (https\://\) * gRPC URL: Available in your WCS dashboard * Enable gRPC TLS: Set to true for WCS ### Create Collections (Optional with Schema Evolution) By default, **Schema Evolution** is enabled, allowing the connector to automatically create collections from incoming Kafka records. However, you may want to pre-create collections if: * You want to define custom vectorizers (e.g., specific OpenAI model, Cohere, Hugging Face) before data arrives * You want to configure custom collection properties not present in the first Kafka record * You prefer explicit schema control in production environments Alternatively, when **Schema Evolution** is enabled and collections are created automatically, you can use the **Default Weaviate Vectorizer** setting to specify which vectorizer should be used for newly created collections. This allows you to control vectorization without pre-creating collections. If you pre-create collections, use Weaviate's UI, API, or Python client: ```bash theme={null} # Example using Python client from weaviate.connect import ConnectionParams from weaviate.client import Client from weaviate.classes.config import Property, DataType, Vectorizer client = Client( connection_params=ConnectionParams.http(host="localhost", port=8080) ) # Create a collection with OpenAI vectorizer client.collections.create( name="Products", properties=[ Property(name="title", data_type=DataType.TEXT), Property(name="description", data_type=DataType.TEXT), Property(name="price", data_type=DataType.NUMBER), ], vectorizer_config=Vectorizer.text2vec_openai( model="text-embedding-3-small" ), ) ``` Note: * Collection names should match your Kafka topic names (or configure via **Collection Mapping**) * If a collection doesn't exist and Schema Evolution is enabled, the connector will create it automatically * If a collection exists and new properties arrive in Kafka records, Weaviate will automatically add them to the collection schema ### Prepare Authentication (if needed) #### API Key Authentication: * Generate an API key from your Weaviate instance settings * Have it ready to paste during setup #### OIDC Authentication: * Gather Client ID, Client Secret, and OIDC Token URL * Ensure OIDC is enabled on your Weaviate instance ## Streamkap Setup (UI) 1. Navigate to [Destinations](https://app.streamkap.com/connectors/add?tab=Destinations) and choose **Weaviate**. 2. Fill in the fields: 1. **Name** – A memorable identifier for this Destination. 2. **Weaviate REST URL** – The REST endpoint. Examples: * Self-hosted: `http://localhost:8080` * WCS: `https://my-instance.gcp.weaviate.cloud` 3. **Weaviate gRPC URL** – The gRPC endpoint. Examples: * Self-hosted: `localhost:50051` * WCS: `my-instance.gcp.weaviate.cloud` 4. **gRPC TLS Secured** – Enable if your gRPC connection requires TLS encryption (required for WCS). 5. **Authentication Type** – Select `None`, `API Key`, or `OIDC`: * **None** – No authentication (suitable for local development) * **API Key** – Use an API key generated by Weaviate * **OIDC** – Use OpenID Connect Client Credentials flow 6. **API Key** (if API Key auth selected) – Paste your Weaviate API key. 7. **OIDC Client ID** (if OIDC selected) – Your OIDC client identifier. 8. **OIDC Client Secret** (if OIDC selected) – Your OIDC client secret. 9. **OIDC Scopes** (if OIDC selected, optional) – Comma-separated scopes (e.g., `openid profile email`). 10. **Custom Headers** – Optional headers to include in all requests (e.g., `X-OpenAI-Api-Key` for embedding provider keys). 11. **Collection Mapping** – Pattern to map Kafka topics to Weaviate collections. Default: `${topic}`. Examples: * `${topic}` – Topic `users` → Collection `users` * `weaviate_${topic}` – Topic `users` → Collection `weaviate_users` * `production_${topic}_v2` – Topic `users` → Collection `production_users_v2` 12. **Document ID Strategy** – Choose how to assign IDs to objects: * **No ID Strategy** – Generate new UUID for each record (always inserts) * **Field ID Strategy** – Use a field from the record as the UUID * **Kafka ID Strategy** – Use the Kafka message key as the UUID 13. **Document ID Field** (if Field ID Strategy selected) – The field name containing the ID (e.g., `id`, `user_id`). This field must exist in your Kafka records. 14. **Vector Strategy** – Choose how embeddings are handled: * **No Vector Strategy** – Let Weaviate vectorize using its configured vectorizer * **Field Vector Strategy** – Use a pre-computed embedding from a field 15. **Vector Field Name** (if Field Vector Strategy selected) – The field containing the embedding vector (e.g., `embedding`, `vector`). Must be an array of numbers. 16. **Default Weaviate Vectorizer** (Advanced) – Sets the default vectorizer for newly created collections when Schema Evolution is enabled. Options include: * `none` – No vectorization (use this if you're providing your own vectors via Field Vector Strategy) * `text2vec-weaviate` – Weaviate's built-in text vectorizer * `text2vec-openai` – OpenAI embeddings (requires API key in Custom Headers) * `text2vec-cohere` – Cohere embeddings (requires API key in Custom Headers) * `text2vec-jinaai` – Jina AI embeddings (requires API key in Custom Headers) * `text2vec-voyageai` – Voyage AI embeddings (requires API key in Custom Headers) 17. **Delete Enabled** – If true, null-valued records are treated as deletes. Useful for delete propagation from source systems. 18. **Schema Evolution** (default: `basic`) – Controls automatic collection and property management: * **basic (default)** – The connector automatically creates missing collections from incoming Kafka records. If new properties appear in later records, Weaviate automatically adds them to the collection schema. This is ideal for development and dynamic data pipelines. * **none** – The connector expects collections to exist in Weaviate and will fail if a collection is missing. Use this in production for strict schema control. 19. **Batch Size** – Number of records to batch before sending to Weaviate (default: 100). Larger batches improve throughput; smaller batches reduce latency. 20. **Pool Size** – Number of worker threads processing batches (default: 1). Increase for parallel processing if Weaviate can handle concurrent requests. 21. **Await Termination (ms)** – Timeout in milliseconds for batch processing before forcing termination (default: 10000). 22. **Max Retries** – Maximum retry attempts on connection/timeout errors (default: 3). 23. **Retry Interval (ms)** – Delay between retries (default: 2000). 3. Click **Save**. ## How It Works 1. **Record Ingestion** – Records from Kafka are received by the connector. 2. **ID Generation** – Based on the ID strategy, a UUID is assigned or extracted. 3. **Vector Assignment** – If using Field Vector Strategy, the embedding is extracted; otherwise, it's left for Weaviate to compute. 4. **Reserved Field Handling** – Fields named `id` or `_id` are renamed to `__id` to avoid Weaviate keyword conflicts. 5. **Batching** – Records are batched according to **Batch Size**. 6. **Weaviate Storage** – The batch is sent to Weaviate via gRPC for ingestion. 7. **Upsert/Insert Semantics** – If using an ID strategy with existing UUIDs, objects are upserted (updated if they exist); otherwise, new objects are always inserted. 8. **Error Handling** – Failed batches are retried up to **Max Retries** times with exponential backoff. ## Reserved Fields & Field Renaming Weaviate has reserved keywords (`id`, `_id`). If your Kafka records contain fields named `id` or `_id`, the connector automatically renames them: * `id` → `__id` * `_id` → `__id` This preserves your data while avoiding conflicts with Weaviate's internal ID field. ### Example **Input from Kafka:** ```json theme={null} { "id": "user-123", "name": "Alice", "email": "alice@example.com" } ``` **After ingestion into Weaviate:** ```json theme={null} { "__id": "user-123", "name": "Alice", "email": "alice@example.com", "_additional": { "id": "" } } ``` ## Limitations & Best Practices ### Limitations * **Collections Require Pre-creation When Schema Evolution is Disabled** – If you disable Schema Evolution (`none` mode), collections must exist in Weaviate before streaming; the connector will fail if a collection is missing. * **Schema Mismatch with Evolution Disabled** – When Schema Evolution is disabled and a Kafka record contains a field not defined in the Weaviate collection schema, that field is silently dropped. Ensure your Weaviate schema matches your Kafka payload structure if evolution is disabled. * **Single Vector Field per Collection (BYOV)** – When using Field Vector Strategy (Bring Your Own Vectors), the connector supports only one vector field per collection. If your Kafka records contain multiple vector fields, only the configured vector field will be used for ingestion. * **Batch Retries** – If a batch fails, the entire batch is retried, potentially causing duplicate inserts if not using upsert semantics (e.g., with Field ID or Kafka ID strategies). ### Best Practices 1. **Use Field ID Strategy for Idempotency** – If your source has unique identifiers (e.g., user IDs, order IDs), use them as document IDs to enable idempotent upserts. 2. **Pre-compute Embeddings for Custom Models** – If you're using embeddings outside Weaviate, use BYOV (Field Vector Strategy) to avoid re-vectorization. Remember that only one vector field per collection is supported. 3. **Configure Default Vectorizer for New Collections** – Use the **Default Weaviate Vectorizer** setting to specify which vectorizer should be used for collections created automatically by Schema Evolution. This is more efficient than pre-creating collections if you're using standard vectorizers. 4. **Design Weaviate Schema for Custom Vectorizers** – If you want specific vectorizer configurations (e.g., OpenAI with a particular model, specific Cohere settings), pre-create collections with those settings. Schema Evolution uses the Default Weaviate Vectorizer for automatic collection creation. 5. **Monitor Weaviate Capacity** – Ensure your Weaviate instance can handle the ingestion rate. Start with small batch sizes and increase gradually. 6. **Handle Reserved Fields** – Be aware that `id` and `_id` are renamed to `__id`; adjust downstream queries if needed. 7. **Enable Deletion if Using CDC** – If your source is a database with CDC, enable **Delete Enabled** to propagate deletes. 8. **Use Custom Headers for Embedding APIs** – If your vectorizer needs API keys (e.g., OpenAI, Cohere, Jina AI), provide them via **Custom Headers**. ## Troubleshooting ### Collection Not Found Error **Problem:** Connector fails with "Collection \ not found" **Solution:** * This error typically occurs when **Schema Evolution** is disabled. Enable Schema Evolution if you want the connector to create collections automatically. * If you prefer Schema Evolution disabled for strict control, pre-create the required collection in Weaviate * Check that the **Collection Mapping** pattern correctly maps your topic name to the collection name * If using custom mapping, verify the generated collection name matches what exists in Weaviate ### Authentication Failures **Problem:** "Unauthorized" or "Forbidden" errors **Solution:** * Verify your API key or OIDC credentials are correct * Ensure the credentials have permissions to write to collections * For OIDC, check that the token URL is reachable and returns valid tokens ### gRPC Connection Issues **Problem:** "Failed to connect to gRPC endpoint" **Solution:** * Verify the gRPC URL is correct (host and port) * Ensure gRPC TLS is enabled if connecting to WCS * Check network connectivity and firewall rules * For self-hosted Weaviate, ensure gRPC is enabled on port 50051 ### Field Not Found or Data Loss **Problem:** Fields from Kafka records are missing in Weaviate **Solution:** * If **Schema Evolution** is enabled (default), new fields are automatically added to the collection schema by Weaviate. If you're not seeing a field, verify the field name is correct (case-sensitive). * If **Schema Evolution** is disabled and a Kafka record contains a field not defined in the Weaviate collection schema, that field will be silently dropped. Check that your Weaviate collection schema includes all expected fields. * Use Weaviate's schema inspection to see what properties are currently defined * Verify field names match exactly (case-sensitive) between Kafka records and Weaviate collection schema ### ID Field Conflicts **Problem:** `id` or `_id` fields are missing from ingested data **Solution:** * The connector renames these to `__id` respectively to avoid conflicts with Weaviate's internal ID field * Query your data and check for the renamed fields * If needed, query the `_additional.id` property to get the Weaviate UUID ### High Latency or Backpressure **Problem:** Connector is slow or not keeping up with Kafka **Solution:** * Increase **Batch Size** to reduce the number of requests to Weaviate * Increase **Pool Size** to enable parallel processing * Check Weaviate's CPU and memory; scale if needed * Monitor network latency between Kafka Connect and Weaviate ## Security Notes * **API Keys & Secrets** – Stored encrypted. Never share them in logs or config files shared publicly. * **HTTPS/TLS** – Always use HTTPS for REST connections and enable gRPC TLS for production. * **OIDC Tokens** – Tokens are refreshed automatically and not persisted in logs. * **Custom Headers** – Be careful with sensitive headers (e.g., API keys); they should be managed securely by your deployment platform. # Next Steps 1. Review Weaviate's [documentation](https://docs.weaviate.io/weaviate/guides) for collection design best practices 2. Test the connector with a small Kafka topic first 3. Monitor latency and throughput during initial deployment 4. Adjust batch size and pool size based on observed performance 5. Set up alerts for connector task failures # Webhook Source: https://docs.streamkap.com/webhook Receive Kafka records via HTTP POST/PUT/DELETE requests to a dynamically generated webhook endpoint # Overview Use the Webhook Source to ingest data into Streamkap by accepting HTTP requests at a dynamically generated endpoint. This is useful for integrating third-party services, webhooks from external platforms, or custom applications that push data via HTTP. The connector automatically routes incoming requests to Kafka topics based on the URL path or a default topic. # Prerequisites * An external service or application capable of sending HTTP POST, PUT, or DELETE requests * Understanding of your data format (JSON or string) * Plan for topic naming and routing strategy * API key for secure access to your webhook endpoint # How It Works 1. **Webhook Generation**: Once created, the connector generates a unique webhook URL and API key. 2. **Flexible Routing**: Requests to `https://webhook.endpoint/topic_name` are routed to the `topic_name` topic. Requests to the base URL route to the default topic. 3. **Automatic Topic Creation**: If you POST to a new path (e.g., `/orders`), the connector can automatically create that topic, but you should pre-register it in the **Schema** section for proper configuration. 4. **Data Transformation**: Incoming data is converted to JSON format and optionally enriched with metadata (e.g., HTTP method, deletion markers). 5. **Message Keys**: The connector extracts Kafka message keys from HTTP headers, enabling downstream systems to perform upserts and deletes based on unique identifiers. # Streamkap Setup (UI) 1. Navigate to [Sources](https://app.streamkap.com/connectors/add?tab=Sources) and choose **Webhook**. 2. Fill in the fields: 1. **Name** – A memorable identifier for this webhook source (e.g., "External Service Webhook"). 2. **Webhook URL** – Auto-generated and displayed as read-only after creation. This URL is unique to your webhook. Share this URL with external services. Example: `https://wh-abc123xyz-tenant-internal.streamkap.net`. 3. **API Key** – Auto-generated and displayed as read-only after creation. External services must include this in requests using the `X-API-Key` header. 4. **Data Format** – Choose `json` or `string`: * **JSON** – Expects JSON payloads; automatically parsed and converted to Kafka records. * **String** – Treats the raw HTTP body as a string value. 5. **Add Delete Field** – Enable to add a `delete: true` field when DELETE HTTP requests are received. Useful for marking deletions in downstream systems. 6. **Camel Message Header Key** – The name of the HTTP header containing the Kafka message key (default: `key`). This is critical for enabling upserts and deletes in downstream consumers. * The header value must be a valid JSON object representing the key fields. * The same key field(s) must also be present in the request body/payload. * Example: If you want to upsert records by `id`, set this to `key` and include both the header and body field. * See **Message Key Extraction** section below for detailed examples. 3. **Schema Configuration**: 1. Define the topics where incoming data will be stored. 2. **Default Topic Name** – The topic that receives all requests to the base webhook URL (default: `default_topic`). **Must be added to the Schema section below.** 3. **Topic Names** – Additional topics to route requests to based on URL paths. Add topic names you plan to use. Examples: * `orders` – Requests to `https://wh-abc123xyz-tenant-internal.streamkap.net/orders` route here. * `payments` – Requests to `https://wh-abc123xyz-tenant-internal.streamkap.net/payments` route here. * `events` – Requests to `https://wh-abc123xyz-tenant-internal.streamkap.net/events` route here. 4. **Important**: Always include `default_topic` in the list. If you expect dynamic topics, pre-register them here or wait for the automatic topic discovery service to detect them (which may take time). 4. Save the Source. 5. Attach to Pipelines and configure downstream Sinks as needed. # Authentication All requests **must** include the API key using the `X-API-Key` header: ``` X-API-Key: ``` Requests without a valid API key will be rejected with a `401 Unauthorized` response. ### Example Request with API Key ```bash theme={null} curl -X POST https://wh-abc123xyz-tenant-internal.streamkap.net/orders \ -H "Content-Type: application/json" \ -H "X-API-Key: sk-1234567890abcdef" \ -d '{"order_id": 12345, "customer": "John Doe", "amount": 99.99}' ``` # Message Key Extraction The connector supports extracting Kafka message keys from HTTP headers. This enables downstream systems to perform upserts (update or insert) and deletes based on unique identifiers. ## How It Works 1. **Header Configuration**: Set the **Camel Message Header Key** to the name of the HTTP header containing the key (default: `key`). 2. **Key Format**: The header value must be a valid JSON object representing the key fields. 3. **Dual Presence**: The key field(s) must appear in both the HTTP header AND the request body for consistency. 4. **Single Record Assumption**: The connector assumes each HTTP request contains exactly one message with one key. ## Key Extraction Examples ### Example 1: Simple ID-Based Key **Setup:** * Camel Message Header Key: `key` * Body includes `id` field **Request:** ```bash theme={null} curl -X POST https://wh-abc123xyz-tenant-internal.streamkap.net/orders \ -H "Content-Type: application/json" \ -H "X-API-Key: sk-1234567890abcdef" \ -H "key: {\"id\": 12345}" \ -d '{ "id": 12345, "customer": "John Doe", "amount": 99.99, "timestamp": "2024-01-15T10:30:00Z" }' ``` **Resulting Kafka Record (orders topic):** * **Message Key**: `{"id": 12345}` * **Message Value**: ```json theme={null} { "id": 12345, "customer": "John Doe", "amount": 99.99, "timestamp": "2024-01-15T10:30:00Z" } ``` **Downstream Usage**: A sink connector can now upsert records by the `id` field, replacing old records with the same `id`. ### Example 2: Composite Key **Setup:** * Camel Message Header Key: `key` * Body includes both `customer_id` and `order_number` fields **Request:** ```bash theme={null} curl -X POST https://wh-abc123xyz-tenant-internal.streamkap.net/orders \ -H "Content-Type: application/json" \ -H "X-API-Key: sk-1234567890abcdef" \ -H "key: {\"customer_id\": \"CUST-123\", \"order_number\": \"ORD-001\"}" \ -d '{ "customer_id": "CUST-123", "order_number": "ORD-001", "amount": 150.50, "status": "pending" }' ``` **Resulting Kafka Record (orders topic):** * **Message Key**: `{"customer_id": "CUST-123", "order_number": "ORD-001"}` * **Message Value**: ```json theme={null} { "customer_id": "CUST-123", "order_number": "ORD-001", "amount": 150.50, "status": "pending" } ``` ### Example 3: DELETE with Key **Setup:** * Camel Message Header Key: `key` * Add Delete Field: Enabled **Request:** ```bash theme={null} curl -X DELETE https://wh-abc123xyz-tenant-internal.streamkap.net/users \ -H "Content-Type: application/json" \ -H "X-API-Key: sk-1234567890abcdef" \ -H "key: {\"user_id\": \"USER-456\"}" \ -d '{ "user_id": "USER-456" }' ``` **Resulting Kafka Record (users topic):** * **Message Key**: `{"user_id": "USER-456"}` * **Message Value**: ```json theme={null} { "user_id": "USER-456", "delete": true } ``` **Downstream Usage**: A sink connector recognizes the `delete: true` field and removes the record with key `{"user_id": "USER-456"}` from the target system. ### Example 4: Custom Header Name **Setup:** * Camel Message Header Key: `msg_key` (custom header name) * Body includes `product_id` field **Request:** ```bash theme={null} curl -X POST https://wh-abc123xyz-tenant-internal.streamkap.net/products \ -H "Content-Type: application/json" \ -H "X-API-Key: sk-1234567890abcdef" \ -H "msg_key: {\"product_id\": \"PROD-789\"}" \ -d '{ "product_id": "PROD-789", "name": "Laptop", "price": 999.99, "stock": 50 }' ``` **Resulting Kafka Record (products topic):** * **Message Key**: `{"product_id": "PROD-789"}` * **Message Value**: ```json theme={null} { "product_id": "PROD-789", "name": "Laptop", "price": 999.99, "stock": 50 } ``` ## Best Practices for Message Keys * **Always Include Both**: Ensure the key field(s) exist in both the HTTP header and the request body. * **Use Immutable IDs**: Use unique, immutable identifiers (e.g., order ID, user ID) as keys to avoid data inconsistencies. * **Composite Keys**: For datasets requiring uniqueness across multiple fields, use composite keys (e.g., `customer_id` + `order_number`). * **Consistency Across Calls**: Use the same key structure for all requests to the same topic. * **Valid JSON**: The header value must be valid JSON. Invalid JSON in the key header will cause request failures. # Using Your Webhook Once the webhook is created, you'll see: * **Webhook URL**: The endpoint external services should POST/PUT/DELETE to * **API Key**: Required for authentication via `X-API-Key` header ### Routing Behavior | Request URL | Resulting Topic | Notes | | ----------------------------------------------------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------- | | `POST https://wh-abc123xyz-tenant-internal.streamkap.net` | `default_topic` | Base URL routes to default topic | | `POST https://wh-abc123xyz-tenant-internal.streamkap.net/orders` | `orders` | Path-based routing (must be pre-registered in Schema) | | `PUT https://wh-abc123xyz-tenant-internal.streamkap.net/customers` | `customers` | PUT requests also supported | | `DELETE https://wh-abc123xyz-tenant-internal.streamkap.net/users` with `key` header | `users` | DELETE requests require key header to identify the record to delete. Body must also contain the key field(s). | # Data Format & Transformation ## JSON Format Incoming JSON payloads are automatically parsed and sent as Kafka records: ```json theme={null} { "order_id": 12345, "customer": "John Doe", "amount": 99.99 } ``` Becomes a Kafka record with the JSON structure preserved. ## String Format Raw string bodies are wrapped in a `data` field: ``` "raw text data" ``` Becomes: ```json theme={null} { "data": "raw text data" } ``` ## DELETE Method Handling If **Add Delete Field** is enabled and a DELETE request is received: ```json theme={null} { "order_id": 12345, "delete": true } ``` This allows downstream systems to identify and process deletions. # Topic Discovery & Auto-Creation The webhook connector supports flexible URL paths: * **Pre-registered Topics** – Topics explicitly added in the Schema section always accept requests. * **Dynamic Topics** – New paths automatically create topics, but: * The topic must be added to the Schema section in advance for guaranteed routing. * If not pre-registered, a background topic discovery service will detect and register new topics, but this may take time (typically a few minutes). * To avoid delays, register all expected topics upfront. ### Example Workflow 1. You plan to receive data for `orders`, `payments`, and `events`. 2. Add all three topics to the **Topic Names** field in the Schema section. 3. External services can now reliably POST to `/orders`, `/payments`, or `/events`. 4. If a request arrives for `/refunds` (not pre-registered), the connector will create the topic, but there may be a delay before it appears in your pipeline. # Request Limits & Performance The webhook connector supports large payloads with the following defaults: | Setting | Default | Purpose | | --------------------------- | ------- | ----------------------------------- | | **Max Initial Line Length** | 16 KB | HTTP request line size limit | | **Max Header Size** | 64 KB | Total HTTP headers size limit | | **Max Chunk Size** | 50 MB | Maximum payload size per request | | **Backlog Size** | 200 | Queued connections before rejection | Adjust these in advanced settings if needed, but ensure your external services respect these limits. # Data Format Examples ### JSON POST to Orders Topic with Key **Request:** ```bash theme={null} curl -X POST https://wh-abc123xyz-tenant-internal.streamkap.net/orders \ -H "Content-Type: application/json" \ -H "X-API-Key: sk-1234567890abcdef" \ -H "key: {\"order_id\": \"ORD-001\"}" \ -d '{ "order_id": "ORD-001", "customer_id": "CUST-123", "amount": 150.50, "timestamp": "2024-01-15T10:30:00Z" }' ``` **Resulting Kafka Record (orders topic):** * **Key**: `{"order_id": "ORD-001"}` * **Value**: ```json theme={null} { "order_id": "ORD-001", "customer_id": "CUST-123", "amount": 150.50, "timestamp": "2024-01-15T10:30:00Z" } ``` ### String POST to Events Topic **Request:** ```bash theme={null} curl -X POST https://wh-abc123xyz-tenant-internal.streamkap.net/events \ -H "Content-Type: text/plain" \ -H "X-API-Key: sk-1234567890abcdef" \ -d 'User logged in from IP 192.168.1.100' ``` **Resulting Kafka Record (events topic):** ```json theme={null} { "data": "User logged in from IP 192.168.1.100" } ``` ### DELETE Request with Delete Field and Key **Request:** ```bash theme={null} curl -X DELETE https://wh-abc123xyz-tenant-internal.streamkap.net/users \ -H "X-API-Key: sk-1234567890abcdef" \ -H "key: {\"user_id\": \"USER-456\"}" \ -d '{"user_id": "USER-456"}' ``` **Resulting Kafka Record (users topic, with Add Delete Field enabled):** * **Key**: `{"user_id": "USER-456"}` * **Value**: ```json theme={null} { "user_id": "USER-456", "delete": true } ``` # Common Scenarios ## Scenario 1: Multi-Topic Event Ingestion with Upserts **Setup:** * Default Topic: `default_topic` * Topic Names: `orders`, `payments`, `events`, `notifications` * Data Format: JSON * Camel Message Header Key: `key` **Usage:** * Third-party service POSTs orders to `.../orders` with a `key` header containing the order ID. * Billing system POSTs payments to `.../payments` with a `key` header containing the payment ID. * Downstream sink connectors upsert records based on these keys, updating existing records instead of creating duplicates. * Each goes to its respective topic, enabling independent processing. ## Scenario 2: Generic Event Logging **Setup:** * Default Topic: `webhook_events` * Topic Names: (empty or just default) * Data Format: String **Usage:** * All requests regardless of path go to `webhook_events`. * Useful for centralized logging of third-party webhooks. ## Scenario 3: Soft Deletes with Tombstones **Setup:** * Default Topic: `users` * Add Delete Field: Enabled * Data Format: JSON * Camel Message Header Key: `key` **Usage:** * POST `{"user_id": 123, "name": "Alice"}` with header `key: {"user_id": 123}` creates a record. * DELETE with `{"user_id": 123}` and header `key: {"user_id": 123}` adds `"delete": true` to mark deletion. * Downstream systems can process deletes accordingly, removing records by key. # Integration Examples ### Integrating Shopify Webhooks Configure Shopify to send order events to your webhook: ``` Webhook URL: https://wh-abc123xyz-tenant-internal.streamkap.net/shopify_orders Header X-API-Key: sk-1234567890abcdef ``` Shopify posts order data to your `shopify_orders` topic in real-time. ### Integrating GitHub Webhooks Configure GitHub to send push and pull request events: ``` Webhook URL: https://wh-abc123xyz-tenant-internal.streamkap.net/github_events Header X-API-Key: sk-1234567890abcdef ``` GitHub events flow into your `github_events` topic. ### Custom Application Integration with Keys From your application, send data via HTTP with message keys: ```python theme={null} import requests import json API_KEY = "sk-1234567890abcdef" WEBHOOK_URL = "https://wh-abc123xyz-tenant-internal.streamkap.net/my_events" headers = { "X-API-Key": API_KEY, "Content-Type": "application/json", "key": json.dumps({"event_id": "EVT-001"}) } data = { "event_id": "EVT-001", "event_type": "purchase", "user_id": "USER-001", "amount": 99.99 } response = requests.post(WEBHOOK_URL, json=data, headers=headers) print(response.status_code) # 202 Accepted ``` # Error Handling | Status Code | Meaning | Action | | ----------------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------- | | **202 Accepted** | Request received and queued for processing | Success; data will be in Kafka shortly | | **400 Bad Request** | Malformed request, invalid format, or invalid JSON in key header | Check JSON syntax, Content-Type header, and key header format | | **401 Unauthorized** | Missing or invalid API key | Verify the API key in the `X-API-Key` header | | **413 Payload Too Large** | Request exceeds max chunk size (50 MB) | Reduce payload size or split into multiple requests | | **429 Too Many Requests** | Rate limit exceeded | Implement backoff and retry logic | | **500 Internal Server Error** | Connector issue | Check connector logs; retry after a delay | # Security Notes * **API Key Protection**: Treat the API key like a password. Do not commit it to version control or expose in client-side code. Store securely in environment variables or secrets management systems. * **HTTPS Only**: Always use HTTPS for webhook requests in production. Plain HTTP is not recommended. * **Key Header Security**: The `key` header contains sensitive identifying information. Ensure HTTPS is used to prevent interception. * **IP Whitelisting**: If available, restrict webhook access to known IP ranges of external services. * **Payload Validation**: Implement server-side validation in downstream consumers to ensure data integrity and key consistency. * **Rate Limiting**: External services should implement exponential backoff for `429` responses. # Limitations * **No Built-in Deduplication**: If external services retry requests, duplicate records may appear. Implement deduplication downstream using unique message keys or IDs. * **Single Record per Request**: The connector assumes each HTTP request contains exactly one Kafka message. Batch requests are not supported at the connector level. * **Single-Instance Deployment**: The connector runs as a single task (`tasks.max=1`). Horizontal scaling requires multiple connector instances. * **No Request Buffering**: Incoming requests are processed sequentially. High-volume ingestion may experience latency. * **Topic Pre-Registration Recommended**: While auto-creation works, pre-registering topics in the Schema section ensures reliable routing without delays. * **Key Header Validation**: Invalid JSON in the key header will cause the request to fail. Always validate key format before sending. # Monitoring & Troubleshooting ## Checking Message Flow Use your pipeline dashboard to monitor: * **Events Written** – Total messages ingested from the webhook * **Volume** – Data volume in bytes * **Topic Distribution** – Messages per topic ## Common Issues **Issue**: Requests to new topics fail or are delayed **Solution**: Pre-register all expected topics in the Schema section. **Issue**: 401 Unauthorized errors **Solution**: Verify the API key is included in the `X-API-Key` header with the correct value. **Issue**: 400 Bad Request with JSON format selected **Solution**: Ensure the request body is valid JSON and the `Content-Type: application/json` header is set. Verify the key header contains valid JSON. **Issue**: Data appears in wrong topic **Solution**: Check the URL path matches a registered topic name. Typos or unregistered paths may route to the default topic. **Issue**: Downstream upserts not working **Solution**: Ensure the message key is correctly extracted. Verify the `key` header contains valid JSON and the same key fields exist in the request body. **Issue**: Invalid JSON in key header **Solution**: Use tools like `jq` or online JSON validators to ensure your key JSON is well-formed. Example: `key: $(echo '{"id": 123}' | jq -c .)`. # Salesforce CDC Source: https://docs.streamkap.com/webhook-salesforce Stream Salesforce change events into Streamkap using native Salesforce Change Data Capture, with optional Apex-trigger webhooks for objects that CDC does not cover ## Overview The Salesforce CDC source captures inserts, updates, deletes, and undeletes from your Salesforce org and writes them to Kafka topics — one topic per Salesforce object. Two ingestion modes are supported and can be used together: * **Native CDC** *(default)* — Streamkap subscribes directly to the Salesforce Change Data Capture stream using OAuth credentials. No Apex code required. * **Apex-trigger webhook** — Apex triggers in your org POST change events to a Streamkap-generated webhook URL. Use this for standard objects without CDC support, custom objects, or when you need fine-grained control. Both paths feed the same payload router, so downstream topics, keys, and flatten/include behavior are identical regardless of how the event arrived. This connector is in **Beta**. Behaviors and defaults may change before general availability. ## Prerequisites * A Salesforce org (Developer, Professional, Enterprise, or Unlimited edition) with **System Administrator** access. * For native CDC: the **Change Data Capture** feature enabled in your org and selected for the objects you want to capture (Setup → Change Data Capture). * OAuth Consumer Key and Consumer Secret from a Salesforce **Connected App** or **External Client App** (see [Salesforce Setup](#salesforce-setup) below). * A Streamkap workspace with permission to create source connectors. ## How It Works 1. **OAuth authentication** — Streamkap exchanges your Consumer Key and Secret for a Salesforce access token. 2. **CDC subscription** — When Native CDC is enabled, Streamkap subscribes to one channel per selected object (`/data/AccountChangeEvent`, `/data/ContactChangeEvent`, …) and streams events as they happen. 3. **Webhook ingestion (optional)** — If you also publish from Apex triggers, those POSTs hit the same connector at a generated webhook URL and flow through the same router. 4. **Routing** — Each event carries an entity name (from the CDC `ChangeEventHeader` or your Apex payload). The router maps it to a topic of the form `{Object}_events` — `Account_events`, `Contact_events`, etc. 5. **Key extraction** — The Salesforce record `Id` becomes the Kafka message key, enabling upsert-style consumption. 6. **Delete handling** — `DELETE` and `GAP_DELETE` change types set `__deleted: true` on the record so downstream sinks can issue tombstones. ## Streamkap Setup ### 1. Create the Source 1. Navigate to [Sources](https://app.streamkap.com/connectors/add?tab=Sources) and choose **Salesforce CDC**. 2. Give the source a memorable **Name** (for example, `salesforce-prod`). ### 2. Connection Settings (Auth tab) | Field | Required | Description | | ----------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------- | | **Webhook URL** | Auto | Read-only. Endpoint that Apex triggers post to. Generated on save. Only used in Apex-trigger mode. | | **API Key** | Auto | Read-only and encrypted. Sent by Apex triggers in the `x-api-key` header. Generated on save. | | **Salesforce Instance URL** | **Yes** | Your org URL — for example `https://myorg.my.salesforce.com` or `https://myorg.lightning.force.com`. | | **Client ID (Consumer Key)** | **Yes** | OAuth2 Consumer Key from your Salesforce Connected App / External Client App. | | **Client Secret (Consumer Secret)** | **Yes** | OAuth2 Consumer Secret. Encrypted at rest. | | **Username (Optional)** | No | Salesforce username. Only required if your Connected App is not configured for the `client_credentials` flow. | | **Password + Token (Optional)** | No | Salesforce password concatenated with the user's security token. Only required alongside Username. Encrypted at rest. | ### 3. Schema (Salesforce Objects) In the **Schema** tab, list the Salesforce objects you want to capture, comma-separated. The default is `Account,Contact,Lead,Opportunity`. The following standard objects are supported out of the box: ``` Account, Contact, Lead, Opportunity, Case, Task, Event, User, Campaign, CampaignMember, Contract, Order, OrderItem, Product2, Pricebook2, PricebookEntry, Asset, Note, Attachment, ContentDocument, ContentVersion, FeedItem, Solution, OpportunityLineItem, OpportunityContactRole, AccountContactRole, Partner, Quote, QuoteLineItem ``` Each object you list produces a Kafka topic named `{Object}_events` (for example `Account_events`, `OpportunityLineItem_events`). For native CDC to deliver events for an object, that object must also be enabled for Change Data Capture in your Salesforce org (Setup → **Change Data Capture** → add the object to the **Selected Entities** list). Custom objects and objects without CDC support must use the [Apex-trigger mode](#apex-trigger-webhook-optional). ### 4. Settings | Field | Default | Description | | ----------------------------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Enable Native CDC** | `true` | Subscribe directly to Salesforce CDC channels using OAuth. Disable if you only want to ingest via Apex triggers. | | **Unselected Object Behavior** | `DEFAULT_TOPIC` | What to do when an event arrives for an object not in your Schema list. `DEFAULT_TOPIC`, `SKIP`, or `FAIL`. | | **Default Topic for Unselected Objects** | `unknown` | Topic used when behavior is `DEFAULT_TOPIC`. Only shown when the option above is set to `DEFAULT_TOPIC`. | | **Flatten Record Fields** | `true` | Promote nested record fields to top level. Recommended for most warehouse destinations. | | **Flatten Prefix** | *(empty)* | Prefix for flattened fields. Empty means Salesforce field names are used directly (`Name`, `BillingCity`, `Phone`). Only shown when flattening is enabled. | | **Include Change Metadata** | `false` | Keep the `ChangeEventHeader` (changeType, commitTimestamp, recordIds, …) in the output. Disable for upsert/state-table mode; enable for audit-log mode. | | **Enable Dead Letter Queue** *(advanced)* | `true` | Failed records are written to a DLQ topic instead of crashing the connector. | ### 5. Save Save the source. If Native CDC is enabled and credentials are valid, events begin flowing as soon as Salesforce records change. To verify, edit a record in Salesforce and watch **Events Written** on the source dashboard. ## Salesforce Setup The required setup depends on the mode you choose. ### OAuth Connected App (required for both modes) 1. In Salesforce, go to **Setup → App Manager → New External Client App** *(or **New Connected App** on older orgs)*. 2. Configure: * **App name** — `Streamkap CDC`. * **Contact Email** — your email. * **Distribution State** — `Local`. * **API (Enable OAuth Settings)** — checked. * **Callback URL** — `https://login.salesforce.com/services/oauth2/callback` (any valid URL works; the connector does not use it). * **OAuth scopes** — add: * `Manage user data via APIs (api)` * `Full access (full)` * `Perform requests at any time (refresh_token, offline_access)` * Uncheck **Require Proof Key for Code Exchange (PKCE)**. * If you want password-less auth, enable **Client Credentials Flow** and select a Run-As user with the necessary object permissions. 3. Save and wait 2–10 minutes for Salesforce to provision the app. 4. Open the app and reveal the **Consumer Key** and **Consumer Secret** (you may need to click **Manage Consumer Details** to reveal the secret). Paste these into the Streamkap source as **Client ID** and **Client Secret**. 5. Confirm the user (either the Run-As user for client-credentials flow, or the Username/Password user) has **API Enabled**, **View All Data** *(or the relevant object permissions)*, and is allowed to log in from Streamkap's IP range if you have IP restrictions. #### Username / password fallback If you cannot use the client-credentials flow, fill the optional **Username** and **Password + Token** fields: * **Username** — the Salesforce user's login email. We recommend a dedicated integration user with API Enabled, the relevant read permissions, and no MFA requirement. * **Password + Token** — concatenate the user's password and security token directly with no separator. To get a security token, log in as that user and go to **Settings → My Personal Information → Reset My Security Token** — Salesforce emails a new token. If Streamkap's IP range is added to your org's **Setup → Network Access** trusted IPs, the security token is not required and the password alone is sufficient. ### Native CDC Setup 1. In Salesforce, go to **Setup → Change Data Capture**. 2. Move the objects you want to stream from **Available Entities** to **Selected Entities**. 3. Save. Salesforce begins publishing change events to the per-object channels (`/data/AccountChangeEvent`, etc.). 4. In Streamkap, ensure **Enable Native CDC** is on and the same objects are listed under **Salesforce Objects**. That is everything required for native CDC. No Apex code is needed. ### Apex-trigger Webhook (optional) Use this mode for objects that CDC does not support (custom objects, certain standard objects, custom field-level filtering) or when you want explicit control over what is sent. The connector accepts the same envelope shape from triggers as it receives from native CDC, so the resulting topics, keys, and downstream handling are identical. #### 1. Create the External Credential 1. **Setup → Named Credentials → External Credentials → New**. 2. Fill in: * **Label** — `Streamkap Webhook`. * **Name** — `Streamkap_Webhook`. * **Authentication Protocol** — `Custom`. 3. Save. 4. Under **Principals**, click **New**: * **Parameter Name** — `Default`. * **Identity Type** — `Named Principal` *(read-only)*. * **Sequence Number** — `1`. 5. Save. 6. Open the **Default** principal and under **Authentication Parameters** click **Add**: * **Parameter 1 Name** — `x-api-key`. * **Parameter 1 Value** — paste the **API Key** from the Streamkap source. 7. Save. #### 2. Create the Named Credential 1. Click the **Named Credentials** tab → **New**. 2. Fill in: * **Label** — `Streamkap`. * **Name** — `Streamkap`. * **URL** — paste the Streamkap **Webhook URL**, with **no path** appended. * **External Credential** — select `Streamkap Webhook`. 3. Save. #### 3. Grant Access via Permission Set The user that runs the Apex callouts must be allowed to use the External Credential. 1. **Setup → Permission Sets → New**: * **Label** — `Streamkap Webhook Access`. * Save. 2. Inside the permission set, open **External Credential Principal Access**. 3. Click **Edit**, move `Streamkap_Webhook - Default` into **Enabled**, save. 4. Click **Manage Assignments → Add Assignment**, select the user(s) that will execute the triggers, and assign. 5. Have those users log out and back in to refresh their session. #### 4. Create the Apex Sender Class This class dynamically discovers every accessible field on the object (including custom fields added later) and posts a CDC-formatted payload. 1. **Setup → Apex Classes → New**, paste the code below, and save: ```apex theme={null} public class WebhookSender { @future(callout=true) public static void sendAsync(String recordId, String changeType, String objectName) { Map fieldMap = Schema.getGlobalDescribe() .get(objectName).getDescribe().fields.getMap(); List fieldNames = new List(); for (String fieldName : fieldMap.keySet()) { Schema.DescribeFieldResult fieldDesc = fieldMap.get(fieldName).getDescribe(); if (fieldDesc.isAccessible()) { fieldNames.add(fieldDesc.getName()); } } String soql = 'SELECT ' + String.join(fieldNames, ', ') + ' FROM ' + objectName + ' WHERE Id = \'' + String.escapeSingleQuotes(recordId) + '\''; List records = Database.query(soql); if (records.isEmpty()) { sendPayload(recordId, objectName, changeType, new Map{'Id' => recordId}); return; } Map recordMap = new Map(); SObject record = records[0]; for (String fieldName : fieldNames) { recordMap.put(fieldName, record.get(fieldName)); } sendPayload(recordId, objectName, changeType, recordMap); } @future(callout=true) public static void sendDeleteAsync(String recordId, String objectName, String recordJson) { Map recordMap = (Map) JSON.deserializeUntyped(recordJson); sendPayload(recordId, objectName, 'DELETE', recordMap); } private static void sendPayload(String recordId, String objectName, String changeType, Map recordFields) { Map header = new Map{ 'entityName' => objectName, 'recordIds' => new List{recordId}, 'changeType' => changeType, 'commitTimestamp' => System.currentTimeMillis() }; Map payload = new Map(); payload.put('ChangeEventHeader', header); payload.putAll(recordFields); Map body = new Map{ 'data' => new Map{ 'payload' => payload, 'event' => new Map{'replayId' => 0} }, 'channel' => '/data/' + objectName + 'ChangeEvent' }; HttpRequest req = new HttpRequest(); req.setEndpoint('callout:Streamkap'); req.setMethod('POST'); req.setHeader('Content-Type', 'application/json'); req.setBody(JSON.serialize(body)); req.setTimeout(30000); Http http = new Http(); try { HttpResponse res = http.send(req); if (res.getStatusCode() != 200) { System.debug(LoggingLevel.ERROR, 'Webhook failed: ' + res.getStatusCode() + ' ' + res.getBody()); } } catch (Exception e) { System.debug(LoggingLevel.ERROR, 'Webhook error: ' + e.getMessage()); } } } ``` The Named Credential URL must have **no path** appended. Use `callout:Streamkap`, not `callout:Streamkap/webhook` — adding a path overrides the payload router and sends every event to a single topic. The connector relies on these exact field names to detect and route events — do not rename them: | Field path | Required value | Purpose | | ------------------------------------------- | ---------------------------------------- | --------------------------------------- | | `data.payload` | Object with record fields | Container for record data + header. | | `data.payload.ChangeEventHeader` | Object | Identifies the event as Salesforce CDC. | | `data.payload.ChangeEventHeader.entityName` | Object name (e.g. `"Account"`) | Determines the Kafka topic. | | `data.payload.ChangeEventHeader.changeType` | `CREATE`, `UPDATE`, `DELETE`, `UNDELETE` | Sets `__deleted`. | | `data.payload.Id` | Record ID | Used as the message key. | | `channel` | `/data/{Object}ChangeEvent` | Used for event-format detection. | #### 5. Create One Trigger per Object Create one Apex trigger per object you want to capture — they all share the `WebhookSender` class. Example for `Account`: ```apex theme={null} trigger AccountToKafka on Account (after insert, after update, after delete, after undelete) { List records; String changeType; if (Trigger.isInsert) { changeType = 'CREATE'; records = Trigger.new; } else if (Trigger.isUpdate) { changeType = 'UPDATE'; records = Trigger.new; } else if (Trigger.isDelete) { changeType = 'DELETE'; records = Trigger.old; } else if (Trigger.isUndelete) { changeType = 'UNDELETE'; records = Trigger.new; } for (SObject rec : records) { if (changeType == 'DELETE') { WebhookSender.sendDeleteAsync(rec.Id, 'Account', JSON.serialize(rec)); } else { WebhookSender.sendAsync(rec.Id, changeType, 'Account'); } } } ``` To capture additional objects (Contact, Lead, Opportunity, custom objects), copy this trigger and change the trigger name, the object in `trigger ... on {Object}`, and the object string passed to `WebhookSender`. Salesforce limits `@future` methods to **50 per transaction** and callouts to **120 seconds**. Bulk operations that touch more than 50 records in a single transaction (Data Loader imports, batch updates) will hit the `@future` ceiling — for those workflows, prefer native CDC. ## Event Routing Reference The payload router supports three Salesforce event formats and detects them by inspecting the payload shape. ### Change Data Capture (CDC) Detected by the presence of `data.payload.ChangeEventHeader`. Used by both native CDC and the Apex `WebhookSender` class above. | `changeType` | Topic | `__deleted` | | -------------- | ----------------- | ----------- | | `CREATE` | `{Entity}_events` | `false` | | `UPDATE` | `{Entity}_events` | `false` | | `DELETE` | `{Entity}_events` | `true` | | `UNDELETE` | `{Entity}_events` | `false` | | `GAP_CREATE` | `gap_events` | `false` | | `GAP_DELETE` | `gap_events` | `true` | | `GAP_OVERFLOW` | `gap_events` | `false` | Gap events indicate that Salesforce could not deliver every change individually (typically due to bulk operations) and are routed to a single `gap_events` topic regardless of the entity. ### PushTopic events Detected by the presence of `data.sobject`. Routed by the PushTopic name. | `event.type` | Topic | `__deleted` | | ------------ | -------------------- | ----------- | | `created` | `{TopicName}_events` | `false` | | `updated` | `{TopicName}_events` | `false` | | `deleted` | `{TopicName}_events` | `true` | | `undeleted` | `{TopicName}_events` | `false` | ### Platform events Detected by the presence of `data.payload` without a `ChangeEventHeader`. Routed by the platform event name. Platform events always have `__deleted = false`. ### Message keys All Salesforce events use `{ Id: }` as the Kafka message key. With **Flatten Record Fields** enabled and a non-empty **Flatten Prefix**, the key field name is prefixed accordingly (for example `{ sf_Id: ... }`). ## Example Payload A typical change event arriving from Salesforce CDC: ```json theme={null} { "data": { "payload": { "ChangeEventHeader": { "entityName": "Account", "recordIds": ["001Hp00002abcDE"], "changeType": "UPDATE", "commitTimestamp": 1764000000000 }, "Id": "001Hp00002abcDE", "Name": "Acme Corp", "Phone": "555-0101", "BillingCity": "San Francisco" }, "event": { "replayId": 12345 } }, "channel": "/data/AccountChangeEvent" } ``` With the default settings (**Flatten Record Fields** = `true`, **Include Change Metadata** = `false`), the record written to `Account_events` looks like: ```json theme={null} { "Id": "001Hp00002abcDE", "Name": "Acme Corp", "Phone": "555-0101", "BillingCity": "San Francisco", "__deleted": false } ``` The Kafka message key is `{ "Id": "001Hp00002abcDE" }`, which a sink can use to upsert. ## Common Patterns ### State table (upsert) ingestion Use this when you want one row per Salesforce record in your warehouse, mirroring the live state. * **Flatten Record Fields** — `true` * **Flatten Prefix** — *(empty)* * **Include Change Metadata** — `false` `DELETE` events flip `__deleted: true`, which most sinks treat as a tombstone. ### Audit log ingestion Use this when you want every change preserved as a separate row. * **Flatten Record Fields** — `false` *(or `true`, depending on your destination)* * **Include Change Metadata** — `true` The output retains `ChangeEventHeader` so you can see `changeType`, `commitTimestamp`, and the full sequence of changes per record. ## Troubleshooting | Issue | Check | | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `401` from Salesforce when the source starts | Verify the Consumer Key and Secret are correct and that the Connected App has finished provisioning (Salesforce can take 2–10 minutes after creation). If using the username/password fallback, confirm the password concatenates the user's security token with no separator. | | No events for an object even though the source is healthy | In native CDC mode, the object must be enabled for Change Data Capture (**Setup → Change Data Capture → Selected Entities**). For objects without CDC support, use the [Apex-trigger mode](#apex-trigger-webhook-optional) instead. | | Events arrive in the `unknown` topic | The entity name does not match any object in your **Salesforce Objects** schema list. Add the object to the schema, or set **Unselected Object Behavior** to `SKIP`. | | No events from Apex triggers | **Setup → Apex Jobs** — check for failed `@future` jobs. Trigger errors typically show up here rather than at insert/update time. | | `Unauthorized endpoint` error from Apex | The user does not have External Credential Principal Access. Re-check the Permission Set assignment and have the user log out and back in. | | `Insufficient access` on a trigger | Verify the user's profile allows modifying the object and that the trigger is **Active**. | | All Apex events land in one topic | The Named Credential URL includes a path, or the `setEndpoint` call adds one. Use `callout:Streamkap` with no trailing path. | | Missing fields on records | Field-level security is hiding the field from the user running the trigger. Grant read access via Profile or Permission Set. | | Destination cannot handle the nested record | Toggle **Flatten Record Fields** on. To avoid collisions with reserved field names downstream, set **Flatten Prefix** to something like `sf_`. | | `gap_events` topic has data | Salesforce dropped granular events (typically during bulk operations) and emitted a gap notification. Affected records need to be backfilled from the source. | ## Limitations * The Salesforce CDC source is currently **Beta**. * Native CDC requires that each object be enabled for Change Data Capture in your Salesforce org. Objects without CDC support must use Apex-trigger mode. * Maximum payload size is 50 MB per request and maximum header size is 64 KB. * Each request is treated as a single Kafka record; the connector does not batch multiple events into one Kafka message. * The connector runs as a single task; horizontal scaling requires multiple source instances. * Initial backfill of historical records is not performed automatically — only changes from the time the source starts are streamed. ## See Also * [Webhook Source](/webhook) — generic webhook source for any HTTP-capable producer. * [Zendesk Webhook](/webhook-zendesk) — Zendesk-specific webhook source. # Shopify Webhook Source: https://docs.streamkap.com/webhook-shopify Stream Shopify store events into Streamkap by registering Shopify webhooks against a dedicated Streamkap endpoint, with optional initial and on-demand snapshots via the Shopify GraphQL Admin API ## Overview The Shopify Webhook source receives events from your Shopify store at a dedicated HTTPS endpoint and routes each event to a Kafka topic based on its resource type (orders, products, customers, draft orders, fulfillments, inventory items, collections, etc.). You point Shopify at the Streamkap-generated webhook URL, register the topics you care about, and Streamkap takes care of parsing, key extraction, schema inference, and routing. For initial loads or ad-hoc backfills, the source can also snapshot data directly from the Shopify GraphQL Admin API. This connector is in **Beta**. Behaviors and defaults may change before general availability. ## Prerequisites * A Shopify store with **Admin** access (required to install apps and register webhooks). * A [Shopify Partners](https://partners.shopify.com) account so you can create a Dev Dashboard app for OAuth credentials (recommended) — or an existing legacy custom app with a permanent access token. * A Streamkap workspace with permission to create source connectors. * A clear list of the Shopify resources you want to capture (orders, products, customers, etc.). ## How It Works 1. **Endpoint provisioning** — When you create the source, Streamkap generates a unique HTTPS webhook URL and an API key. 2. **Shopify subscriptions** — In Shopify, you register webhook subscriptions for the topics you want (`orders/create`, `products/update`, …) pointing at the Streamkap URL with the API key passed as a query parameter. 3. **Header-based routing** — Each incoming request carries an `X-Shopify-Topic` header (`orders/create`, `customers/delete`, etc.). The Shopify payload router maps the resource segment to a topic — `orders/*` events go to `orders`, `products/*` events go to `products`, and so on. 4. **Key extraction** — The router pulls the resource `id` from the JSON body and uses it as the Kafka message key, enabling upsert-style consumption downstream. 5. **HMAC verification (optional)** — When you provide the Shopify app's client secret, each payload is verified against the `X-Shopify-Hmac-Sha256` header. Invalid payloads are rejected and (if configured) routed to the DLQ. 6. **Optional fan-out** — Array fields (line items, variants, addresses) can be fanned out into their own topics. 7. **Optional snapshot** — For initial loads or ad-hoc backfills, the source queries the Shopify GraphQL Admin API with cursor pagination and emits records alongside the live webhook stream. ## Streamkap Setup ### 1. Create the Source 1. Navigate to [Sources](https://app.streamkap.com/connectors/add?tab=Sources) and choose **Shopify**. 2. Give the source a memorable **Name** (for example, `shopify-prod`). ### 2. Connection Settings (Auth tab) | Field | Required | Description | | ------------------------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Webhook URL** | Auto | Read-only. Endpoint that Shopify posts events to. Generated on save. Append `?api_key=` when registering webhooks (see [Shopify Setup](#shopify-setup)). | | **API Key** | Auto | Read-only and encrypted. Sent by Shopify as the `api_key` query parameter to authenticate each request. Generated on save. | | **Store URL** | **Yes (for snapshots)** | Your store URL — for example `https://yourstore.myshopify.com`. Required for snapshot mode. | | **Client ID** | Conditional | Client ID from your Shopify Dev Dashboard app. Use together with Client Secret for the recommended client-credentials flow (tokens auto-refresh every 24 hours). | | **Client Secret** | Conditional | Client Secret from your Shopify Dev Dashboard app. Encrypted at rest. | | **Access Token (Legacy)** | Conditional | Static access token from a legacy custom app. Provide **either** Access Token **or** Client ID + Client Secret. | | **API Version** | No | Shopify Admin API version used by snapshots. Default `2024-10`. | | **HMAC Secret** | No | Your Shopify app's client secret. When set, every webhook is verified against the `X-Shopify-Hmac-Sha256` header. Leave empty to skip verification. | ### 3. Schema (Shopify Resources) In the **Schema** tab, choose the resources you want to capture. The default is `orders,products,customers`. A resource is the **first segment** of the `X-Shopify-Topic` header — for example `orders/create` and `orders/cancelled` both map to the `orders` resource. Each resource becomes a Kafka topic of the same name. The dropdown is pre-loaded with the resources surfaced in the Shopify Admin UI's webhook picker (**Settings → Notifications → Webhooks → Create webhook**): | Category | Resources | | -------------------- | ------------------------------------------------------------------------------------------------------------------------------- | | Core entities | `orders`, `products`, `customers`, `draft_orders`, `fulfillments`, `inventory_items`, `inventory_levels`, `collections`, `shop` | | Customer events | `customer` *(for tag-change events)*, `customer_groups`, `customer_account_settings` | | Order lifecycle | `order_transactions`, `refunds`, `fulfillment_orders`, `fulfillment_holds` | | Storefront | `discounts`, `carts`, `checkouts` | | Inventory operations | `inventory_shipments`, `inventory_transfers` | | Store configuration | `locations`, `markets`, `themes`, `tender_transactions` | Any fan-out topics you configure (see [Fan-out](#fan-out)) are added automatically. **Advanced — GraphQL Admin API registration.** If you register webhooks via the GraphQL Admin API instead of the Admin UI, additional [WebhookSubscriptionTopic](https://shopify.dev/docs/api/admin-graphql/latest/enums/WebhookSubscriptionTopic) values are available (for example `SUBSCRIPTION_CONTRACTS_*`, `COMPANIES_*`, `METAOBJECTS_*`, `BULK_OPERATIONS_FINISH`). The Schema field accepts any resource name — just type the first-segment of the topic header and Streamkap will route it. **Snapshot support is narrower** than webhook routing. Only `orders`, `products`, `customers`, `draft_orders`, `collections`, and `inventory_items` can be snapshotted via the GraphQL Admin API — see [Snapshot](#snapshot-backfill). All other resources stream via webhooks only. ### 4. Settings | Field | Default | Description | | ------------------------------------------ | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Unselected Resource Behavior** | `DEFAULT_TOPIC` | What to do when an event arrives for a resource not in your Schema list. `DEFAULT_TOPIC`, `SKIP`, or `FAIL`. | | **Default Topic for Unselected Resources** | `unknown` | Topic used when behavior is `DEFAULT_TOPIC`. Only shown when the option above is set to `DEFAULT_TOPIC`. | | **Include Webhook Metadata** | `false` | Keep Shopify webhook headers (`_shop_domain`, `_event_id`, `_triggered_at`, `_api_version`, `_webhook_id`) on the output record. Disable for upsert / state-table mode; enable for audit-log mode. | | **Fan-out Fields** *(advanced)* | *(empty)* | Comma-separated list of nested arrays to fan out into their own topics (for example `orders.line_items,products.variants,customers.addresses`). See [Fan-out](#fan-out). | | **Enable Dead Letter Queue** *(advanced)* | `true` | Failed records are written to a DLQ topic instead of crashing the connector. | ### 5. Save and Copy the Endpoint Save the source. Copy the **Webhook URL** and **API Key** — you will need them in the next section to configure Shopify. ## Shopify Setup Shopify does **not** allow custom headers on webhook deliveries — it only sends its own `X-Shopify-*` headers. Since Streamkap authenticates requests with an `api_key`, you must pass the key in the webhook URL as a query parameter: ``` https://?api_key= ``` Shopify will POST to this exact URL including the query string, and the connector authenticates the request via the `api_key` parameter. ### 1. Create an App in the Partners Dashboard > **Note**: As of January 2026, Shopify has deprecated legacy custom apps. New apps must be created via the [Partners Dashboard](https://partners.shopify.com). Existing legacy custom apps with permanent tokens still work but cannot be created on new stores. 1. Go to the [Shopify Partners Dashboard](https://partners.shopify.com) (create a partner account if needed). 2. Click **Apps → Create app → Create app manually**. 3. Name it (for example, `Streamkap`), set the App URL to your Streamkap webhook URL. 4. Click **Create app**. ### 2. Configure API Scopes Inside the app, go to **Configuration → Access scopes** and enable the scopes for the topics you plan to receive: | Webhook Topics | Required Scope | | --------------------------------------------------------------------------------------------------------- | ------------------- | | `orders/create`, `orders/updated`, `orders/delete`, `orders/cancelled`, `orders/fulfilled`, `orders/paid` | `read_orders` | | `products/create`, `products/update`, `products/delete` | `read_products` | | `customers/create`, `customers/update`, `customers/delete` | `read_customers` | | `draft_orders/*` | `read_draft_orders` | | `fulfillments/*` | `read_orders` | | `inventory_items/*`, `inventory_levels/*` | `read_inventory` | | `collections/*` | `read_products` | | `refunds/create` | `read_orders` | Save. For a full CDC-style setup, enable `read_orders`, `read_products`, `read_customers`, and `read_inventory` at minimum. ### 3. Install the App and Get Credentials 1. In the app, open **Settings** and note the **Client ID** and **Client Secret**. 2. Install the app on your store (from the Partners Dashboard or via **Settings → Apps and sales channels → Develop apps** in your store admin) and approve the scopes. You have two options for obtaining the access token Streamkap uses for snapshots. #### Option A — Client credentials grant (recommended) Paste the **Client ID** and **Client Secret** into the Streamkap source's Auth tab. Streamkap handles token acquisition and refresh automatically (Shopify tokens are valid for 24 hours). For webhook registration in the next step you still need a token. Mint one with curl: ```bash theme={null} curl -X POST "https://YOUR-STORE.myshopify.com/admin/oauth/access_token" \ -H "Content-Type: application/json" \ -d '{ "client_id": "YOUR_CLIENT_ID", "client_secret": "YOUR_CLIENT_SECRET", "grant_type": "client_credentials" }' ``` The response contains an `access_token` valid for 24 hours. #### Option B — Static access token (legacy custom apps only) If you have an existing legacy custom app with a permanent token, paste it into the Streamkap source's Auth tab as **Access Token (Legacy)**. Leave Client ID / Client Secret empty. ### 4. Register Webhooks via the GraphQL Admin API The Shopify Admin UI only registers one webhook at a time. For multiple topics, use the GraphQL Admin API. **Endpoint**: `https://YOUR-STORE.myshopify.com/admin/api/2024-10/graphql.json` **Required header**: `X-Shopify-Access-Token: YOUR_ACCESS_TOKEN` #### Register a single webhook ```bash theme={null} curl -X POST "https://YOUR-STORE.myshopify.com/admin/api/2024-10/graphql.json" \ -H "Content-Type: application/json" \ -H "X-Shopify-Access-Token: YOUR_ACCESS_TOKEN" \ -d '{ "query": "mutation { webhookSubscriptionCreate(topic: ORDERS_CREATE, webhookSubscription: { callbackUrl: \"https://YOUR-WEBHOOK-URL?api_key=YOUR_API_KEY\", format: JSON }) { webhookSubscription { id } userErrors { field message } } }" }' ``` #### Register all common webhooks at once Save the following as `setup-shopify-webhooks.sh`, set the variables, and run it: ```bash theme={null} #!/bin/bash STORE="YOUR-STORE.myshopify.com" TOKEN="YOUR_ACCESS_TOKEN" CALLBACK="https://YOUR-WEBHOOK-URL?api_key=YOUR_API_KEY" API_VERSION="2024-10" TOPICS=( # Orders ORDERS_CREATE ORDERS_UPDATED ORDERS_DELETE ORDERS_CANCELLED ORDERS_FULFILLED ORDERS_PAID # Products PRODUCTS_CREATE PRODUCTS_UPDATE PRODUCTS_DELETE # Customers CUSTOMERS_CREATE CUSTOMERS_UPDATE CUSTOMERS_DELETE # Draft Orders DRAFT_ORDERS_CREATE DRAFT_ORDERS_UPDATE DRAFT_ORDERS_DELETE # Fulfillments FULFILLMENTS_CREATE FULFILLMENTS_UPDATE # Inventory INVENTORY_ITEMS_CREATE INVENTORY_ITEMS_UPDATE INVENTORY_ITEMS_DELETE # Collections COLLECTIONS_CREATE COLLECTIONS_UPDATE COLLECTIONS_DELETE ) for TOPIC in "${TOPICS[@]}"; do echo "Creating webhook for $TOPIC..." curl -s -X POST "https://$STORE/admin/api/$API_VERSION/graphql.json" \ -H "Content-Type: application/json" \ -H "X-Shopify-Access-Token: $TOKEN" \ -d "{\"query\": \"mutation { webhookSubscriptionCreate(topic: $TOPIC, webhookSubscription: { callbackUrl: \\\"$CALLBACK\\\", format: JSON }) { webhookSubscription { id } userErrors { field message } } }\"}" echo done ``` #### Verify registered webhooks ```bash theme={null} curl -s -X POST "https://YOUR-STORE.myshopify.com/admin/api/2024-10/graphql.json" \ -H "Content-Type: application/json" \ -H "X-Shopify-Access-Token: YOUR_ACCESS_TOKEN" \ -d '{ "query": "{ webhookSubscriptions(first: 50) { edges { node { id topic endpoint { ... on WebhookHttpEndpoint { callbackUrl } } } } } }" }' ``` #### Delete a webhook ```bash theme={null} curl -s -X POST "https://YOUR-STORE.myshopify.com/admin/api/2024-10/graphql.json" \ -H "Content-Type: application/json" \ -H "X-Shopify-Access-Token: YOUR_ACCESS_TOKEN" \ -d '{ "query": "mutation { webhookSubscriptionDelete(id: \"gid://shopify/WebhookSubscription/1234567890\") { deletedWebhookSubscriptionId userErrors { field message } } }" }' ``` ### Via Shopify Admin UI (Quick Setup) For a quick setup with a handful of topics: 1. In Shopify Admin, go to **Settings → Notifications → Webhooks**. 2. Click **Create webhook**. 3. Select the event (for example `Order creation`). 4. Set format to **JSON**. 5. Enter your Streamkap webhook URL with the API key appended: `https://YOUR-WEBHOOK-URL?api_key=YOUR_API_KEY`. 6. Save and repeat for each event. ## Event Routing Reference The payload router parses the `X-Shopify-Topic` header on each incoming request and routes by resource segment. | Shopify Topic | Kafka Topic | Default Key | | --------------------------------------------------------------------------------------------------------- | ----------------- | ----------- | | `orders/create`, `orders/updated`, `orders/delete`, `orders/cancelled`, `orders/fulfilled`, `orders/paid` | `orders` | `{ id }` | | `products/create`, `products/update`, `products/delete` | `products` | `{ id }` | | `customers/create`, `customers/update`, `customers/delete` | `customers` | `{ id }` | | `draft_orders/*` | `draft_orders` | `{ id }` | | `fulfillments/*` | `fulfillments` | `{ id }` | | `inventory_items/*` | `inventory_items` | `{ id }` | | `collections/*` | `collections` | `{ id }` | | `shop/update` | `shop` | *(no key)* | Topics ending in `/delete` set `__deleted: true` on the output record so downstream sinks can issue tombstones. `__op` is emitted as a Kafka header (`c` create, `u` update, `d` delete, `r` snapshot). Each record additionally carries `__changeType` (`CREATE`, `UPDATE`, `DELETE`, `CANCELLED`, `FULFILLED`, `PAID`, `SNAPSHOT`, …) in the value. ## Fan-out Shopify resources contain arrays of objects (line items, variants, addresses, tax lines, …) that most warehouse destinations can't store cleanly as a single column. Fan-out emits one record per array element to a dedicated topic so each child becomes its own row. Set **Fan-out Fields** to a comma-separated list of `resource.field` pairs from the allowed set: ``` orders.line_items, orders.shipping_lines, orders.discount_codes, orders.tax_lines, orders.fulfillments, products.variants, products.images, products.options, customers.addresses, draft_orders.line_items, fulfillments.line_items ``` | Fan-out entry | Generated topic | Key shape | | ------------------------- | ------------------------- | ----------------------------------------------- | | `orders.line_items` | `orders_line_items` | `{ id: order_id, item_id: line_item_id }` | | `orders.shipping_lines` | `orders_shipping_lines` | `{ id: order_id, item_id: shipping_line_id }` | | `orders.discount_codes` | `orders_discount_codes` | `{ id: order_id }` | | `orders.tax_lines` | `orders_tax_lines` | `{ id: order_id }` | | `orders.fulfillments` | `orders_fulfillments` | `{ id: order_id, item_id: fulfillment_id }` | | `products.variants` | `products_variants` | `{ id: product_id, item_id: variant_id }` | | `products.images` | `products_images` | `{ id: product_id, item_id: image_id }` | | `products.options` | `products_options` | `{ id: product_id, item_id: option_id }` | | `customers.addresses` | `customers_addresses` | `{ id: customer_id, item_id: address_id }` | | `draft_orders.line_items` | `draft_orders_line_items` | `{ id: draft_order_id, item_id: line_item_id }` | | `fulfillments.line_items` | `fulfillments_line_items` | `{ id: fulfillment_id, item_id: line_item_id }` | Fan-out topics are added to the Schema list automatically — you do not need to register them manually. Streamkap rejects unknown fan-out entries when you save the source. Each fan-out record contains the array element's fields plus: * `_ctx_event_id` — the originating `X-Shopify-Event-Id` for correlation. * `_ctx_shop_domain` — the originating `X-Shopify-Shop-Domain`. Fan-out topics do **not** emit tombstone records when array items are removed (for example when a line item is removed from an order). To handle deletions, configure the downstream sink for delete-and-reinsert on each parent event, or treat each parent record as the source of truth and full-replace the child rows. ## HMAC Verification To cryptographically verify that webhooks come from Shopify, set **HMAC Secret** in the Auth tab to your Shopify app's client secret. Every payload is then verified against the `X-Shopify-Hmac-Sha256` header using HMAC-SHA256. Failed payloads are rejected (and sent to the DLQ when enabled). Leave the field empty to skip verification — useful while testing. ## Snapshot Once your source is live, you can run a snapshot from the Streamkap UI to load historical data for selected resources. Webhook streaming continues to run in parallel — snapshot is a one-time backfill, not an alternative to live events. ### Snapshottable Resources Only these resources can be snapshotted via Shopify's GraphQL Admin API: | Resource | What's included | | ----------------- | -------------------------------------------------------------------------------------------------------------------- | | `orders` | id, name, email, created/updated timestamps, total price, financial status, fulfillment status, customer, line items | | `products` | id, title, handle, status, vendor, type, created/updated timestamps, variants, images | | `customers` | id, name, email, phone, created/updated timestamps, state, number of orders, addresses | | `draft_orders` | id, name, status, created/updated timestamps, line items | | `collections` | id, title, handle, updated timestamp, sort order | | `inventory_items` | id, sku, created/updated timestamps, requires shipping, tracked | All other resources you select in the Schema tab (`fulfillments`, `shop`, `discounts`, `carts`, etc.) **cannot be snapshotted** — they only receive live events via webhooks once you register them in Shopify. This is a limitation of the Shopify Admin API, which exposes historical-fetch endpoints only for the resources above. ## Troubleshooting | Issue | Check | | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `401 Unauthorized` on webhook delivery | Verify the URL registered in Shopify includes `?api_key=` with the value from the Streamkap source. Shopify does not pass custom headers, so the key must be in the query string. | | Webhook not firing | In Shopify Admin **Settings → Notifications → Webhooks**, check delivery status. Shopify retries 8 times over 4 hours and then deletes the subscription. | | `Callback URL is not allowed` | The callback URL must be HTTPS. For local testing, expose Streamkap behind a tunnel (ngrok, Cloudflare Tunnel). | | Snapshot does not start | Confirm **Store URL** is populated and either Client ID + Client Secret or Access Token is set in the Auth tab. | | `Access denied` on snapshot | Verify the app has the right scopes for the resources you are snapshotting. Reinstall the app after changing scopes. | | Events arrive in the `unknown` topic | The resource segment in `X-Shopify-Topic` is not in your Schema list. Add it, or set **Unselected Resource Behavior** to `SKIP`. | | `400 Bad Request` saving the source | The source rejects unknown fan-out fields. The error message lists the allowed values. | | HMAC failures filling the DLQ | The HMAC secret must be the **client secret** of the Shopify app that registered the webhooks. Confirm you copied it from the same app. | ## Limitations * The Shopify Webhook source is currently **Beta**. * Shopify expects a `200` response within 5 seconds. The connector responds immediately and processes asynchronously; failures are captured in the DLQ when enabled. * Snapshot supports only the resources backed by the GraphQL Admin API listed above. Other resources (e.g., `fulfillments`, `shop`) are streamed via webhooks only. * Mandatory Shopify compliance webhooks (`customers/data_request`, `customers/redact`, `shop/redact`) are accepted by the endpoint but currently routed to the `unknown` topic; you should respond to them out of band per Shopify's requirements. * Maximum payload size is 50 MB and maximum header size is 64 KB. * The connector runs as a single task; horizontal scaling requires multiple source instances. ## See Also * [Webhook Source](/webhook) — generic webhook source for any HTTP-capable producer. * [Zendesk Webhook](/webhook-zendesk) — Zendesk-specific webhook source. * [Salesforce CDC](/webhook-salesforce) — Salesforce CDC + Apex-trigger webhook source. # Stripe Webhook Source: https://docs.streamkap.com/webhook-stripe Stream Stripe events into Streamkap by registering Stripe webhooks against a dedicated Streamkap endpoint, with optional initial and on-demand snapshots via the Stripe REST API ## Overview The Stripe Webhook source receives events from your Stripe account at a dedicated HTTPS endpoint and routes each event to a Kafka topic based on its resource type (customers, payment intents, charges, invoices, subscriptions, products, prices, payouts, refunds, etc.). You point Stripe at the Streamkap-generated webhook URL, register the events you care about, and Streamkap takes care of parsing, key extraction, schema inference, and routing. For initial loads or ad-hoc backfills, the source can also snapshot data directly from the Stripe REST API using your Stripe secret key. This connector is in **Beta**. Behaviors and defaults may change before general availability. ## Prerequisites * A Stripe account with **Administrator** access (required to manage webhooks and API keys). * Your Stripe **Secret key** (`sk_live_xxx` for production or `sk_test_xxx` for test mode), available at [dashboard.stripe.com/apikeys](https://dashboard.stripe.com/apikeys). * A Streamkap workspace with permission to create source connectors. * A clear list of the Stripe resources you want to capture (customers, payment intents, invoices, etc.). ## How It Works 1. **Endpoint provisioning** — When you create the source, Streamkap generates a unique HTTPS webhook URL and an API key. 2. **Stripe subscriptions** — In Stripe Workbench, you register a webhook destination pointing at the Streamkap URL with the API key passed as a query parameter, and select the events to deliver. 3. **Event-type routing** — Each incoming request carries an event in its JSON body with a `type` field (`customer.created`, `payment_intent.succeeded`, etc.). The Stripe payload router takes the **first segment** as the resource — `customer.*` events go to the `customer` topic, `payment_intent.*` events go to `payment_intent`, and so on. The connector extracts `data.object` as the Kafka record value. 4. **Key extraction** — The router pulls the resource `id` (e.g. `cus_abc123`, `pi_xyz789`) from `data.object` and uses it as the Kafka message key, enabling upsert-style consumption downstream. 5. **Signature verification (recommended)** — When you provide the Stripe signing secret (`whsec_xxx`), each payload is verified against the `Stripe-Signature` header using HMAC-SHA256. Invalid payloads are rejected and (if configured) routed to the DLQ. 6. **Optional fan-out** — Stripe's list objects (line items, refunds, subscription items) can be fanned out into their own topics. 7. **Optional snapshot** — For initial loads or ad-hoc backfills, the source queries the Stripe REST API with cursor pagination and emits records alongside the live webhook stream. ## Streamkap Setup ### 1. Create the Source 1. Navigate to [Sources](https://app.streamkap.com/connectors/add?tab=Sources) and choose **Stripe**. 2. Give the source a memorable **Name** (for example, `stripe-prod`). ### 2. Connection Settings (Auth tab) | Field | Required | Description | | ------------------ | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Webhook URL** | Auto | Read-only. Endpoint that Stripe posts events to. Generated on save. Append `?api_key=` when registering the webhook destination in Stripe. | | **API Key** | Auto | Read-only and encrypted. Sent by Stripe as the `api_key` query parameter to authenticate each request. Generated on save. | | **Signing Secret** | Strongly recommended | Stripe webhook signing secret (`whsec_xxx`) from the webhook destination in Stripe Workbench. When set, every payload is verified against the `Stripe-Signature` header. Leave empty to skip verification (testing only). | | **Stripe API Key** | Required for snapshots | Stripe Secret key (`sk_live_xxx` or `sk_test_xxx`) from [dashboard.stripe.com/apikeys](https://dashboard.stripe.com/apikeys). Used by Streamkap to call the Stripe REST API during snapshots. Encrypted at rest. | ### 3. Schema (Stripe Resources) In the **Schema** tab, choose the resources you want to capture. The default is `customer,payment_intent,charge,invoice`. A resource is the **first segment** of the Stripe event `type` field — for example `customer.created`, `customer.updated`, and `customer.subscription.created` all map to the `customer` resource. Each resource becomes a Kafka topic of the same name. The dropdown is pre-loaded with the resources most commonly used in billing / SaaS CDC scenarios: | Category | Resources | | --------------------------- | ------------------------------------------------------------------------------------------------------------- | | Customers and subscriptions | `customer`, `subscription_schedule`, `setup_intent`, `payment_method`, `mandate` | | Payments and charges | `payment_intent`, `charge`, `refund`, `payout`, `transfer`, `topup`, `application_fee`, `balance_transaction` | | Billing | `invoice`, `invoiceitem`, `credit_note`, `coupon`, `promotion_code`, `plan`, `quote`, `tax_rate` | | Checkout and storefront | `product`, `price`, `checkout` | | Other | `review`, `source` | Any fan-out topics you configure (see [Fan-out](#fan-out)) are added automatically. **Advanced — other Stripe resources.** Stripe has [many more event types](https://stripe.com/docs/api/events/types) than listed above (Issuing, Financial Connections, Identity, Treasury, Climate, Terminal, etc.). The Schema field accepts any resource name — just type the first-segment of the Stripe event type and Streamkap will route it. **Snapshot support is narrower** than webhook routing. Only `customer`, `charge`, `payment_intent`, `invoice`, `subscription`, `product`, `price`, `payout`, `refund`, `payment_method`, `balance_transaction`, `coupon`, `plan`, and `setup_intent` can be snapshotted via the Stripe REST API. All other resources stream via webhooks only. ### 4. Settings | Field | Default | Description | | ------------------------------------------ | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Unselected Resource Behavior** | `DEFAULT_TOPIC` | What to do when an event arrives for a resource not in your Schema list. `DEFAULT_TOPIC`, `SKIP`, or `FAIL`. | | **Default Topic for Unselected Resources** | `unknown` | Topic used when behavior is `DEFAULT_TOPIC`. Only shown when the option above is set to `DEFAULT_TOPIC`. | | **Include Event Metadata** | `true` | Keep Stripe event metadata (`_event_id`, `_event_type`, `_event_created`, `_api_version`, `_livemode`, `_previous_attributes`) on the output record. Disable for upsert / state-table mode; keep enabled for audit-log mode and for deduplication on `_event_id`. | | **Fan-out Fields** *(advanced)* | *(empty)* | Comma-separated list of nested lists to fan out into their own topics. Allowed values: `invoice.lines`, `charge.refunds`, `subscription.items`. See [Fan-out](#fan-out). | | **Enable Dead Letter Queue** *(advanced)* | `true` | Failed records are written to a DLQ topic instead of crashing the connector. | ### 5. Save and Copy the Endpoint Save the source. Copy the **Webhook URL** and **API Key** — you will need them in the next section to configure Stripe. ## Stripe Setup Stripe does not support custom headers on webhook deliveries. Pass the Streamkap API key in the destination URL as a query parameter: ``` https://?api_key= ``` ### 1. Get Your API Keys 1. Go to [dashboard.stripe.com/apikeys](https://dashboard.stripe.com/apikeys). 2. Copy your **Secret key** (`sk_test_xxx` for test mode, `sk_live_xxx` for production). 3. Use the toggle at the top of the dashboard to switch between test and live mode. 4. Paste the Secret key into the Streamkap source's **Stripe API Key** field in the Auth tab — this lets Streamkap call the Stripe REST API when you run a snapshot. ### 2. Register the Webhook in Workbench (recommended) > **Note**: Stripe replaced the old "Developers → Webhooks" page with **Workbench**. New accounts use Workbench by default. 1. Go to [dashboard.stripe.com](https://dashboard.stripe.com) and open **Developers → Workbench** (or [dashboard.stripe.com/webhooks](https://dashboard.stripe.com/webhooks)). 2. Click **Add destination** (or **Create new destination**). 3. Enter your endpoint URL: `https://?api_key=` using the values copied from Streamkap. 4. **Important — choose the "Snapshot" payload format, NOT "Thin" payload.** Streamkap requires the full resource object in `data.object`. Thin-payload events (events whose `object` is `v2.core.event`) are not supported and should be left unselected. 5. Select the events you want to deliver. Recommended starting set: | Category | Events | | ------------------- | ---------------------------------------------------------------------------------------------------------- | | Customers | `customer.created`, `customer.updated`, `customer.deleted` | | Subscriptions | `customer.subscription.created`, `customer.subscription.updated`, `customer.subscription.deleted` | | Payment intents | `payment_intent.succeeded`, `payment_intent.payment_failed`, `payment_intent.canceled` | | Charges | `charge.succeeded`, `charge.refunded`, `charge.captured`, `charge.failed` | | Invoices | `invoice.created`, `invoice.finalized`, `invoice.paid`, `invoice.payment_failed` | | Products and prices | `product.created`, `product.updated`, `product.deleted`, `price.created`, `price.updated`, `price.deleted` | | Payouts | `payout.created`, `payout.paid`, `payout.failed` | | Refunds | `refund.created`, `refund.updated` | | Checkout | `checkout.session.completed`, `checkout.session.expired` | 6. Click **Create**. 7. Open the destination's detail page and copy the **Signing secret** (`whsec_xxx`). Paste it into Streamkap's **Signing Secret** field in the Auth tab. ### 2 (Alternative). Register the Webhook via the Stripe API For scripted setup, use curl with your Secret key: ```bash theme={null} curl https://api.stripe.com/v1/webhook_endpoints \ -u sk_live_YOUR_SECRET_KEY: \ -d url="https://?api_key=" \ -d "enabled_events[]"="customer.created" \ -d "enabled_events[]"="customer.updated" \ -d "enabled_events[]"="customer.deleted" \ -d "enabled_events[]"="customer.subscription.created" \ -d "enabled_events[]"="customer.subscription.updated" \ -d "enabled_events[]"="customer.subscription.deleted" \ -d "enabled_events[]"="payment_intent.succeeded" \ -d "enabled_events[]"="payment_intent.payment_failed" \ -d "enabled_events[]"="charge.succeeded" \ -d "enabled_events[]"="charge.refunded" \ -d "enabled_events[]"="invoice.paid" \ -d "enabled_events[]"="invoice.finalized" \ -d "enabled_events[]"="product.created" \ -d "enabled_events[]"="product.updated" \ -d "enabled_events[]"="price.created" \ -d "enabled_events[]"="price.updated" ``` The response includes a `secret` field (`whsec_xxx`) — copy it into Streamkap's **Signing Secret** field. Or subscribe to all snapshot-payload events: ```bash theme={null} curl https://api.stripe.com/v1/webhook_endpoints \ -u sk_live_YOUR_SECRET_KEY: \ -d url="https://?api_key=" \ -d "enabled_events[]"="*" ``` ### Local Testing with the Stripe CLI ```bash theme={null} stripe listen --forward-to "https://?api_key=" ``` The CLI prints a `whsec_xxx` signing secret on startup — paste it into Streamkap's **Signing Secret** field. Trigger test events with: ```bash theme={null} stripe trigger customer.created stripe trigger payment_intent.succeeded ``` ## Event Routing Reference The payload router inspects the event `type` field on each incoming request and routes by the first segment. | Stripe Event Type | Kafka Topic | Default Key | | -------------------------------------------------------------------------------------- | ------------------- | ---------------------------------------- | | `customer.created`, `customer.updated`, `customer.deleted` | `customer` | `{ id: cus_xxx }` | | `customer.subscription.*` | `customer` | `{ id: sub_xxx }` | | `payment_intent.succeeded`, `payment_intent.payment_failed`, `payment_intent.canceled` | `payment_intent` | `{ id: pi_xxx }` | | `charge.succeeded`, `charge.refunded`, `charge.captured`, `charge.failed` | `charge` | `{ id: ch_xxx }` | | `charge.dispute.*` | `charge` | `{ id: dp_xxx }` | | `invoice.created`, `invoice.finalized`, `invoice.paid`, `invoice.payment_failed` | `invoice` | `{ id: in_xxx }` | | `product.*`, `price.*` | `product` / `price` | `{ id: prod_xxx }` / `{ id: price_xxx }` | | `payout.*`, `refund.*` | `payout` / `refund` | `{ id: po_xxx }` / `{ id: re_xxx }` | | `checkout.session.completed`, `checkout.session.expired` | `checkout` | `{ id: cs_xxx }` | Events whose action ends in `.deleted` set `__deleted: true` on the output record so downstream sinks can issue tombstones. `__op` is emitted as a Kafka header (`c` create, `u` update, `d` delete, `r` snapshot). Each record additionally carries `__changeType` (`CREATE`, `UPDATE`, `DELETE`, `SUCCEEDED`, `FAILED`, `REFUNDED`, `PAID`, `FINALIZED`, `SNAPSHOT`, …) in the value. ## Fan-out Stripe resources contain nested list objects (`{object: "list", data: [...]}`) for line items, refunds, and subscription items. Most warehouse destinations can't store these cleanly as a single column. Fan-out emits one record per list element to a dedicated topic so each child becomes its own row. Set **Fan-out Fields** to a comma-separated list from the allowed set: ``` invoice.lines, charge.refunds, subscription.items ``` | Fan-out entry | Generated topic | Key shape | | -------------------- | -------------------- | ----------------------------------------- | | `invoice.lines` | `invoice_lines` | `{ id: invoice_id, item_id: line_id }` | | `charge.refunds` | `charge_refunds` | `{ id: charge_id, item_id: refund_id }` | | `subscription.items` | `subscription_items` | `{ id: subscription_id, item_id: si_id }` | Fan-out topics are added to the Schema list automatically — you do not need to register them manually. Each fan-out record contains the list element's fields plus a `_ctx_event_id` for correlation back to the parent Stripe event. Fan-out topics do **not** emit tombstone records when list items are removed (for example when a line item is removed from an invoice). To handle deletions, configure the downstream sink for delete-and-reinsert on each parent event, or treat each parent record as the source of truth and full-replace the child rows. ## Signature Verification Stripe signs every webhook with HMAC-SHA256. Streamkap parses the `Stripe-Signature` header (`t=timestamp,v1=hex_signature`) and verifies the payload against the signing secret you provide. To enable verification, paste your destination's signing secret (`whsec_xxx`) into the Auth tab's **Signing Secret** field. The connector checks all `v1` signatures sent during secret rotation, so you can rotate without downtime. Leave the field empty only when testing — anyone who knows the connector URL could otherwise submit fake events. ## Snapshot Once your source is live, you can run a snapshot from the Streamkap UI to load historical data for selected resources. Webhook streaming continues to run in parallel — snapshot is a one-time backfill, not an alternative to live events. ### Snapshottable Resources Only these resources can be snapshotted via the Stripe REST API: | Resource | API Endpoint | | --------------------- | -------------------------- | | `customer` | `/v1/customers` | | `charge` | `/v1/charges` | | `payment_intent` | `/v1/payment_intents` | | `invoice` | `/v1/invoices` | | `subscription` | `/v1/subscriptions` | | `product` | `/v1/products` | | `price` | `/v1/prices` | | `payout` | `/v1/payouts` | | `refund` | `/v1/refunds` | | `payment_method` | `/v1/payment_methods` | | `balance_transaction` | `/v1/balance_transactions` | | `coupon` | `/v1/coupons` | | `plan` | `/v1/plans` | | `setup_intent` | `/v1/setup_intents` | All other resources you select in the Schema tab (`checkout`, `quote`, `credit_note`, `transfer`, etc.) **cannot be snapshotted** — they only receive live events via webhooks once you register them in Stripe. This is a limitation of the Stripe REST API, which exposes historical-fetch endpoints only for the resources above. ## Troubleshooting | Issue | Check | | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `401 Unauthorized` on webhook delivery | Verify the URL registered in Stripe includes `?api_key=` with the value from the Streamkap source. Stripe does not pass custom headers, so the key must be in the query string. | | Webhook not firing | In Stripe Workbench, open the destination and check delivery attempts. Stripe retries with exponential backoff up to 3 days in live mode (3 attempts in test mode). | | `Callback URL is not allowed` | The callback URL must be HTTPS in live mode. For local testing, use the Stripe CLI's `stripe listen --forward-to` command. | | Snapshot does not start | Confirm **Stripe API Key** is set in the Auth tab. The key needs read access for the resources you want to snapshot. | | `Access denied` on snapshot | Verify the Stripe API key is in the correct mode (live vs test) for the data you expect, and that it has not been revoked. | | Events arrive in the `unknown` topic | The resource segment in the Stripe event `type` is not in your Schema list. Add it, or set **Unselected Resource Behavior** to `SKIP`. | | Signature verification failed | Verify the **Signing Secret** matches the `whsec_xxx` shown on the destination's detail page in Stripe Workbench. Re-copy it if you regenerated it. | | Thin-payload events rejected | Streamkap only supports Snapshot (v1) payloads. Events with `"object": "v2.core.event"` in their body are thin and won't be processed. Deselect thin-only events in Workbench. | | Retry storms | Stripe retries up to 3 days in live mode. If the connector was down, expect a burst when it recovers. Check the destination's delivery attempts in Workbench. | ## Limitations * The Stripe Webhook source is currently **Beta**. * Stripe expects a `2xx` response promptly. The connector responds immediately and processes asynchronously; failures are captured in the DLQ when enabled. * Snapshot supports only the 14 resources listed above. Other resources (`checkout`, `quote`, `credit_note`, `transfer`, etc.) are streamed via webhooks only. * Only Stripe's **Snapshot (v1)** payload format is supported. Thin payloads (`v2.core.event`) are not processed. * Stripe does not guarantee event ordering. Use the `_event_created` timestamp for ordering and `_event_id` for deduplication. * Maximum payload size is 50 MB and maximum header size is 64 KB. * The connector runs as a single task; horizontal scaling requires multiple source instances. ## See Also * [Webhook Source](/webhook) — generic webhook source for any HTTP-capable producer. * [Zendesk Webhook](/webhook-zendesk) — Zendesk-specific webhook source. * [Salesforce CDC](/webhook-salesforce) — Salesforce CDC + Apex-trigger webhook source. * [Shopify Webhook](/webhook-shopify) — Shopify-specific webhook source. # Zendesk Webhook Source: https://docs.streamkap.com/webhook-zendesk Stream Zendesk events into Streamkap by registering a webhook target in Zendesk that posts to a dedicated Streamkap endpoint ## Overview The Zendesk Webhook source receives events from your Zendesk account at a dedicated HTTPS endpoint and routes each event to a Kafka topic based on its event type (tickets, users, organizations, articles, messaging, etc.). You point Zendesk at the Streamkap-generated webhook URL, configure which event subscriptions to send, and Streamkap takes care of parsing, key extraction, schema inference, and routing. This connector is in **Beta**. Behaviors and defaults may change before general availability. ## Prerequisites * A Zendesk account with **Admin** permissions (required to create webhooks and event subscriptions). * A Streamkap workspace with permission to create source connectors. * A clear list of the Zendesk objects you want to capture (tickets, users, organizations, etc.). ## How It Works 1. **Endpoint provisioning** — When you create the source, Streamkap generates a unique HTTPS webhook URL and an API key. 2. **Zendesk subscription** — In Zendesk, you create a webhook target pointing at that URL and subscribe it to the event types you care about. 3. **Event routing** — Each incoming event carries a `type` field (for example `zen:event-type:ticket.created`). The Zendesk payload router maps the event domain to a topic — `ticket.*` events go to `ticket_events`, `user.*` events go to `user_events`, and so on. 4. **Key extraction** — The router pulls a stable identifier from the payload (typically `detail.id`) and uses it as the Kafka message key, enabling upsert-style consumption downstream. 5. **Optional flattening / fan-out** — Nested `detail` fields can be promoted to top level, and array fields (such as ticket tags or custom fields) can be fanned out into their own topics. ## Streamkap Setup ### 1. Create the Source 1. Navigate to [Sources](https://app.streamkap.com/connectors/add?tab=Sources) and choose **Zendesk Webhook**. 2. Give the source a memorable **Name** (for example, `zendesk-prod`). ### 2. Connection Settings (Auth tab) These fields are populated automatically once you save the source — you do not enter them manually. | Field | Description | | --------------- | ------------------------------------------------------------------------------------------------------------------- | | **Webhook URL** | Read-only. The HTTPS endpoint that Zendesk posts events to. Generated on save. | | **API Key** | Read-only and encrypted. Sent by Zendesk in the `X-API-Key` header to authenticate each request. Generated on save. | ### 3. Schema (Zendesk Models) In the **Schema** tab, the topics are pre-populated with the nine built-in Zendesk event models. Leave them all enabled, or remove any you do not want to ingest. | Topic | Domain | | --------------------------- | --------------------------------- | | `ticket_events` | Tickets | | `user_events` | Users | | `organization_events` | Organizations | | `article_events` | Help Center articles | | `community_post_events` | Community posts | | `messaging_events` | Messaging tickets / conversations | | `agent_events` | Agent availability and status | | `omnichannel_config_events` | Omnichannel configuration | | `messaging_metrics_events` | Messaging live metrics | Any fan-out topics you configure (see below) are added to this list automatically. ### 4. Settings | Field | Default | Description | | ----------------------------------------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Unknown Event Behavior** | `DEFAULT_TOPIC` | What to do when an event type is not recognised. `DEFAULT_TOPIC` routes it to a fallback topic, `SKIP` drops it silently, `FAIL` halts the connector. | | **Unknown Event Default Topic** | `unknown` | Topic name used when behavior is `DEFAULT_TOPIC`. Only shown when the option above is set to `DEFAULT_TOPIC`. | | **Flatten Detail Fields** | `false` | Promote nested `detail.*` fields to top level. Recommended for destinations that do not support nested objects (most data warehouses). | | **Flatten Detail Prefix** | `detail_` | Prefix added to flattened fields (for example `detail.id` becomes `detail_id`). Only shown when flattening is enabled. | | **Include Event Field** | `true` | Keep the raw `event` field in the output record. Enable for audit-log style ingestion (append mode); disable for state-table style (upsert mode) where only the entity matters. | | **Fan-out Fields** *(advanced)* | *(empty)* | Comma-separated list of `domain.field` pairs to fan out into their own topics — for example `ticket.tags,ticket.custom_fields,organization.tags`. See [Fan-out](#fan-out) below. | | **Enable Dead Letter Queue** *(advanced)* | `false` | Failed records are written to a DLQ topic instead of crashing the connector. | ### 5. Save and Copy the Endpoint Save the source. Copy the **Webhook URL** and **API Key** — you will need them in the next section to configure Zendesk. ## Zendesk Setup You configure Zendesk to send events to the Streamkap endpoint in two pieces: a **webhook target** and one or more **event subscriptions**. ### 1. Create the Webhook in Zendesk 1. In Zendesk, go to **Admin Center → Apps and integrations → Webhooks → Webhooks**. 2. Click **Create webhook**. 3. Choose **Trigger or automation** *(or **Zendesk events** — see step 2 below)*. 4. Configure the webhook: * **Name** — `Streamkap` (or anything memorable). * **Endpoint URL** — paste the **Webhook URL** from Streamkap. * **Request method** — `POST`. * **Request format** — `JSON`. * **Authentication** — choose **API key**: * **Header name** — `X-API-Key` * **Value** — paste the **API Key** from Streamkap. 5. Click **Test webhook** to confirm Zendesk can reach the endpoint, then **Create webhook**. ### 2. Subscribe to Event Types To stream object-level events (ticket created, user updated, organization deleted, …), connect the webhook to Zendesk's **Events** subscription system: 1. In **Admin Center**, go to **Apps and integrations → Webhooks → Connections**. 2. Click **Create connection**, pick the webhook you just created, and select the **event types** you want to forward. 3. Use event types from the families that match the topics you enabled in Streamkap: * `zen:event-type:ticket.*` * `zen:event-type:user.*` * `zen:event-type:organization.*` * `zen:event-type:article.*` * `zen:event-type:community_post.*` * `zen:event-type:messaging_ticket.*` * `zen:event-type:agent.*` * `zen:event-type:omnichannel_config.*` * `zen:event-type:messaging_live_metrics.*` 4. Save the connection. Zendesk now begins POSTing events to Streamkap. For triggers and automations (instead of native event subscriptions), set the request body to forward the relevant fields — including a `type` value such as `zen:event-type:ticket.updated` — so the payload router can identify the event family. ## Event Routing Reference The payload router inspects the `type` field on each incoming event and maps it to a topic. | Event type prefix | Topic | Default key | | ----------------------------------------- | --------------------------- | ------------------------------------ | | `zen:event-type:ticket.*` | `ticket_events` | `{ "detail_id": }` | | `zen:event-type:user.*` | `user_events` | `{ "detail_id": }` | | `zen:event-type:organization.*` | `organization_events` | `{ "detail_id": }` | | `zen:event-type:article.*` | `article_events` | `{ "detail_id": }` | | `zen:event-type:community_post.*` | `community_post_events` | `{ "detail_id": }` | | `zen:event-type:messaging_ticket.*` | `messaging_events` | `{ "detail_id": }` | | `zen:event-type:agent.*` | `agent_events` | `{ "detail_agent_id": }` | | `zen:event-type:omnichannel_config.*` | `omnichannel_config_events` | `{ "account_id": }` | | `zen:event-type:messaging_live_metrics.*` | `messaging_metrics_events` | `{ "account_id": }` | Event types ending in `.deleted` or `.removed` (but not `.soft_deleted` or `.undeleted`) automatically set `__deleted: true` on the output record so downstream sinks can issue tombstones. ## Fan-out When **Flatten Detail Fields** is on, scalar fields are promoted to the top level — but arrays and lists of objects (tags, custom fields, comments, …) cannot be flattened cleanly. Fan-out solves this by emitting one record per array element to a dedicated topic. Set **Fan-out Fields** to a comma-separated list of `domain.field` pairs: ``` ticket.tags,ticket.custom_fields,ticket.comments,ticket.collaborators,ticket.followers,organization.tags ``` | Fan-out entry | Generated topic | Key shape | | ---------------------- | ---------------------- | ---------------------- | | `ticket.tags` | `ticket_tags` | `{ detail_id, value }` | | `ticket.custom_fields` | `ticket_custom_fields` | `{ detail_id, id }` | | `ticket.comments` | `ticket_comments` | `{ id }` | | `ticket.collaborators` | `ticket_collaborators` | `{ detail_id, id }` | | `ticket.followers` | `ticket_followers` | `{ detail_id, id }` | | `organization.tags` | `organization_tags` | `{ detail_id, value }` | The fan-out topics are added to the Schema list automatically, so you do not need to register them manually. Each fan-out record contains the array element's own fields plus: * `detail_id` — parent entity ID (matches the key field name). * `_ctx_event_id` — the originating webhook event UUID, useful for correlating fan-out records back to the parent event. Example fan-out tag record: ```json theme={null} { "value": "urgent", "detail_id": 987654, "_ctx_event_id": "01HF6...-event-uuid" } ``` Fan-out topics do **not** emit tombstone records when array items are removed (for example when a tag is removed from a ticket). To handle deletions, either configure the downstream sink for delete-and-reinsert mode on each parent event, or treat each ticket event as the source of truth and full-replace the child rows. ## Example Payload A typical event sent by Zendesk: ```json theme={null} { "type": "zen:event-type:ticket.created", "id": "01HF6...-event-uuid", "account_id": 123456, "time": "2026-01-24T15:30:00Z", "subject": "zen:ticket:987654", "detail": { "id": 987654, "subject": "Cannot login", "status": "new", "tags": ["urgent", "billing"], "custom_fields": [{ "id": 123, "value": "tier1" }] }, "event": { "comment": { "id": 555, "body": "We are looking into it" } } } ``` With **Flatten Detail Fields** enabled and **Include Event Field** disabled, the record written to `ticket_events` looks like: ```json theme={null} { "type": "zen:event-type:ticket.created", "account_id": 123456, "time": "2026-01-24T15:30:00Z", "subject": "zen:ticket:987654", "detail_id": 987654, "detail_subject": "Cannot login", "detail_status": "new", "__deleted": false } ``` The arrays (`tags`, `custom_fields`) are emitted to `ticket_tags` and `ticket_custom_fields` if those entries are listed in **Fan-out Fields**. ## Common Patterns ### State table (upsert) ingestion Use this when you want one row per Zendesk entity in your warehouse, mirroring the live state. * **Flatten Detail Fields** — `true` * **Include Event Field** — `false` * **Fan-out Fields** — list any nested arrays you want as separate tables (typically `ticket.tags`, `ticket.custom_fields`) Downstream sinks upsert by the `detail_id` key. `.deleted` events flip `__deleted: true`, which most sinks treat as a tombstone. ### Audit log ingestion Use this when you want every event preserved as a separate row. * **Flatten Detail Fields** — `false` * **Include Event Field** — `true` * **Fan-out Fields** — empty Each event becomes a record. Downstream consumers see the full context of every change. ## Troubleshooting **Zendesk's webhook test returns 401** — Check that the `X-API-Key` header name is exactly `X-API-Key` and the value matches the API Key shown in the Streamkap UI. Regenerate the source if the key was lost. **Events arrive in the `unknown` topic instead of the expected one** — The event `type` did not match any known prefix. Verify the event family is one of those listed in [Event Routing Reference](#event-routing-reference). If you are using a Zendesk trigger (rather than an event subscription), make sure the request body includes a valid `type` value. **No data is flowing** — Confirm in Zendesk that the webhook is **Active** and connected to at least one event subscription. Use the **Activity** view on the Zendesk webhook to see recent delivery attempts and response codes. **Records are nested when I expected flat fields** — Toggle **Flatten Detail Fields** on and re-deploy the source. Existing records already in Kafka will not be retroactively flattened. **My destination cannot handle the array fields** — Add the relevant entries to **Fan-out Fields** so each array element becomes its own record in a dedicated topic. ## Limitations * The Zendesk Webhook source is currently **Beta**. * The connector accepts only `POST` requests; do not configure Zendesk to use a different HTTP method. * Maximum payload size is 50 MB and maximum header size is 64 KB. Zendesk events fit comfortably within these limits. * Each request is treated as a single Kafka record. Zendesk does not batch events into one request. * The connector runs as a single task; horizontal scaling requires multiple source instances. ## See Also * [Webhook Source](/webhook) — generic webhook source for any HTTP-capable producer. # What is CDC? Source: https://docs.streamkap.com/what-is-cdc Change data capture (CDC) is a method for capturing and tracking changes made to data in a database. Using CDC ensures real-time data synchronisation and data integrity across all systems reliant on that data. Transactional databases store all changes in a [transaction log](https://en.wikipedia.org/wiki/Transaction_log), facilitating database recovery in case of a crash. Log-based CDC leverages this feature to read changes from the log. Streamkap uses this method as it offers the ideal balance between minimising performance impact on the source database while maintaining high data quality. The most significant advantage of log-based change data capture is its ability to decouple data capture from the source application, making it highly versatile across various applications. ## What CDC Can and Cannot Capture ### CDC Captures Physical Tables Only Change Data Capture works by reading the database transaction log (binlog, WAL, oplog, redo log, etc.), which records **committed changes to physical tables**. **CDC can capture:** * Base tables (regular tables with physical storage) * Insert, update, and delete operations on those tables * Schema changes (DDL) on those tables ### Why Views Cannot Be Captured **Views are not capturable** because they are virtual—they're query-time computations over base tables with no physical storage. When you query a view, the database engine executes the underlying SELECT statement against the base tables. Since views don't store data, they don't generate transaction log entries. **Common non-capturable entities across all databases:** * **Views**: Virtual tables defined by queries, no physical storage or transaction log entries * **Common Table Expressions (CTEs)**: Query-time constructs, exist only during query execution * **Temporary tables**: Session or transaction-scoped, not durably logged * **System/metadata tables**: Represent database internals, not user data (e.g., `information_schema`, `pg_catalog`, `sys.*`) Each database also has vendor-specific entities that cannot be captured, such as PostgreSQL unlogged tables, SQL Server memory-optimized tables, MySQL MEMORY tables, Oracle global temporary tables, and MongoDB time series collections. See the source-specific FAQ documentation for details. ### The Solution: Capture Base Tables **Solution:**\ Configure CDC on the underlying base tables that power your views. The view logic can be recreated in your destination or transformation layer. # Kafka (Writing) Source: https://docs.streamkap.com/write-to-kafka Write directly to Kafka Customers on a paid plan can enable direct access via Proxy. This guide shows you how to write messages to your Streamkap Kafka topics using Python or command-line tools. ## Creating Kafka Users You can create and manage Kafka users through the Streamkap web interface at [Kafka Access](https://app.streamkap.com/kafka-access). To create a new Kafka user, click the "Create User" button. This will open the user creation dialog where you can configure the user's permissions and access settings. For detailed step-by-step instructions on creating and managing Kafka users through the UI, see the [Kafka Access](/kafka-access) documentation. ### User Configuration When creating a Kafka user, you'll need to configure: * **Username**: Enter a lowercase username for the Kafka user * **Password**: Set a secure password for authentication * **Safe listed IPs**: Specify IP addresses or CIDR ranges that are allowed to connect * **Kafka ACLs**: Configure access control lists to define what the user can do ### Access Control Lists (ACLs) Kafka ACLs control what operations users can perform on specific resources. When creating a user, you can configure: * **Resource Type**: * `TOPIC` - Controls access to Kafka topics * `GROUP` - Controls access to consumer groups * **Operation**: The type of operation allowed (varies by resource type)
**For `TOPIC` resources:** * `ALL` - All operations * `WRITE` - Write/produce messages * `READ` - Read/consume messages * `ALTER` - Modify resource configurations * `ALTER_CONFIGS` - Modify resource configurations * `CREATE` - Create new resources * `DELETE` - Delete resources * `DESCRIBE` - View resource metadata * `DESCRIBE_CONFIGS` - View resource configurations **For `GROUP` resources (consumers only):** * `READ` - Join and consume from consumer group * `DELETE` - Delete consumer group * `DESCRIBE` - View consumer group metadata * **Pattern Type**: How the resource name is matched * `LITERAL` - Exact match of the resource name * `PREFIXED` - Match resources with the specified prefix * **Name**: The specific resource name or prefix to apply the ACL to ### Connection Details Once a user is created, your endpoints are shown under "Proxy Endpoints". These endpoints follow the naming pattern: `-.streamkap.net:PORT` Where: * `` - Your Streamkap service/tenant name * `` - The Kafka user's username * `PORT` - One of the available ports: 32400, 32401, or 32402 **Example proxy endpoints:** * `my-service-kafka-user.streamkap.net:32400` * `my-service-kafka-user.streamkap.net:32401` * `my-service-kafka-user.streamkap.net:32402` **Connection settings:** * **Security protocol**: `SASL_SSL` (recommended for secure connections) * **SASL mechanism**: `PLAIN` * **Username/password**: As configured for the user ### Required Permissions To write to Kafka topics, your user needs these ACL permissions: **Essential permissions (always required):** * **Resource Type**: `TOPIC` | **Operation**: `WRITE` | **Pattern Type**: `LITERAL` or `PREFIXED` | **Name**: Your topic name/prefix * **Resource Type**: `TOPIC` | **Operation**: `DESCRIBE` | **Pattern Type**: `LITERAL` or `PREFIXED` | **Name**: Your topic name/prefix **Additional permission (if topic doesn't exist):** * **Resource Type**: `TOPIC` | **Operation**: `CREATE` | **Pattern Type**: `LITERAL` or `PREFIXED` | **Name**: Your topic name/prefix Both `WRITE` and `DESCRIBE` are required for successful message production. Add `CREATE` only if you need to create new topics. ## Code Examples ### Prerequisites Install the required packages: ```bash theme={null} pip install confluent-kafka certifi ``` Install kcat: ```bash theme={null} # macOS brew install kcat # Ubuntu/Debian sudo apt-get install kcat ``` ```python Python theme={null} from confluent_kafka import Producer import socket import certifi import os def delivery_report(err, msg): """Called once for each message produced to indicate delivery result.""" if err is not None: print(f'Message delivery failed: {err}') else: print(f'Message delivered to {msg.topic()} [{msg.partition()}] at offset {msg.offset()}') conf = { 'bootstrap.servers': '-.streamkap.net:32400,-.streamkap.net:32401,-.streamkap.net:32402', 'security.protocol': 'SASL_SSL', 'sasl.mechanism': 'PLAIN', 'sasl.username': '', 'sasl.password': '', 'client.id': socket.gethostname(), # Required to trust AWS root certificates 'ssl.ca.location': certifi.where(), } producer = Producer(conf) # Produce a message producer.produce('', key='key1', value='Hello Streamkap!', callback=delivery_report) producer.flush() # Note: If the topic doesn't exist, you may need to create it first # This requires CREATE permissions in addition to WRITE and DESCRIBE ``` ```bash CLI (Single Message) theme={null} # Single message echo "Hello Streamkap!" | kcat -P \ -b -.streamkap.net:32400,-.streamkap.net:32401,-.streamkap.net:32402 \ -t \ -X security.protocol=SASL_SSL \ -X sasl.mechanisms=PLAIN \ -X sasl.username= \ -X sasl.password= ``` ```bash CLI (Batch Messages) theme={null} # Multiple messages from file kcat -P \ -b -.streamkap.net:32400,-.streamkap.net:32401,-.streamkap.net:32402 \ -t \ -X security.protocol=SASL_SSL \ -X sasl.mechanisms=PLAIN \ -X sasl.username= \ -X sasl.password= \ -l messages.txt ``` ```bash CLI (Key-Value) theme={null} # Produce messages with keys (key:value format) echo "user123:Hello from user 123" | kcat -P \ -b -.streamkap.net:32400,-.streamkap.net:32401,-.streamkap.net:32402 \ -t \ -K: \ -X security.protocol=SASL_SSL \ -X sasl.mechanisms=PLAIN \ -X sasl.username= \ -X sasl.password= ``` **Replace the following values in the examples above:** Your proxy endpoints are listed in the Streamkap web interface at [Kafka Access](https://app.streamkap.com/kafka-access) under "Proxy Endpoints". The format is `-.streamkap.net:PORT`. * `-` - Your proxy endpoints * `` - Your Kafka user username * `` - Your Kafka user password * `` - The topic you want to write to ## Troubleshooting Before diving into complex debugging, verify basic network connectivity to your Streamkap Kafka cluster. **Test DNS Resolution:** ```bash theme={null} # Check if hostname resolves nslookup -.streamkap.net ``` **Test Port Connectivity:** ```bash theme={null} # Test with netcat (preferred - quick and clean) nc -zv -.streamkap.net 32400 # Test all three ports nc -zv -.streamkap.net 32400 nc -zv -.streamkap.net 32401 nc -zv -.streamkap.net 32402 # Alternative with telnet (press Ctrl+C to exit after connection success) telnet -.streamkap.net 32400 ``` **Test SSL/TLS Handshake:** ```bash theme={null} # Test SSL handshake and certificate chain openssl s_client -connect -.streamkap.net:32400 -servername -.streamkap.net # Alternative with timeout (press Ctrl+C to exit) echo "Q" | openssl s_client -connect -.streamkap.net:32400 -servername -.streamkap.net ``` **Common Network Issues & Solutions:** * **VPN interference**: Disconnect VPN and try again * **Firewall blocking ports**: Ensure ports 32400-32402 are accessible * **Safe listed IPs**: Verify your public IP address is in the user's safe list If basic connectivity fails, check your network configuration before proceeding with Kafka-specific troubleshooting. **Common Errors**: * `SSL connection closed by peer` during message production * SSL certificate verification failures * `SASL authentication failed` or authentication errors * SSL handshake failures **Authentication Solutions**: 1. Verify username and password are correct 2. Ensure `sasl.mechanism` is set to `PLAIN` and `security.protocol` is set to `SASL_SSL` 3. Check that the user account is active and not disabled 4. Confirm the user has basic connection permissions **SSL Solutions**: 1. **For Python**
Ensure certificates are properly configured: ```bash theme={null} pip install --upgrade certifi ``` ```python theme={null} import certifi 'ssl.ca.location': certifi.where() ``` 2. **For CLI tools**
Try different certificate paths: ```bash theme={null} -X ssl.ca.location=/etc/ssl/cert.pem # or -X ssl.ca.location=/etc/ssl/certs/ca-certificates.crt ``` 3. **Disable hostname verification** (temporary): ```python theme={null} 'ssl.endpoint.identification.algorithm': 'none' ``` 4. **Contact support** if issues persist - may require infrastructure team resolution **Note**: Metadata operations (listing topics) may work while data operations fail
**Error**: `Topic authorization failed` or `TOPIC_AUTHORIZATION_FAILED` **Cause**: Missing `TOPIC READ` or `TOPIC WRITE` permissions **Solution**: Add the appropriate ACL permissions: * **Resource Type**: `TOPIC` * **Operation**: `READ` (for consumers) or `WRITE` (for producers) * **Pattern Type**: `LITERAL` or `PREFIXED` * **Name**: Your topic name or prefix **Error**: `GROUP_AUTHORIZATION_FAILED` or `Group authorization failed` **Cause**: Missing `GROUP READ` permissions for your consumer group **Solution**: Add the following ACL permission: * **Resource Type**: `GROUP` * **Operation**: `READ` * **Pattern Type**: `LITERAL` or `PREFIXED` * **Name**: Your consumer group ID (e.g., `my-consumer-group`) **Note**: This only affects Python consumers and CLI tools using consumer groups **Issue**: Consumer polls but receives no messages **Possible Causes**: 1. **No messages in topic**: Topic is empty or messages are at different offsets 2. **Consumer group offset**: Group has already consumed available messages 3. **Partition assignment**: Messages might be in different partitions 4. **Offset reset**: Check `auto.offset.reset` setting **Solutions**: 1. **Check topic contents**: Use CLI to verify messages exist 2. **Use fresh consumer group**: Try with a new `group.id` 3. **Reset offsets**: Set `auto.offset.reset` to `earliest` 4. **Check all partitions**: For CLI, try without specifying partition