API Reference
Schema introspection
GraphQL has built-in introspection: you can query the schema itself to discover what queries, types, and fields are available. This is useful when building scripts or exploring what data you can access.
All introspection queries use the same endpoint and authentication as regular queries.
List all available queries
{
__schema {
queryType {
fields {
name
description
}
}
}
}
This returns the full list of root query fields — for example controls_paged, activities_paged, risks_paged, and so on.
Inspect a specific type
Use __type to see the fields available on any type:
{
__type(name: "Activity") {
fields {
name
description
type {
name
kind
ofType {
name
kind
}
}
}
}
}
Replace "Activity" with any type name — for example "Control", "Risk", "Issue", or "Assessment".
List enum values
To see the valid values for an enum type:
{
__type(name: "ActivityDueStatus") {
enumValues {
name
}
}
}
Common enums worth inspecting: ActivityDueStatus, Effectiveness, ControlCategory, Priority, IssueType.
Complete Python example
This script authenticates and prints a summary of the schema: all query fields and the fields on a given type.
import requests
import json
import time
TENANT = "{your-tenant}"
BASE_URL = f"https://auth.tidalcontrol.com/realms/{TENANT}/protocol/openid-connect"
CLIENT_ID = "portal"
GRAPHQL_URL = "https://portal.tidalcontrol.com/graphql"
def get_token():
r = requests.post(
f"{BASE_URL}/auth/device",
data={"client_id": CLIENT_ID, "scope": "openid"},
)
r.raise_for_status()
device = r.json()
print(f"\nOpen this URL in your browser to log in:")
print(f" {device['verification_uri_complete']}\n")
interval = device.get("interval", 5)
deadline = time.time() + device["expires_in"]
while time.time() < deadline:
time.sleep(interval)
r = requests.post(
f"{BASE_URL}/token",
data={
"grant_type": "urn:ietf:params:oauth:grant-type:device_code",
"client_id": CLIENT_ID,
"device_code": device["device_code"],
},
)
if r.status_code == 200:
print("Authentication successful.")
return r.json()
body = r.json()
if body.get("error") == "authorization_pending":
continue
if body.get("error") == "slow_down":
interval += 5
continue
r.raise_for_status()
raise RuntimeError("Authentication timed out.")
def graphql(query, access_token):
r = requests.post(
GRAPHQL_URL,
json={"query": query, "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 list_queries(access_token):
data = graphql(
"{ __schema { queryType { fields { name description } } } }",
access_token,
)
fields = data["__schema"]["queryType"]["fields"]
print(f"\n{len(fields)} query fields available:\n")
for f in sorted(fields, key=lambda x: x["name"]):
desc = f.get("description") or ""
print(f" {f['name']:<40} {desc}")
def describe_type(type_name, access_token):
data = graphql(
f'{{ __type(name: "{type_name}") {{ fields {{ name description '
f'type {{ name kind ofType {{ name }} }} }} }} }}',
access_token,
)
t = data.get("__type")
if not t:
print(f"Type '{type_name}' not found.")
return
print(f"\nFields on {type_name}:\n")
for f in t["fields"]:
type_info = f["type"]
type_name_str = type_info.get("name") or (type_info.get("ofType") or {}).get("name", "")
print(f" {f['name']:<30} {type_name_str}")
if __name__ == "__main__":
token = get_token()
access_token = token["access_token"]
list_queries(access_token)
describe_type("Activity", access_token)
describe_type("Control", access_token)
- Previous
- Reporting use cases