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

# Sources

> Declare external data inputs for your pipeline.

Sources declare external data that your models depend on. They are defined in YAML files under `sources/` in your project directory.

## Table sources

Point at an existing table or view in your warehouse:

```yaml theme={null}
sources:
  - name: raw_events
    database: analytics
    schema: raw
    table: events
```

Reference it in models with `__source("raw_events")`.

## Expression sources

Define source data inline as a SQL expression. No external tables or setup scripts needed:

```yaml theme={null}
sources:
  - name: raw__customers
    expression: |
      SELECT * FROM (VALUES
        (1, 'Leslie', 'Knope', 'leslie@pawnee.gov', TIMESTAMP '2026-01-15 09:00:00'),
        (2, 'Ron', 'Swanson', 'ron@pawnee.gov', TIMESTAMP '2026-02-01 08:00:00')
      ) AS raw__customers(id, first_name, last_name, email, created_at)
```

Expression sources are resolved at compile time. They're the escape hatch for anything the framework doesn't natively model: external tables, warehouse-specific syntax, function calls, or any other relation type that doesn't fit a standard table reference.

## Source audits

Sources support the same audit system as models. Audits attached to sources run before any dependent model is built:

```yaml theme={null}
sources:
  - name: raw_orders
    columns:
      - name: id
        audits:
          - not_null
          - unique
    audits:
      - expression_is_true:
          name: no future orders
          expression: "ordered_at <= CURRENT_TIMESTAMP"
```

If a source audit with `error` severity fails, all downstream models that depend on that source are blocked.

## Type enforcement

Type enforcement is implicit for sources, the same as for models. If any column declares a `type`, SQLBuild automatically casts that column and uses declared types for schema-change detection:

```yaml theme={null}
sources:
  - name: raw__customers
    expression: |
      SELECT 1 AS id, 'Leslie' AS first_name, 'Knope' AS last_name
    columns:
      - name: id
        type: INTEGER
      - name: first_name
      - name: last_name
```

In this example, only `id` has a type declared, so only `id` is cast. Columns without a `type` are passed through unchanged.

For expression sources, SQLBuild probes the expression's output columns and builds a projection that casts typed columns while preserving the rest. For table sources, it uses warehouse metadata to validate that declared column names exist and applies casts accordingly.

You can explicitly set `type_enforcement: false` on a source to disable casting even when column types are declared.

## Managed sources (loaders)

Sources can be loaded by Python functions instead of pointing at existing tables or inline expressions. Set `managed: true` to bind a source to the `@loader` function **of the same name**, and SQLBuild will call it to populate the source table:

```yaml theme={null}
sources:
  - name: raw_customers
    managed: true
    write_strategy: table
    columns:
      - name: id
        type: INTEGER
      - name: name
        type: VARCHAR
```

Managed sources support incremental write strategies (`table`, `append`, `delete_insert`, `merge`), cursor-based loading, and concurrent execution.

See [Loaders](/concepts/python-nodes/loaders) for the full guide on writing loader functions, write strategies, the loader context API, and auto-load behavior during builds.

### Declarative integrations (no Python)

For common ingestion you can declare the source entirely in YAML, with no `@loader` function. SQLBuild generates the loader for you and runs it during the build:

* [dlt](/integrations/dlt) - declare `dlt_sources` for `rest_api`, `sql_database`, and `filesystem` sources.
* [ingestr](/integrations/ingestr) - add an `ingestr` block to a source to pull from 50+ sources.

## Source freshness

Source freshness lets SQLBuild observe whether a source's data has changed between runs. This feeds into [planning and change detection](/concepts/planning): under `--changes-only`, models downstream of unchanged sources are skipped.

Configure freshness per source with a `freshness:` block:

```yaml theme={null}
sources:
  - name: raw_events
    schema: raw
    table: events
    freshness:
      strategy: column
      column: updated_at
      type: timestamp
      lag_tolerance: 15m
```

### Strategies

| Strategy  | Description                                                                                 | Required fields  |
| --------- | ------------------------------------------------------------------------------------------- | ---------------- |
| `adapter` | Uses warehouse metadata (e.g. Snowflake `LAST_ALTERED`). No query against the source table. | None             |
| `column`  | Reads `MAX(column)` from the source table.                                                  | `column`, `type` |
| `sql`     | Runs a custom query that returns a single scalar value.                                     | `query`, `type`  |

