# Exporting assets

> Export assets from the Tidal Control API with the assets_paged query, including CIA scores, linked controls, risks and information types, with a Python script.

Source: https://tidalcontrol.com/docs/api/assets

Assets are exported using the `assets_paged` query. Each asset includes its CIA scores (confidentiality, integrity, availability), linked controls, risks, and information types.

## Basic query

```graphql
query ExportAssets($first: Int, $after: String, $filter: AssetFilter) {
  assets_paged(first: $first, after: $after, filter: $filter) {
    edges {
      node {
        id
        sequenceId
        customId
        name
        description
        notBefore
        expires
        archived
        confidentiality
        integrity
        availability
        mao
        rto
        rpo
        informationTypes
        controls {
          id
          sequenceId
          name
        }
        risks {
          id
          sequenceId
          name
          treatment
        }
        assignments {
          assignmentType
          user {
            name
            email
          }
        }
        attributes {
          key
          value
        }
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}
```

Variables to fetch the first page:

```json
{ "first": 50, "after": null, "filter": {} }
```

## Key fields

- `sequenceId`: the human-readable number shown in the UI (e.g. A-7)
- `customId`: optional custom identifier set by your team
- `confidentiality` / `integrity` / `availability`: CIA impact scores (1-3 scale)
- `mao`: Maximum Acceptable Outage (ISO 8601 Duration, e.g. `PT4H`)
- `rto`: Recovery Time Objective (ISO 8601 Duration)
- `rpo`: Recovery Point Objective (ISO 8601 Duration)
- `informationTypes`: types of information processed by this asset (e.g. `CUSTOMER_INFORMATION`, `FINANCIAL_RECORDS`)

## Complete export script (Python)

This script authenticates, pages through every asset record, and writes the result to a CSV file:

```python

GRAPHQL_URL = "https://portal.tidalcontrol.com/graphql"

QUERY = """
query ExportAssets($first: Int, $after: String, $filter: AssetFilter) {
  assets_paged(first: $first, after: $after, filter: $filter) {
    edges {
      node {
        id
        sequenceId
        customId
        name
        description
        notBefore
        expires
        archived
        confidentiality
        integrity
        availability
        mao
        rto
        rpo
        informationTypes
        controls { id sequenceId name }
        risks { id sequenceId name treatment }
        assignments { assignmentType user { name email } }
      }
    }
    pageInfo { hasNextPage endCursor }
  }
}
"""

def graphql(query, variables, access_token):
    r = requests.post(
        GRAPHQL_URL,
        json={"query": query, "variables": variables},
        headers={"Authorization": f"Bearer {access_token}"},
    )
    r.raise_for_status()
    result = r.json()
    if "errors" in result:
        raise RuntimeError(result["errors"])
    return result["data"]

def export_assets(access_token, include_archived=False):
    assets = []
    cursor = None
    asset_filter = {"archived": include_archived} if include_archived else {}

    while True:
        data = graphql(
            QUERY,
            {"first": 50, "after": cursor, "filter": asset_filter},
            access_token,
        )
        page = data["assets_paged"]
        assets.extend(edge["node"] for edge in page["edges"])

        if not page["pageInfo"]["hasNextPage"]:
            break
        cursor = page["pageInfo"]["endCursor"]

    return assets

def to_csv(assets, output_path):
    if not assets:
        print("No assets found.")
        return

    with open(output_path, "w", newline="", encoding="utf-8") as f:
        writer = csv.writer(f)
        writer.writerow([
            "ID", "Sequence ID", "Custom ID", "Name", "Description",
            "Not Before", "Expires", "Archived",
            "Confidentiality", "Integrity", "Availability",
            "MAO", "RTO", "RPO",
            "Information Types", "Linked Controls", "Linked Risks", "Owners",
        ])
        for a in assets:
            owners = [
                asgn["user"]["email"]
                for asgn in a["assignments"]
                if asgn["assignmentType"] == "OWNER"
            ]
            writer.writerow([
                a["id"],
                a["sequenceId"],
                a.get("customId", ""),
                a["name"],
                a.get("description", ""),
                a.get("notBefore", ""),
                a.get("expires", ""),
                a["archived"],
                a.get("confidentiality", ""),
                a.get("integrity", ""),
                a.get("availability", ""),
                a.get("mao", ""),
                a.get("rto", ""),
                a.get("rpo", ""),
                ", ".join(a.get("informationTypes") or []),
                ", ".join(c["name"] for c in a["controls"]),
                ", ".join(r["name"] for r in a["risks"]),
                ", ".join(owners),
            ])

    print(f"Exported {len(assets)} assets to {output_path}")

if __name__ == "__main__":
    ACCESS_TOKEN = "eyJhbGci..."

    assets = export_assets(ACCESS_TOKEN)
    to_csv(assets, "assets_export.csv")
```

## Filtering examples

Pass a `filter` argument to narrow the export. These are the combinations asked for most often:

**Assets with high impact:**

```json
{ "filter": { "impact": ["HIGH"] } }
```

**Assets linked to a specific control:**

```json
{ "filter": { "controls": ["control-uuid-here"] } }
```

**Assets assigned to a specific user:**

```json
{ "filter": { "assignees": ["user-uuid-here"] } }
```

**Assets linked to a specific framework:**

```json
{ "filter": { "frameworks": ["framework-uuid-here"] } }
```

## Count only

To check how many assets exist before exporting the full set:

```graphql
query {
  assets_count(filter: { archived: false })
}
```
