Your dbt DAG is the pipeline.
Pull data from SaaS APIs as ordinary dbt models. Declare the source, add a model, and the data incrementally lands in your warehouse on every dbt run. Extract, load, and transform in one dbt project, one DAG.
the extract is just another node — dbt run does the rest
Retire the standalone EL tool
Most stacks bolt a separate extract-and-load product onto dbt — its own
scheduler, its own UI, its own copy of your data. With Datattach the
extract is an ordinary dbt model: the body names the
source object, dbt run triggers the pull, and the table lands where your models already build.
- No separate EL tool — the extract is a node in your dbt DAG
- No second scheduler — dbt run (core or Cloud) triggers everything
- Data lands in a catalog you own — never a copy inside Datattach
- Downstream models ref() the landed table in the same run
-- models/ingest/stg_stripe_customers.sql
{{ config(cursor='created') }}
SELECT * FROM {{ source('stripe_prod', 'customers') }} -- models/marts/customers_by_country.sql
SELECT country, COUNT(*)
FROM {{ ref('stg_stripe_customers') }}
GROUP BY 1 Declared entirely in your dbt repo
An admin registers the API credential once. Everything else is plain dbt config, version-controlled with the rest of your project — and the Connect dbt page generates it for your registered sources.
version: 2
sources:
- name: stripe_prod # the source registered in Datattach
schema: stripe_prod
tables:
- name: customers
- name: charges models:
your_project:
ingest:
+materialized: ingest
+database: your_warehouse # a catalog you own
+schema: raw Incremental loads, zero bookkeeping
Every run extracts only what's new — and there is no hidden state to babysit. No cursor tables, no staging schemas, no sync-state dashboard. The landed table itself is the source of truth.
- State lives in the destination — Datattach reads max(cursor) from the landed table and asks the API only for newer records
- No cursor store to manage or reset — drop the table and the next run is naturally a full extract
- Rows upsert on the object's id — overlapping windows never duplicate
- dbt run --full-refresh rebuilds from everything the API still serves
- Nested objects flatten into columns; nested lists become queryable child tables
Any source. Any destination.
Extraction is destination-agnostic — every source can land in any supported warehouse you own, and new sources arrive with the full matrix from day one.
customers, charges, invoices, subscriptions, products, prices, payment intents, refunds, coupons, plans
On the roadmap
Lands in a warehouse you own
One DAG instead of two systems
Everything a dedicated EL tool does for SaaS sources — without the second system to run, sync, and secure.
Built for the governed stack
Credentials never leave the platform
The API key is registered once by an admin and encrypted at rest. It never appears in dbt config, logs, or model output — your repo stays secret-free.
Your warehouse, your data
The destination is a catalog owned by your organization. Datattach keeps no staging copy of your SaaS data — the landed table is the only copy, and it's yours.
Governed like every other write
The load runs as the admin-gated build principal through the query engine, subject to the same access rules as any query — and every job is audited end to end.
API to insight, in one dbt run.
See how Datattach turns dbt into your whole data platform — ingestion, transformation, and governance behind one connection.