Creating a Loader
This guide walks you through setting up a new loader in Zeotap. A loader connects to a SaaS application, extracts data from selected objects, and writes it into your data warehouse on a recurring schedule.
Prerequisites
Before creating a loader, ensure you have:
- A Zeotap workspace with appropriate permissions (Owner, Admin, or a role with
loaders:createpermission) - A configured Warehouse (the target warehouse where loader data will be written)
- Credentials for the SaaS application you want to connect (OAuth access or API key)
- Write permissions on the target schema in your warehouse
Step-by-Step Guide
Step 1: Navigate to Loaders
- Log in to your Zeotap workspace
- Click Loaders in the left sidebar
- Click the Add Loader button in the top-right corner
Step 2: Select the Source Application
Choose the SaaS application you want to pull data from. Zeotap supports 15+ connectors across CRM, marketing, advertising, payments, support, and productivity categories.
Each card shows a short description of the connector; hover over it (or focus it with the keyboard) to read the full text. Once you pick a connector, its full description is shown at the top of the configuration step.
Each connector has its own authentication flow and available objects. See the individual connector guides for detailed setup instructions.
Step 3: Authenticate
Depending on the connector, you’ll authenticate using one of the following methods:
| Method | Flow | Connectors |
|---|---|---|
| OAuth 2.0 | Click “Connect” and authorize via the application’s login page. Zeotap handles token storage and refresh. | Salesforce, HubSpot, Google Ads, Facebook Ads, LinkedIn Ads, Shopify, Intercom, GitHub, Slack |
| API Key | Paste your API key or secret directly into the configuration form. | Stripe, HubSpot (alternative), Zendesk |
| OAuth 2.0 Client Credentials | Provide your client ID and client secret. Zeotap exchanges them for an access token. | Marketo |
| API Token | Provide a personal or workspace API token along with your account identifier. | Zendesk, Jira |
| Personal Access Token (PAT) | Generate a token in the application’s developer settings and paste it into Zeotap. | GitHub (alternative), Jira (alternative) |
All credentials are encrypted at rest using AES-256 encryption.
Step 4: Discover and Select Objects
After authentication, Zeotap discovers the available objects and streams from the connected application. You’ll see a list of all available objects with metadata:
- Object name — The API name of the object (e.g.,
Contact,deals,charges) - Record count — Estimated number of records (when available from the API)
- Cursor field — The field used for incremental sync (e.g.,
updated_at,SystemModstamp); a stream without one is read in full every run
Select the objects you want to sync. You don’t need to select everything — choose only the objects that are relevant to your use case to minimize API usage and warehouse storage.
For each selected object, you can:
- Choose its sync mode — Full refresh, Append or Merge. The select is pre-filled with the connector’s recommendation for that object where it has one, and with the loader’s Default sync mode otherwise. Changing the default offers Apply to all streams. The form warns if you choose Append for a stream with no cursor, because every run would add another full copy of it. See Sync mode per stream.
- Set the merge key — For a stream in Merge mode, specify which field(s) uniquely identify a record, overriding the key the connector declares
Step 5: Configure the Target Warehouse
Specify where loader data should be written:
| Setting | Description | Example |
|---|---|---|
| Target Warehouse | The warehouse connection to write into | Production Snowflake |
| Target Schema | The schema where tables will be created | SALESFORCE_RAW |
| Table Prefix | Optional prefix for all table names created by this loader | sf_ |
Zeotap creates tables automatically. If a table already exists, the loader replaces, appends to or merges into it according to that stream’s sync mode.
Step 6: Configure the Schedule
Set the sync frequency:
| Interval | Best For |
|---|---|
| Every 15 minutes | Critical operational data (e.g., support tickets, orders) |
| Hourly | General-purpose, balances freshness with API efficiency |
| Every 6 hours | Operational data that doesn’t need real-time freshness |
| Daily | Reference data, large full-refresh tables, cost-sensitive workloads |
| Custom cron | Advanced scheduling needs (e.g., 0 2 * * 1-5 for weekday 2 AM runs) |
All schedules are evaluated in UTC. You can also choose to leave the schedule paused and trigger runs manually.
Step 7: Name and Save
Give your loader a descriptive name (e.g., “Salesforce Production” or “Stripe Payments”) and click Save.
Zeotap will:
- Create the target tables in your warehouse
- Run an initial full sync to backfill historical data
- Begin the recurring schedule for incremental syncs
The initial sync may take longer depending on data volume. You can monitor progress from the loader’s detail page.
Using the API
You can create loaders programmatically with the Loaders API. Paths are workspace-scoped, and requests need the Authorization and X-Workspace-ID headers described in Authentication.
First list the streams the credentials can read, with each stream’s cursor, key and recommended sync mode:
curl -X POST https://agentic.zeotap.com/api/v1/workspaces/{id}/loaders/discover \
-H "Authorization: Bearer $API_TOKEN" \
-H "X-Workspace-ID: <workspace-id>" \
-H "Content-Type: application/json" \
-d '{
"loader_type": "stripe",
"config": {},
"credentials": {"secret_key": "<your Stripe secret key>"}
}'Then create the loader with the streams you want:
curl -X POST https://agentic.zeotap.com/api/v1/workspaces/{id}/loaders \
-H "Authorization: Bearer $API_TOKEN" \
-H "X-Workspace-ID: <workspace-id>" \
-H "Content-Type: application/json" \
-d '{
"name": "Stripe Payments",
"loader_type": "stripe",
"source_id": "770e8400-e29b-41d4-a716-446655440000",
"config": {},
"credentials": {"secret_key": "<your Stripe secret key>"},
"target_schema": "STRIPE_RAW",
"selected_streams": ["customers", "charges"],
"sync_mode": "incremental",
"stream_sync_modes": {"customers": "incremental_merge"},
"primary_keys": {"customers": ["id"]},
"schedule": "0 * * * *"
}'| Field | Meaning |
|---|---|
selected_streams | Stream names, exactly as discovery returns them |
sync_mode | The loader’s default sync mode: full_refresh, incremental (append) or incremental_merge (merge on key) |
stream_sync_modes | Modes for streams that differ from sync_mode. Omit it to get the connector’s recommended mode for each selected stream; send it to choose them yourself |
primary_keys | Optional merge keys per stream, overriding the connector’s |
schedule | A cron expression in UTC; omit it to run only on demand |
For OAuth connectors (Salesforce, HubSpot, Google Ads, …), complete the connect flow in the UI or through the OAuth endpoints and send "credentials": {"credential_ref": "<ref>"} with the reference it returns.
The response is 201 Created with the stored loader. Its stream_sync_modes holds the per-stream modes that were saved — here {"customers": "incremental_merge"}, so charges appends under the loader’s incremental default:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Stripe Payments",
"loader_type": "stripe",
"target_schema": "STRIPE_RAW",
"selected_streams": ["customers", "charges"],
"sync_mode": "incremental",
"stream_sync_modes": {"customers": "incremental_merge"},
"primary_keys": {"customers": ["id"]},
"change_detection": {},
"schedule": "0 * * * *",
"status": "draft",
"created_at": "2026-10-02T11:00:00Z"
}A new loader is saved as draft. Its schedule starts firing once a connection test (POST …/loaders/{loaderId}/test) succeeds and marks it active; you can trigger a run at any time with POST …/loaders/{loaderId}/trigger.
Managing Loaders
Editing a Loader
To modify an existing loader:
- Navigate to Loaders in the sidebar
- Click on the loader you want to edit
- Modify objects, schedule, or target configuration
- Click Save
Adding new objects triggers a backfill for those objects. Removing objects does not delete the corresponding warehouse tables — you must drop them manually if desired.
Pausing and Resuming
You can pause a loader to temporarily stop scheduled runs without losing cursor state. Click Pause on the loader’s detail page. Resume when ready — the next run picks up exactly where it left off.
Manual Runs
Click Run Now to trigger an immediate sync outside the regular schedule. This is useful for testing configuration changes or backfilling after a pause.
Deleting a Loader
Deleting a loader stops all scheduled runs and removes the loader configuration. Existing data in your warehouse is not affected — tables and data remain until you manually clean them up.
Common Issues
| Issue | Solution |
|---|---|
| OAuth token expired | Re-authenticate by clicking “Reconnect” on the loader detail page |
| API rate limit exceeded | Reduce sync frequency or select fewer objects |
| Target schema doesn’t exist | Create the schema in your warehouse before saving the loader |
| ”Permission denied” writing to warehouse | Ensure the warehouse connection user has write access to the target schema |
| Initial sync taking too long | Large datasets may take hours for the initial backfill — subsequent incremental syncs will be much faster |
| Missing records after sync | Verify the cursor field is correctly set and that the source API returns expected data for the date range |
Next Steps
- Choose a connector: Salesforce | HubSpot | Stripe | Zendesk
- Monitor your data pipeline from the Loaders dashboard
- Create a model using the loaded data