> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zwiron.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Snowflake Connector

> Load Postgres, MySQL, MongoDB, and more into Snowflake with Zwiron — hosted or agent-based CDC sync.

<Info>
  **Live — enterprise connector.** Fully supported today (SAT-validated). One of Zwiron's five production connectors: PostgreSQL, MySQL, MongoDB, Snowflake, BigQuery.
</Info>

Snowflake is a cloud data warehouse built for analytics. You can use it as a **destination** to load data from databases, APIs, and files, or as a **source** to read from existing tables.

***

## Supported sync modes

| As destination | Write modes             |
| -------------- | ----------------------- |
|                | Append, Upsert, Replace |

| As source | Full Refresh | Incremental |
| --------- | ------------ | ----------- |
|           | ✅            | ✅           |

***

## How to connect

You can connect Zwiron to Snowflake using **OAuth (recommended)** or **username + password**.

<Tabs>
  <Tab title="OAuth (recommended)">
    OAuth lets you connect without storing a password — you authorise Zwiron directly from your Snowflake account.

    ### Step 1 — Create a Security Integration in Snowflake

    A Security Integration is Snowflake's term for an OAuth app. You need to create one inside your Snowflake account so Zwiron can request access on your behalf.

    Open a Snowflake worksheet and run:

    ```sql theme={null}
    CREATE SECURITY INTEGRATION zwiron_oauth
      TYPE = OAUTH
      ENABLED = TRUE
      OAUTH_CLIENT = CUSTOM
      OAUTH_CLIENT_TYPE = 'CONFIDENTIAL'
      OAUTH_REDIRECT_URI = 'https://api.zwiron.com/v1/oauth/callback'
      OAUTH_ISSUE_REFRESH_TOKENS = TRUE
      OAUTH_REFRESH_TOKEN_VALIDITY = 7776000;
    ```

    <Note>
      You need the `ACCOUNTADMIN` role to create Security Integrations.
    </Note>

    ### Step 2 — Get your client ID and secret

    ```sql theme={null}
    SELECT SYSTEM$SHOW_OAUTH_CLIENT_SECRETS('ZWIRON_OAUTH');
    ```

    This returns a JSON object. Copy the values of `OAUTH_CLIENT_ID` and `OAUTH_CLIENT_SECRET` — you'll need them in the next step.

    ### Step 3 — Add the credentials to Zwiron

    Contact your Zwiron administrator to set the following environment variables for your workspace:

    ```
    OAUTH_SNOWFLAKE_CLIENT_ID=<your client ID>
    OAUTH_SNOWFLAKE_CLIENT_SECRET=<your client secret>
    ```

    ### Step 4 — Find your account identifier

    **You must provide your account identifier to start OAuth.** Unlike providers such as GitHub, Snowflake has no single global login URL — every customer has their own dedicated server (e.g. `ym34109.ap-southeast-1.snowflakecomputing.com`). Zwiron needs to know which server to open the OAuth popup for before the login can begin. There is no way to discover this automatically.

    Your account identifier is the subdomain from your Snowflake login URL — **including the region**.

    | Your Snowflake URL                                      | Account identifier to enter |
    | ------------------------------------------------------- | --------------------------- |
    | `https://ym34109.ap-southeast-1.snowflakecomputing.com` | `ym34109.ap-southeast-1`    |
    | `https://xy12345.us-east-1.snowflakecomputing.com`      | `xy12345.us-east-1`         |
    | `https://myorg-myaccount.snowflakecomputing.com`        | `myorg-myaccount`           |

    <Tip>
      Not sure of your full identifier? Run this in a Snowflake worksheet:

      ```sql theme={null}
      SELECT CURRENT_ACCOUNT(), CURRENT_REGION();
      ```

      Or go to **Admin → Accounts** in the Snowflake UI — hover your account name to copy the full identifier.
    </Tip>

    <Warning>
      Always include the region (e.g. `ym34109.ap-southeast-1`, not just `YM34109`). Using the short ID without the region will result in a 404 error when the OAuth popup opens.
    </Warning>

    ### Step 5 — Connect in Zwiron

    1. Go to **Connections → Add Connection → Snowflake**
    2. Enter your **Account** identifier (e.g. `ym34109.ap-southeast-1`)
    3. Click **Sign in with Snowflake**
    4. A Snowflake login popup opens — sign in and approve the access request
    5. Zwiron fills your credentials automatically
    6. Fill in **Database** and **Warehouse** in the form
    7. Click **Test Connection** to verify, then **Next** to save
  </Tab>

  <Tab title="Username + Password">
    ### Step 1 — Create a dedicated Snowflake user

    We recommend creating a separate user and role so you can control exactly what Zwiron can access.

    Run this in a Snowflake worksheet (replace the placeholders with your values):

    ```sql theme={null}
    -- Create a role for Zwiron
    CREATE ROLE zwiron_role;

    -- Create a user
    CREATE USER zwiron_user
      PASSWORD = 'your_strong_password'
      DEFAULT_ROLE = zwiron_role
      DEFAULT_WAREHOUSE = 'your_warehouse';

    -- Grant the role to the user
    GRANT ROLE zwiron_role TO USER zwiron_user;

    -- Grant warehouse access
    GRANT USAGE ON WAREHOUSE your_warehouse TO ROLE zwiron_role;

    -- Grant database and schema access
    GRANT USAGE ON DATABASE your_database TO ROLE zwiron_role;
    GRANT USAGE ON SCHEMA your_database.your_schema TO ROLE zwiron_role;

    -- Grant table permissions
    GRANT CREATE TABLE ON SCHEMA your_database.your_schema TO ROLE zwiron_role;
    GRANT INSERT, UPDATE, DELETE, SELECT ON ALL TABLES IN SCHEMA your_database.your_schema TO ROLE zwiron_role;
    GRANT INSERT, UPDATE, DELETE, SELECT ON FUTURE TABLES IN SCHEMA your_database.your_schema TO ROLE zwiron_role;
    ```

    ### Step 2 — Connect in Zwiron

    1. Go to **Connections → Add Connection → Snowflake**
    2. Fill in the connection fields:

    | Field         | What to enter                                      |
    | ------------- | -------------------------------------------------- |
    | **Account**   | Your account identifier, e.g. `abc12345.us-east-1` |
    | **Auth type** | `password`                                         |
    | **User**      | `zwiron_user`                                      |
    | **Password**  | The password you set above                         |
    | **Database**  | Your target database name                          |
    | **Warehouse** | Your warehouse name                                |
    | **Schema**    | Target schema (defaults to `PUBLIC`)               |
    | **Role**      | `zwiron_role`                                      |

    3. Click **Test Connection** — you should see "Connection successful"
    4. Click **Next** to name and save the connection
  </Tab>