#### adapter

```yaml theme={null}
freshness:
  strategy: adapter
```

Uses adapter-level table metadata. Supported on Snowflake, BigQuery, Databricks, PostgreSQL, DuckDB, and SQL Server. Does not support `type`, `column`, or `query`.

#### column

```yaml theme={null}
freshness:
  strategy: column
  column: updated_at
  type: timestamp
```

Queries `MAX(column)` from the source table. The column must be a plain column name (no expressions; use `sql` strategy for those). Requires `type`.

#### sql

```yaml theme={null}
freshness:
  strategy: sql
  query: "SELECT MAX(version_id) FROM raw.events"
  type: integer
```

Runs an arbitrary SQL query that returns a single scalar. Requires `type`. Does not support `column`.

### Type

The `type` field declares the value kind for comparison:

| Type        | Description                                         |
| ----------- | --------------------------------------------------- |
| `timestamp` | Datetime value. Supports `lag_tolerance`.           |
| `integer`   | Integer value. Change detected by exact comparison. |
| `string`    | String value. Change detected by exact comparison.  |

### Lag tolerance

`lag_tolerance` is optional and only valid with `type: timestamp`. It declares how much the observed timestamp can drift from the previous observation before being treated as a change:

```yaml theme={null}
freshness:
  strategy: column
  column: updated_at
  type: timestamp
  lag_tolerance: 2h
```

Accepts positive durations: `15m` (minutes), `2h` (hours), `1d` (days). If the current observation is within the tolerance of the previous one, the source is treated as unchanged.

### Auto-observation

Sources without an explicit `freshness:` block are auto-observed using the `adapter` strategy if:

* The source has a physical table (not an expression source)
* The source is not managed
* The adapter supports table freshness metadata

This means most unmanaged table sources get freshness tracking automatically on adapters that support it, with no configuration needed.

Use [`sqb freshness`](/cli/freshness) to observe source freshness on demand without triggering a build. See [Source freshness](/concepts/planning/source-freshness) for how freshness feeds into change-aware builds.

### Freshness config reference

| Field           | Description                                                                                               |
| --------------- | --------------------------------------------------------------------------------------------------------- |
| `strategy`      | Observation strategy: `adapter`, `column`, or `sql`                                                       |
| `type`          | Value kind: `timestamp`, `integer`, or `string`                                                           |
| `column`        | Column name for `column` strategy                                                                         |
| `query`         | SQL query for `sql` strategy                                                                              |
| `lag_tolerance` | Duration tolerance for timestamp comparisons (e.g. `15m`, `2h`, `1d`). Only valid with `type: timestamp`. |

## Config reference

| Field              | Description                                                                                                                 |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------- |
| `name`             | Source name, used in `__source("name")` references                                                                          |
| `database`         | Target database (optional)                                                                                                  |
| `schema`           | Target schema (optional)                                                                                                    |
| `table`            | Target table name (defaults to `name` if omitted)                                                                           |
| `expression`       | Inline SQL expression (alternative to table reference)                                                                      |
| `managed`          | Set to `true` to bind the source to the `@loader` function of the same name (see [Loaders](/concepts/python-nodes/loaders)) |
| `write_strategy`   | How the loader writes data: `table`, `append`, `delete_insert`, or `merge` (requires `managed: true`)                       |
| `cursor_column`    | Column for incremental cursor tracking (required for `delete_insert` and `merge`)                                           |
| `unique_key`       | Merge key column(s) (required for `merge`)                                                                                  |
| `freshness`        | Source freshness observation config (see [Source freshness](#source-freshness))                                             |
| `description`      | Human-readable description                                                                                                  |
| `type_enforcement` | Override implicit type enforcement (`true`/`false`). Defaults to `true` when any column declares a type.                    |
| `contract`         | `enforced` or `none`. When enforced, downstream models validate configured column references against source columns.        |
| `columns`          | Column declarations with optional types and audits                                                                          |
| `audits`           | Source-level audits                                                                                                         |
