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

# Virtual APIs

> The foundation of DataHarbor. Publish governed, purpose-limited data products from a single source of truth.

# Virtual APIs

**One source of truth. Many governed views.**

A Virtual API is a governed view of an existing dataset. Instead of cloning data or building custom pipelines for each consumer, you define a declarative Virtual API Configuration that applies redaction, tokenization, and transformations at the field level — automatically, per route.

<Info>
  A Virtual API is also a pipeline definition: DataHarbor fetches upstream data, normalizes it into canonical JSON, matches the correct object definition, runs your ordered controls, then formats the response for delivery. See [Data Pipeline](./data-pipeline) for the full request flow.
</Info>

## How it works

1. **Enroll** your data source (HTTP and GraphQL today, data lakes coming soon)
2. **Define** your Virtual API Configuration — object definitions, controls, and access rules
3. **Publish** — your Virtual API is instantly available
4. **Monitor** — observe usage and schema changes over time

## Basic configuration

A Virtual API Configuration defines which fields to protect, organized by the source objects they apply to:

```yaml theme={null}
version: "0.3"
objects:
  customers:
    controls:
      - type: redact
        fields: [ssn, date_of_birth]
      - type: anonymize
        fields: [email]
```

This configuration redacts `ssn` and `date_of_birth` and anonymizes `email` for any request matching the `customers` route — such as `/api/customers` or `/api/customers/456`.

In the current runtime, controls live under named object definitions inside `objects`. There is no top-level `controls` list in the executed Virtual API Configuration.

<Tip>
  You'll see `fields` written two ways in our examples — both are valid YAML and produce identical results:

  ```yaml theme={null}
  # Inline style
  fields: [first_name, last_name]

  # Multi-line style
  fields:
    - first_name
    - last_name
  ```
</Tip>

## Controls are a pipeline

Controls execute **top-to-bottom** as an ordered pipeline. This means you can create a field with a [transform](../control-blocks/data-transform) and then apply a privacy control to it in a later step:

```yaml theme={null}
version: "0.3"
objects:
  customers:
    controls:
      # Step 1: Combine name fields into a single field
      - type: combine
        fields: [first_name, last_name]
        into: full_name
        separator: " "
        remove_source: true

      # Step 2: Hash the combined name
      - type: hash
        fields: [full_name]

      # Step 3: Redact email
      - type: redact
        fields: [email]
```

In this example, `full_name` is created by the combine, then hashed — and the original `first_name` and `last_name` fields are removed. Order matters: each control sees the result of the controls above it.

This ordered controls list runs after [input normalization](../sources/input-normalization) and before [output formatting](../delivery/output-formatting).

## Object definitions and route matching

Each key under `objects` is a named object definition that matches against URL path segments. DataHarbor matches the object name to the last (or second-to-last) segment of the request path.

<Info>
  GraphQL sources are the exception: object names come from the response shape under `data`, not from URL path segments. See [GraphQL Sources](../sources/graphql-sources) for the GraphQL-specific matching model.
</Info>

```yaml theme={null}
version: "0.3"
objects:
  addresses:
    controls:
      - type: redact
        fields: [name]
  users:
    controls:
      - type: redact
        fields: [metadata.tag]
```

| Request path      | Matched object |
| ----------------- | -------------- |
| `/v1/addresses`   | `addresses`    |
| `/v1/addresses/1` | `addresses`    |
| `/api/users/123`  | `users`        |

## Targeting nested and array fields

Target nested fields using dot notation, and array items using `[arrayName]` syntax:

```yaml theme={null}
version: "0.3"
objects:
  orders:
    controls:
      - type: redact
        fields: [name, '[addresses].name', '[addresses].city']
```

This redacts the top-level `name`, plus the `name` and `city` fields inside every item in the `addresses` array — all within a single object definition.

Use dot notation only in v0.3. Slash notation like `metadata/tag` is rejected.

If an endpoint returns a root array, the matched object's controls run against each object element automatically.

See [Field Targeting](../control-blocks/field-targeting) for the shared path rules used by Data Control and Data Transform.

For nested objects, use dot notation:

```yaml theme={null}
controls:
  - type: redact
    fields: [metadata.tag, metadata.viewCount]
```

## Multiple views from one source

One enrolled API can power many governed views, each with different control configurations:

```yaml theme={null}
# Partner View — redact identifiers
version: "0.3"
objects:
  customers:
    controls:
      - type: redact
        fields: [ssn, date_of_birth, email]
```

```yaml theme={null}
# Analytics View — tokenize for correlation
version: "0.3"
objects:
  customers:
    controls:
      - type: tokenize
        fields: [email, phone]
      - type: redact
        fields: [ssn]
```

```yaml theme={null}
# AI Training View — anonymize for model training
version: "0.3"
objects:
  customers:
    controls:
      - type: redact
        fields: ['[addresses].name']
      - type: anonymize
        fields: [metadata.viewCount]
```

No duplicated data. No cloned APIs.

## The \_default object

`_default` is an optional catch-all that applies when no named object matches the request route:

```yaml theme={null}
version: "0.3"
objects:
  addresses:
    controls:
      - type: redact
        fields: [name]
  _default:
    controls:
      - type: redact
        fields: [description]
```

**Precedence:** When a request matches a named object (e.g., `addresses`), only that object's controls apply. When no named object matches, `_default` controls apply.

## Spec options

| Option                  | Default       | Description                                                         |
| ----------------------- | ------------- | ------------------------------------------------------------------- |
| `input_format`          | Auto-detected | Expected upstream format. Values: `json`, `csv`, `yaml`, `markdown` |
| `default_output_format` | `json`        | Desired response format. Values: `json`, `markdown`, `csv`, `yaml`  |
| `failOnUnmatchedObject` | `false`       | Reject requests that don't match any defined object                 |
| `useStrictNameMatching` | `true`        | When `false`, match field names case-insensitively                  |

```yaml theme={null}
version: "0.3"
default_output_format: markdown
failOnUnmatchedObject: true
objects:
  addresses:
    controls:
      - type: redact
        fields: [name]
```

<Tip>
  Set `default_output_format: markdown` to make every response from this Virtual API agent-friendly. You can also request Markdown per-request by appending `.md` to the URL. See [Output Formatting](../delivery/output-formatting) for details.
</Tip>

<Info>
  If your upstream API serves Markdown, set `input_format: markdown` when the upstream `Content-Type` is missing or unreliable. See [Markdown Input](../delivery/markdown) for document-mode normalization, front matter rules, and limitations.
</Info>

## Versioning

Every change to a Virtual API creates a new version. You can:

* View the full version history
* Compare configurations between versions
* Roll back to a previous version
* Audit which version was active at any point in time

## Lifecycle

| State        | Description                                        |
| ------------ | -------------------------------------------------- |
| **Active**   | Live and accepting requests                        |
| **Expired**  | Past expiration date, no longer accepting requests |
| **Inactive** | Manually disabled, instant shutdown                |

## Next steps

<CardGroup cols={2}>
  <Card title="Data Pipeline" icon="workflow" href="./data-pipeline">
    See normalization, object matching, controls, and formatting end to end
  </Card>

  <Card title="Data Control" icon="shield" href="../control-blocks/data-control">
    Redaction, tokenization, anonymization
  </Card>

  <Card title="YAML Reference" icon="code" href="../api-reference/yaml-reference">
    Look up config keys and pipeline options
  </Card>
</CardGroup>