</Tabs>

***

## Finding your account identifier

Your account identifier is shown in Snowflake under **Admin → Accounts**. Hover over your account name to see the full identifier.

The format is usually one of:

* `abc12345.us-east-1` (legacy)
* `orgname-accountname` (newer organisation accounts)

***

## How Zwiron writes to Snowflake

Zwiron uses Snowflake's **bulk COPY INTO** for high-throughput writes — data is staged internally then loaded in one shot. This is far faster than row-by-row inserts.

For **upsert** mode, Zwiron uses a staging table + `MERGE INTO` pattern:

1. Write incoming rows to a temporary staging table
2. `MERGE INTO` the target table using the primary key
3. Drop the staging table

***

## Schema management

Zwiron creates tables automatically if they don't exist, mapping source types to Snowflake types:

| Source type      | Snowflake type |
| ---------------- | -------------- |
| INTEGER / BIGINT | NUMBER         |
| FLOAT / DOUBLE   | FLOAT          |
| VARCHAR / TEXT   | VARCHAR        |
| BOOLEAN          | BOOLEAN        |
| DATE             | DATE           |
| TIMESTAMP        | TIMESTAMP\_NTZ |
| JSON / JSONB     | VARIANT        |
| BYTES / BLOB     | BINARY         |

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="Databases, schemas, or warehouses not showing in the dropdown">
    Zwiron lists only resources that your role has access to. If you don't see a database, schema, or warehouse in the dropdown after connecting:

    1. Check which role is active — the dropdown only shows resources visible to that role
    2. Grant the missing permissions in a Snowflake worksheet:

    ```sql theme={null}
    -- Grant database access
    GRANT USAGE ON DATABASE your_database TO ROLE your_role;

    -- Grant schema access
    GRANT USAGE ON SCHEMA your_database.your_schema TO ROLE your_role;
    GRANT CREATE TABLE ON SCHEMA your_database.your_schema TO ROLE your_role;

    -- Grant warehouse access
    GRANT USAGE ON WAREHOUSE your_warehouse TO ROLE your_role;
    ```

    3. If the schema does not exist yet, create it first:

    ```sql theme={null}
    CREATE SCHEMA IF NOT EXISTS your_database.your_schema;
    GRANT ALL PRIVILEGES ON SCHEMA your_database.your_schema TO ROLE your_role;
    GRANT ALL PRIVILEGES ON FUTURE TABLES IN SCHEMA your_database.your_schema TO ROLE your_role;
    ```

    After granting permissions, go back to Zwiron and re-test the connection — the resources will appear.
  </Accordion>

  <Accordion title="OAuth popup closes but nothing happens">
    Make sure the `OAUTH_REDIRECT_URI` in your Security Integration exactly matches `https://api.zwiron.com/v1/oauth/callback`. Even a trailing slash difference will cause a redirect mismatch error from Snowflake.
  </Accordion>

  <Accordion title="'This integration is not enabled' error">
    Run `ALTER SECURITY INTEGRATION zwiron_oauth SET ENABLED = TRUE;` in Snowflake.
  </Accordion>

  <Accordion title="Warehouse suspended / auto-resume not enabled">
    Enable auto-resume: `ALTER WAREHOUSE your_warehouse SET AUTO_RESUME = TRUE;`
  </Accordion>

  <Accordion title="Insufficient privileges">
    Make sure the Zwiron role has `CREATE TABLE` on the schema and `INSERT`, `UPDATE`, `DELETE` on the tables. Re-run the setup SQL from the password tab above.
  </Accordion>

  <Accordion title="Account identifier format">
    Use the full identifier including cloud region, e.g. `abc12345.us-east-1`. Find it in Snowflake under **Admin → Accounts**. For organisation accounts use `orgname-accountname`.
  </Accordion>
</AccordionGroup>
