API Reference
Exporting risks
Risks are exported using the risks_paged query. Each risk includes its assessment scores (likelihood, impact, residual scores), treatment decision, linked controls, and linked assets.
Basic query
query ExportRisks($first: Int, $after: String, $filter: RiskFilter) {
risks_paged(first: $first, after: $after, filter: $filter) {
edges {
node {
id
sequenceId
customId
name
description
notBefore
expires
archived
likelihood
impact
residualLikelihood
residualImpact
treatment
treatmentNotes
appetite
controls {
id
sequenceId
name
}
assets {
id
name
}
assignments {
assignmentType
user {
name
email
}
}
attributes {
key
value
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
Variables to fetch the first page:
{ "first": 50, "after": null, "filter": {} }
Key fields
sequenceId— the human-readable number shown in the UI (e.g. R-42)customId— optional custom identifier set by your teamlikelihood/impact— inherent risk scores (1–5 scale)residualLikelihood/residualImpact— residual risk scores after controlstreatment— one ofACCEPT,AVOID,TRANSFER,REDUCEappetite— the organization's risk appetite score for this risk
Complete export script (Python)
import requests
import json
import csv
import sys
GRAPHQL_URL = "https://portal.tidalcontrol.com/graphql"
QUERY = """
query ExportRisks($first: Int, $after: String, $filter: RiskFilter) {
risks_paged(first: $first, after: $after, filter: $filter) {
edges {
node {
id
sequenceId
customId
name
description
notBefore
expires
archived
likelihood
impact
residualLikelihood
residualImpact
treatment
treatmentNotes
appetite
controls { id name }
assets { id name }
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_risks(access_token, include_archived=False):
risks = []
cursor = None
risk_filter = {"archived": include_archived} if include_archived else {}
while True:
data = graphql(
QUERY,
{"first": 50, "after": cursor, "filter": risk_filter},
access_token,
)
page = data["risks_paged"]
risks.extend(edge["node"] for edge in page["edges"])
if not page["pageInfo"]["hasNextPage"]:
break
cursor = page["pageInfo"]["endCursor"]
return risks
def to_csv(risks, output_path):
if not risks:
print("No risks 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",
"Likelihood", "Impact", "Residual Likelihood", "Residual Impact",
"Treatment", "Treatment Notes", "Appetite",
"Controls", "Assets", "Owners",
])
for r in risks:
owners = [
a["user"]["email"]
for a in r["assignments"]
if a["assignmentType"] == "OWNER"
]
writer.writerow([
r["id"],
r["sequenceId"],
r.get("customId", ""),
r["name"],
r.get("description", ""),
r.get("notBefore", ""),
r.get("expires", ""),
r["archived"],
r.get("likelihood", ""),
r.get("impact", ""),
r.get("residualLikelihood", ""),
r.get("residualImpact", ""),
r.get("treatment", ""),
r.get("treatmentNotes", ""),
r.get("appetite", ""),
", ".join(c["name"] for c in r["controls"]),
", ".join(a["name"] for a in r["assets"]),
", ".join(owners),
])
print(f"Exported {len(risks)} risks to {output_path}")
if __name__ == "__main__":
# Paste your access token here, or load it from the authentication script
ACCESS_TOKEN = "eyJhbGci..."
risks = export_risks(ACCESS_TOKEN)
to_csv(risks, "risks_export.csv")
Filtering examples
Only active (non-archived) risks:
{ "filter": { "archived": false } }
Risks with treatment REDUCE:
# The RiskFilter does not filter by treatment directly.
# Fetch all and filter in Python:
risks_with_reduce = [r for r in risks if r.get("treatment") == "REDUCE"]
Risks assigned to a specific user:
{ "filter": { "assignees": ["user-uuid-here"] } }
Risks linked to a specific framework:
{ "filter": { "frameworks": ["framework-uuid-here"] } }
Info
To find user UUIDs or framework UUIDs, use the users_paged or frameworks_paged queries respectively.
Count only
To check how many risks exist before exporting:
query {
risks_count(filter: { archived: false })
}
- Previous
- Querying data