---
title: "Export joined data across multiple DQ topics"
method: POST
path: "/egress/export"
tags: ["Data Egress"]
---

# Export joined data across multiple DQ topics

`POST /egress/export`

Streams the full result set of rows joined across an arbitrary
connected subset of the DQ topics (`jobs`, `monitors`, `rules`)
in a single pass for bulk extraction into BI tools, data pipelines,
and integrations.

The request body is **optional**: POST with no body (or a body that
omits `topics`) to export all three topics — `jobs`, `monitors`,
`rules` — with every other default applied.

**Response format (content negotiation).** The format is selected by
the `Accept` request header:
- `Accept: application/x-ndjson` → newline-delimited JSON
  ([JSON Lines](https://jsonlines.org)): one JSON object per line.
  Preferred for programmatic consumers — types are
  preserved (numbers stay numbers, `null` stays `null`) and rows can
  be parsed incrementally.
- `Accept: text/csv` → CSV download. The header row is the qualified
  field names (e.g. `rules.ruleName`). List-typed fields
  (`jobs.daysOfWeek`, `monitors.dimensionNames`, `rules.dimensionNames`)
  render as pipe-delimited cells (`Completeness|Accuracy`) so
  consumers don't have to handle quoted-comma CSV.

When `Accept` is absent or `*/*`, the server returns
`application/x-ndjson`; any other media type is rejected with
HTTP 406. Both formats stream the same rows in the same order; only
the encoding differs.

**Topics.** `JOBS`, `MONITORS`, and `RULES` are mutually
joinable — any non-empty combination of them is valid. Quality
dimensions are not a topic: the names of the quality dimensions
assigned to a monitor (and to a rule — a rule is a `RULE`-type
monitor) are surfaced inline on the row as the
`monitors.dimensionNames` / `rules.dimensionNames` list field
(active assignments only; empty when none are assigned).

To discover what topics can be joined call GET `/egress/manifest` first. Each topic's
`joinsTo` lists the other topics it can be joined to; your selected
`topics` are valid if they form a connected subgraph over those edges.
The manifest also lists each field's `filterable` flag and
`allowedOperators`, so a client can fully validate a request locally
before POSTing.

**Filters.** Optional predicates in the body's `filters` array, **ANDed**
together (omit for no filtering beyond RBAC and the default window). Each
`EgressFilter` is a `field`, an `op`, and a value whose shape depends on
the operator:
- `value` (a single scalar) for `EQ`, `NE`, `LIKE`, `GT`, `GTE`, `LT`, `LTE`
- `values` (an array) for `IN`, `NOT_IN`
- neither for `IS_NULL`, `IS_NOT_NULL`

`field` is the qualified name `<topic>.<apiName>` (e.g. `rules.ruleName`)
and must belong to a **selected** topic; each field permits only a subset
of operators. Call `GET /egress/manifest` to discover the filterable
fields and their `allowedOperators`. A request fails with HTTP 400 if a
filter names an unknown field, uses an operator the field doesn't allow,
or references a field whose topic isn't in `topics`. Filtering on any
`*.updatedAt` field also overrides the default time window (see below).

**Ordering.** Custom sort keys are not currently a request parameter.
Rows are always returned ordered by `<granularity>.updatedAt`
descending — the same field the default time window keys on (rules >
monitors > jobs) — followed by a unique tiebreaker for deterministic
streaming.

**Projection.** is not currently a request parameter. Each row is a
key/value map containing all projectable fields of the selected
topics, in manifest order (fields marked `optIn: true` are excluded
unless explicitly enabled — see catalog assets below). Use
`GET /egress/manifest` to discover the supported topics, fields,
operators, and opt-in flags.

**Catalog assets (opt-in).** Set `includeCatalogAssets` (query parameter
or request-body property — enrichment applies when either is `true`;
default `false`) to add, to each row, the DGC Catalog assets it maps to.
Three field groups are populated, all omitted when the flag is off:

- **Table** — `jobs.tableCatalogAssetId` / `...DisplayName` / `...Type`:
  the Catalog `Table` asset for the row's table. One per row.
- **Column** — `monitors.columnCatalogAsset*` and
  `rules.columnCatalogAsset*`: the `Column` asset for the row's column
  (`monitors.columnName` / `rules.ruleColumnName`). One per row, or
  `null` when the row has no column scope (e.g. a dataset-level monitor).
- **Business Rule** — `monitors.businessRuleLinks`: the `Business Rule`
  assets linked to the monitor, matched by identity (a rule surfaces here
  as its `RULE`-type monitor). Collapsed into one list of
  `"<assetId>::<signifier>"` entries, so a monitor linked to several rules
  stays one row (no row multiplication).

Two query parameters trim the enrichment (both default `false`):
`excludeColumnAssets` omits the Column group; `excludeRuleAssets` omits
the Business Rule group. Both are no-ops unless `includeCatalogAssets`
is in effect.

**Default time window.** When the request omits any `*.updatedAt`
filter, the server injects a rolling window on the `updatedAt` of a
selected topic, anchored in the order monitors > rules > jobs.
Its length is the `window` query parameter in days, defaulting to 90.
To set an exact range instead, filter explicitly on any `*.updatedAt`
field — the server suppresses the injected window (and ignores `window`)
once any such filter is present. The default-window contract is also
surfaced in `defaultWindow` on the manifest response.

**Permissions**
- **Required:** `DATA_QUALITY` and `DATA_QUALITY_JOB_VIEW`. Results are
  scoped to the jobs the caller can see.
- **Global bypass:**
  - `DATA_QUALITY` and `VIEW_PERMISSIONS_VIEW_ALL`
  - `DATA_QUALITY` and `RESOURCE_MANAGE_ALL`

## Query parameters

- `window` integer
- `includeCatalogAssets` boolean
- `excludeRuleAssets` boolean
- `excludeColumnAssets` boolean
- `completionMarker` boolean

## Request body

- EgressRequest — Request body for `POST /egress/export`. Optional — omit the body (or omit `topics`) to export all three topics with defaults. Any `topics` you do select must form a connected subgraph in the manifest's join graph.
  - `topics` EgressTopic[] — Topics to include in the export. Omitted or empty → all three (`JOBS`, `MONITORS`, `RULES`). Today's surface exposes those three; the upper bound has headroom so adding a new topic doesn't break clients.
  - `filters` EgressFilter[] — Filter predicates ANDed together. Empty or omitted means no filtering.
    - `field` string, required — Qualified field name `<topic>.<apiName>`, e.g. `rules.ruleName`.
    - `op` 'EQ' | 'NE' | 'IN' | 'NOT_IN' | 'LIKE' | 'GTE' | 'LTE' | 'GT' | 'LT' | 'IS_NULL' | 'IS_NOT_NULL', required — Filter operator. Each field declares its allowed operators in the manifest; requesting an unsupported operator yields HTTP 400. `LIKE` exposes SQL `%` (zero-or-more chars) and `_` (single char) wildcards verbatim and is case-sensitive — the operator is a thin wrapper over the SQL `LIKE` predicate. Callers that want case-insensitive matching or anchored prefix/suffix can express that with the wildcards (`%foo%` for contains, `foo%` for prefix). The intentional SQL-flavoured shape mirrors the rest of the egress query surface; consumers that need a higher-level abstraction can layer it on the client.
    - `value` unknown
    - `values` unknown[] — List of values for `IN` / `NOT_IN`. Each item follows the same scalar-or-null contract as `value` above and is coerced to the field's declared type at validation time. Capped at 1000 items per request as a DoS guard; pageable surfaces should split larger lists across multiple requests.
      - unknown
  - `includeCatalogAssets` boolean — Opt in to catalog-asset enrichment. May also be set via the `includeCatalogAssets` query parameter (useful on a body-less request); enrichment applies when either is `true`. When `true`, each row is enriched with the DGC Catalog assets it maps to: the `Table` asset on `jobs`, the `Column` asset on `monitors`/`rules`, and the governing `Data Quality Rule` asset on `monitors`. See **Catalog assets** in the `POST /egress/export` description for the exact fields and the `excludeColumnAssets` / `excludeRuleAssets` switches. When `false` (default) no enrichment is performed and those fields are omitted — off by default because enrichment makes external round-trips to DGC. Note a monitor linked to several `Data Quality Rule` assets emits one row per linked asset; `excludeRuleAssets` opts out of that group (and its row multiplication).

## Response `200`

The egress export, streamed in the format selected by the
`Accept` header (`application/x-ndjson` by default, or `text/csv`).
The response carries `Content-Disposition: attachment` so browsers
download to disk; the filename extension reflects the negotiated
format (`.jsonl` / `.csv`).

**Streaming semantics.** Both response schemas are declared with
`format: binary` even though the bodies are text — this hints to
SDK codegen to produce a streaming `Resource` / `InputStream`
return type instead of buffering the entire payload in memory as
a `String`. Without that hint, regenerated SDKs default to
string-buffered responses, which defeats the streaming-export
design (and would OOM on large result sets). Treat the body as
text at the wire level; treat the SDK return type as a stream.

**Stream completeness.** Because the body streams, a failure after
the first byte cannot change the HTTP status — it surfaces only as a
short stream (plus a server-side error log) and can look like a
successful, fully-read extract. To let consumers detect this, the
stream always ends with an explicit completion marker once every row
has been written:
- NDJSON: a final line `{"_complete":true,"rowCount":N}` (data rows
  never carry a `_complete` key).
- CSV: a trailing comment line `# complete rowCount=N`.

A consumer that reaches end-of-stream **without** seeing the marker
must treat the extract as truncated/failed, not complete. `rowCount`
is the number of data rows emitted (excludes the header and marker).

## Other responses

- `400` — Bad request — invalid input or validation error.
- `401` — Unauthorized — authentication is required or the provided credentials are invalid.
- `403` — Forbidden — insufficient permissions.
- `406` — Not Acceptable
- `429` — Too Many Requests
- `500` — Internal Server Error

---

[API](https://skmtc.net/collibra/apis/collibra-data-governance-center-core-api.md) · [All operations](https://skmtc.net/collibra/apis/collibra-data-governance-center-core-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/collibra/collibra-data-governance-center-core-api/revisions/6d091bc86a33/schema)
