API Reference
Adding evidence
Most of this guide covers reading data, but the API can also write data through mutations. This page walks through a complete example: a custom integration that attaches an evidence file to a control by creating an execution task and adding the file to it.
Mutations require a token with write permissions. Authenticate with the Device Authorization Grant so the token acts as a user with the Regular User or Super User role. Read-only machine-to-machine credentials cannot run mutations.
How it works
Evidence in Tidal Control is a document linked to an activity. Attaching evidence to a control is four steps:
- Find the control you want to add evidence to
- Upload the evidence file as a document
- Create an execution task linked to the control
- Attach the document to the task as evidence
Step 1 — Find the control
Search for the control by name to get its id:
query FindControl($filter: ControlFilter) {
controls_paged(first: 1, filter: $filter) {
edges {
node {
id
sequenceId
name
}
}
}
}
Variables:
{ "filter": { "search": "Access control policy" } }
Step 2 — Upload the evidence file
Documents are uploaded through a REST endpoint (not GraphQL), as multipart/form-data on the files field. Use the same Bearer token:
curl -s -X POST https://portal.tidalcontrol.com/documents \
-H "Authorization: Bearer {access_token}" \
-F "files=@evidence.pdf"
The response is an array of the documents you uploaded:
[
{
"id": "11111111-1111-1111-1111-111111111111",
"name": "evidence.pdf",
"type": "application/pdf"
}
]
Keep the document id for the next steps.
The maximum upload size is 128 MB per request. You can upload several files in one request by repeating the files field.
Step 3 — Create an execution task on the control
Create an execution task and link it to the control from step 1 using the controls field:
mutation CreateExecution($input: ExecutionInput!) {
addExecution(input: $input) {
id
sequenceId
name
}
}
Variables:
{
"input": {
"name": "Quarterly access review",
"controls": ["22222222-2222-2222-2222-222222222222"]
}
}
Only name is required. controls takes one or more control ids, and you can also set description, expires, owners, and assets. The mutation returns the new task's id.
Step 4 — Attach the evidence
Attach the uploaded document to the task with addEvidenceToExecution:
mutation AddEvidence($activityId: ID!, $documentIds: [ID!]!) {
addEvidenceToExecution(activityId: $activityId, documentIds: $documentIds) {
id
sequenceId
evidence {
id
document {
name
}
}
}
}
Variables:
{
"activityId": "33333333-3333-3333-3333-333333333333",
"documentIds": ["11111111-1111-1111-1111-111111111111"]
}
The evidence now appears on the task, which is linked to your control.
Complete Python example
This script ties the four steps together. It reuses the get_token() and graphql() helpers from Authentication.
import requests
GRAPHQL_URL = "https://portal.tidalcontrol.com/graphql"
DOCUMENTS_URL = "https://portal.tidalcontrol.com/documents"
def find_control(search, access_token):
query = """
query FindControl($filter: ControlFilter) {
controls_paged(first: 1, filter: $filter) {
edges { node { id sequenceId name } }
}
}
"""
data = graphql(query, {"filter": {"search": search}}, access_token)
edges = data["controls_paged"]["edges"]
if not edges:
raise RuntimeError(f"No control found for '{search}'")
return edges[0]["node"]
def upload_document(file_path, access_token):
with open(file_path, "rb") as f:
r = requests.post(
DOCUMENTS_URL,
headers={"Authorization": f"Bearer {access_token}"},
files={"files": f},
)
r.raise_for_status()
return r.json()[0] # first uploaded document
def create_execution(name, control_id, access_token):
mutation = """
mutation CreateExecution($input: ExecutionInput!) {
addExecution(input: $input) { id sequenceId name }
}
"""
data = graphql(
mutation,
{"input": {"name": name, "controls": [control_id]}},
access_token,
)
return data["addExecution"]
def add_evidence(activity_id, document_id, access_token):
mutation = """
mutation AddEvidence($activityId: ID!, $documentIds: [ID!]!) {
addEvidenceToExecution(activityId: $activityId, documentIds: $documentIds) {
id
sequenceId
evidence { id document { name } }
}
}
"""
data = graphql(
mutation,
{"activityId": activity_id, "documentIds": [document_id]},
access_token,
)
return data["addEvidenceToExecution"]
if __name__ == "__main__":
token = get_token()
access_token = token["access_token"]
control = find_control("Access control policy", access_token)
document = upload_document("evidence.pdf", access_token)
task = create_execution("Quarterly access review", control["id"], access_token)
result = add_evidence(task["id"], document["id"], access_token)
print(f"Added '{document['name']}' to task #{result['sequenceId']} on control {control['name']}.")
Attaching evidence to an existing task
If a suitable execution task already exists, skip step 3. Find the task with the activities_paged query (see Exporting tasks), then call addEvidenceToExecution with its id.
- Previous
- Schema introspection