What does Airbyte write into its raw destination tables before typing and deduping?
answer
- records land uninterpreted before they land typed
- the payload arrives as JSON with metadata beside it
- one timestamp for emission, one for consumption
- a bad value annotates the row instead of failing the sync
- query the final table, never the internal one
basics
~20 sEach record lands as a JSON payload plus Airbyte metadata — a generated record id, an extraction timestamp and a loaded timestamp — in a raw table in a separate internal schema. A later step parses it into typed columns in the final table.
solid answer
~50 sModern Airbyte destinations load in two stages. Stage one appends every record, untyped, into a **raw table** in Airbyte's own schema (`airbyte_internal` by default rather than your destination schema). The row holds the record as JSON alongside metadata columns: `_airbyte_raw_id`, `_airbyte_extracted_at` (when the source emitted it) and `_airbyte_loaded_at` (when the typing step consumed it). Stage two — typing and deduplication — parses that JSON into the typed columns of the **final table** in your destination schema and, for deduped streams, collapses to one row per primary key. A value that cannot be coerced into its declared type does not fail the sync; the problem is recorded in the `_airbyte_meta` column of the final row so the rest of the record still lands. Build models on the final table. The raw table is Airbyte's working area: append-only, duplicate-tolerant and subject to change between versions.
code
sql · 7 linesSELECT _airbyte_raw_id,
_airbyte_extracted_at,
_airbyte_loaded_at,
_airbyte_data
FROM airbyte_internal.public_raw__stream_orders
WHERE _airbyte_loaded_at IS NULL
ORDER BY _airbyte_extracted_at DESC;go deeper
Recall that a record lands twice: once as JSON in an Airbyte-managed raw table, then as typed columns in the table you query. Query the second one.
Explain why the split exists — land first so a schema surprise cannot poison extraction, interpret second with per-row error capture — and what the extraction timestamp is used for.
Bring the operational angle: raw-table growth and retention, typing-and-deduplication compute scaling with sync frequency, and alerting on the metadata column instead of trusting a green sync.
Own what the destination layout means for consumers: the contract that models read final tables only, retention and cost policy for internal storage, and how you survive a destination version change that reshapes it.
## Two stages, two tables A destination write in modern Airbyte is not one operation. Records first land verbatim in a **raw table**, then a second step turns them into the **final table** consumers query. Understanding the split explains most of the confusing things people see in a destination: duplicate rows that are supposed to be deduplicated, an unfamiliar schema full of underscore-prefixed columns, and a sync that succeeded while one field arrived null. ## The raw table Raw tables live in an Airbyte-managed schema, `airbyte_internal` by default, and the schema name is configurable in the destination settings. Each row carries the record as a JSON document plus metadata: - `_airbyte_raw_id` — a generated identifier for the raw record. - `_airbyte_extracted_at` — when the source emitted the record. This is the timestamp deduplication uses to break ties and the one you filter on when reading snapshots from a full-refresh-append stream. - `_airbyte_loaded_at` — set when the typing step has consumed the row; a null value means it is still pending. - `_airbyte_data` — the record payload as JSON. The raw table is strictly append-only within a sync and tolerates everything: boundary re-reads, retry replays, records whose types do not match the declared schema. That tolerance is the point — landing first and interpreting second means a schema surprise cannot destroy the extraction. ## The final table The final table is what you name in the connection's destination namespace, with real typed columns derived from the stream's schema. For deduped streams it holds one row per primary key; for append streams it holds everything. It also carries `_airbyte_meta`, a structured column recording per-row problems the typing step hit. That last column is the one worth remembering. If a field declared as a number arrives as the string `"n/a"`, Airbyte does not fail the sync and does not silently discard the record. It writes the row with that field unset and notes the problem in `_airbyte_meta`. The practical consequence: a green sync is not evidence that every value survived typing, and a quality check that never inspects `_airbyte_meta` will not notice a source that started sending garbage in one field. ## Why the split exists Historically, Airbyte destinations wrote a raw table and then ran a separate **basic normalization** step, implemented with dbt, to produce the typed and deduplicated tables. That approach coupled the destination to a transformation runtime, was slow on large streams, and failed the whole sync on a type mismatch. It was replaced by destination-native typing and deduplication executed by the destination's own SQL engine, which is faster, does not require dbt, and degrades per-row rather than per-sync. Which of the two a given deployment does depends on its version and destination — worth stating as an assumption rather than asserting. ## How this should change what you do Point downstream models at the final table. Reading the raw table directly means parsing JSON yourself, inheriting the duplicates that deduplication exists to remove, and coupling your models to an internal layout Airbyte is free to change between releases. Do budget for the raw table, though. It grows with every record ever landed for the stream, so its storage cost is real and it is a legitimate target for a retention policy — bearing in mind that clearing raw data undermines the ability to rebuild the final table without a full re-read. And watch the second stage as a cost centre. Typing and deduplication is work the destination warehouse performs on every sync, scaling with the volume landed. A stream synced very frequently with a large key space can spend more compute rebuilding its final table than extracting from the source — a real reason to sync some streams less often rather than more. ## In an interview Describe the two stages and the reason for the ordering: land untyped so extraction cannot be poisoned by a schema surprise, then interpret with per-row error capture instead of per-sync failure. Naming `_airbyte_extracted_at` and `_airbyte_meta` and their uses is enough detail; reciting the whole layout is not the point, and the layout is version-dependent.
- A field declared as an integer arrives as the string "unknown". What does the sync do?It does not fail. The record is written with that field unset and the typing problem is recorded in the row's `_airbyte_meta` column, so the rest of the record still lands. That is why a green sync history is not proof that every value survived, and why `_airbyte_meta` deserves a quality check of its own.
- Why shouldn't downstream models read the raw tables directly?They hold untyped JSON, contain duplicates the deduplication step exists to remove, and use an internal layout Airbyte may change between versions. Models built on them re-implement parsing and deduplication and break on upgrade. Read the final table in your destination schema instead.
- What drives the cost of the second stage?It is warehouse compute proportional to the records landed since the last run and the size of the final table being rebuilt for deduped streams. A very frequent schedule over a large key space can make deduplication cost more than extraction, which is an argument for syncing that stream less often.
saying these in an interview costs you the question
- Thinks records are written straight into typed final tables
- Builds downstream models on the internal raw tables
- Assumes a type mismatch fails the whole sync
- Never checks the metadata column for typing problems
- Says raw tables are deleted automatically after each sync