cURL
curl --request GET \
--url https://api.ctrl-hub.com/v3/operations \
--header 'X-Session-Token: <api-key>'import requests
url = "https://api.ctrl-hub.com/v3/operations"
headers = {"X-Session-Token": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'X-Session-Token': '<api-key>'}};
fetch('https://api.ctrl-hub.com/v3/operations', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.ctrl-hub.com/v3/operations",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"X-Session-Token: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.ctrl-hub.com/v3/operations"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-Session-Token", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.ctrl-hub.com/v3/operations")
.header("X-Session-Token", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.ctrl-hub.com/v3/operations")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-Session-Token"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"data": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "operations",
"attributes": {
"name": "<string>",
"status": "done",
"code": "<string>",
"description": "<string>",
"requires_meter_details": false,
"dates": {
"scheduled": {
"start": "2023-11-07T05:31:56Z",
"end": "2023-11-07T05:31:56Z"
}
},
"labels": [
{
"key": "Your label key",
"value": "Your label value"
}
],
"requirements": {
"forms": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"required": true
}
]
},
"done_reason": "aborted"
},
"meta": {
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"counts": {
"streets": 0,
"properties": 0,
"locations": 0
},
"related_data": {
"properties": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"postcode": "<string>"
}
]
}
},
"relationships": {
"organisation": {
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "organisations"
}
},
"scheme": {
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "schemes"
}
},
"work_order": {
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "work-orders"
}
},
"appointment": {
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "appointments"
}
},
"permits": {
"data": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "permits"
}
]
},
"template": {
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "operation-templates"
}
},
"type": {
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "operation-types"
}
},
"teams": {
"data": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "teams"
}
]
},
"assignees": {
"data": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "users"
}
]
},
"properties": {
"data": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "properties"
}
]
},
"locations": {
"data": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "locations"
}
]
},
"streets": {
"data": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "streets"
}
]
}
}
}
],
"meta": {
"pagination": {
"counts": {
"pages": 1,
"resources": 1
},
"current_page": 1,
"offsets": {
"next": null,
"previous": null
},
"requested": {
"limit": 10,
"offset": 0
}
},
"features": {
"include": {
"options": [
"related.resource"
]
}
}
},
"jsonapi": {
"version": "1.0"
},
"included": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "forms",
"attributes": {
"name": "<string>",
"description": "<string>",
"status": "draft"
},
"meta": {
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"modified_at": "2023-11-07T05:31:56Z",
"counts": {
"schemas": 0,
"categories": 0,
"submissions": 0,
"submission_versions": 0
}
},
"relationships": {
"organisation": {
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "organisations"
}
},
"categories": {
"data": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "form-categories"
}
]
},
"schemas": {
"data": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "form-schemas"
}
]
},
"author": {
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "users"
}
},
"workflow": {
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "workflow-definitions"
}
},
"latest_schema": {
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "form-schemas"
}
},
"submissions": {
"data": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "form-submissions"
}
]
}
}
}
]
}{
"id": "98ca4a78-b66f-4234-9719-aaf832ee6669",
"status": "400",
"title": "A validation error was encountered",
"source": {
"parameter": "include"
},
"meta": {
"resource": "wrong_value"
}
}{
"id": "05fc9c8d-73b9-4697-9337-57f7a567a48f",
"status": "401",
"title": "You are not authorised to access this resource",
"detail": "In order to access this resource, you need the 'admin' role.",
"code": "AUTH.001"
}{
"id": "fe9d9a69-f0a7-4fdc-bb2c-176027f316c5",
"status": "500",
"title": "Internal Server Error",
"detail": "An unexpected error occurred on the server."
}Operations
List operations
List all of the operations in an organisation.
GET
/
v3
/
operations
cURL
curl --request GET \
--url https://api.ctrl-hub.com/v3/operations \
--header 'X-Session-Token: <api-key>'import requests
url = "https://api.ctrl-hub.com/v3/operations"
headers = {"X-Session-Token": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'X-Session-Token': '<api-key>'}};
fetch('https://api.ctrl-hub.com/v3/operations', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.ctrl-hub.com/v3/operations",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"X-Session-Token: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.ctrl-hub.com/v3/operations"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-Session-Token", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.ctrl-hub.com/v3/operations")
.header("X-Session-Token", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.ctrl-hub.com/v3/operations")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-Session-Token"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"data": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "operations",
"attributes": {
"name": "<string>",
"status": "done",
"code": "<string>",
"description": "<string>",
"requires_meter_details": false,
"dates": {
"scheduled": {
"start": "2023-11-07T05:31:56Z",
"end": "2023-11-07T05:31:56Z"
}
},
"labels": [
{
"key": "Your label key",
"value": "Your label value"
}
],
"requirements": {
"forms": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"required": true
}
]
},
"done_reason": "aborted"
},
"meta": {
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"counts": {
"streets": 0,
"properties": 0,
"locations": 0
},
"related_data": {
"properties": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"postcode": "<string>"
}
]
}
},
"relationships": {
"organisation": {
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "organisations"
}
},
"scheme": {
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "schemes"
}
},
"work_order": {
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "work-orders"
}
},
"appointment": {
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "appointments"
}
},
"permits": {
"data": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "permits"
}
]
},
"template": {
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "operation-templates"
}
},
"type": {
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "operation-types"
}
},
"teams": {
"data": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "teams"
}
]
},
"assignees": {
"data": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "users"
}
]
},
"properties": {
"data": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "properties"
}
]
},
"locations": {
"data": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "locations"
}
]
},
"streets": {
"data": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "streets"
}
]
}
}
}
],
"meta": {
"pagination": {
"counts": {
"pages": 1,
"resources": 1
},
"current_page": 1,
"offsets": {
"next": null,
"previous": null
},
"requested": {
"limit": 10,
"offset": 0
}
},
"features": {
"include": {
"options": [
"related.resource"
]
}
}
},
"jsonapi": {
"version": "1.0"
},
"included": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "forms",
"attributes": {
"name": "<string>",
"description": "<string>",
"status": "draft"
},
"meta": {
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"modified_at": "2023-11-07T05:31:56Z",
"counts": {
"schemas": 0,
"categories": 0,
"submissions": 0,
"submission_versions": 0
}
},
"relationships": {
"organisation": {
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "organisations"
}
},
"categories": {
"data": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "form-categories"
}
]
},
"schemas": {
"data": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "form-schemas"
}
]
},
"author": {
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "users"
}
},
"workflow": {
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "workflow-definitions"
}
},
"latest_schema": {
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "form-schemas"
}
},
"submissions": {
"data": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "form-submissions"
}
]
}
}
}
]
}{
"id": "98ca4a78-b66f-4234-9719-aaf832ee6669",
"status": "400",
"title": "A validation error was encountered",
"source": {
"parameter": "include"
},
"meta": {
"resource": "wrong_value"
}
}{
"id": "05fc9c8d-73b9-4697-9337-57f7a567a48f",
"status": "401",
"title": "You are not authorised to access this resource",
"detail": "In order to access this resource, you need the 'admin' role.",
"code": "AUTH.001"
}{
"id": "fe9d9a69-f0a7-4fdc-bb2c-176027f316c5",
"status": "500",
"title": "Internal Server Error",
"detail": "An unexpected error occurred on the server."
}Authorizations
SessionOAuth2Cookie
Session token for authentication.
Query Parameters
A comma separated list of related resources to include.
Available options:
appointment, assignees, forms, locations, operation_type, organisation, permits, properties, scheme, streets, teams, template, work_order Filters the response data based on the value provided.
Available filters:
id_in: Filter by ID(s)in_schemes: Filter for operations that are part of a scheme(s) by their ID(s)in_work_orders: Filter for operations that are part of a work order(s) by their ID(s)assigned_to: Filter for operations assigned to a user(s) by their ID(s)at_properties: Filter for operations at a property(s) by their ID(s)unscheduled: Filter for unscheduled operationsno_start: Filter operations with no start timeno_end: Filter operations with no end timehas_appointment: Filter operations that have appointmentsno_appointment: Filter operations that do not have appointmentshas_property: Filter operations that have any related property - this does not accept arguments, it just checks for the existence of a related property. You can useat_propertiesto find operations at a specific property.
Project-role filters (cross-resource lookups against project role assignments):
has_role(<project_role_id>): Filter for operations that have at least one active assignment for the given project role. Example:has_role(11111111-1111-1111-1111-111111111111).held_by(<user_id>): Filter for operations where the given user holds at least one role (any role). Example:held_by(22222222-2222-2222-2222-222222222222).has_role_assignment(<project_role_id>,<user_id>): Filter for operations where the given user holds the given role specifically. Example:has_role_assignment(11111111-1111-1111-1111-111111111111,22222222-2222-2222-2222-222222222222).
Field filters (RQL expressions on Operation fields):
id: Filter by operation ID. Example:eq(id,11111111-1111-1111-1111-111111111111), or multiple within(id,[<uuid>,<uuid>]).organisation: Filter by owning organisation ID. Example:eq(organisation,<uuid>).scheme: Filter by scheme ID. Example:eq(scheme,<uuid>), or multiple within(scheme,[<uuid>,<uuid>]).work-order: Filter by work order ID. Example:eq(work-order,<uuid>), or multiple within(work-order,[<uuid>,<uuid>]).type: Filter by operation type ID. Example:eq(type,<uuid>).name: Filter by name. Example:eq(name,Excavation), or partial match withcontains(name,exc).assignees: Filter by assigned user ID. Matches operations that include the given user. Example:eq(assignees,<uuid>), or multiple within(assignees,[<uuid>,<uuid>]).properties: Filter by related property ID. Matches operations that include the given property. Example:eq(properties,<uuid>), or multiple within(properties,[<uuid>,<uuid>]).appointment: Filter by appointment ID. Example:eq(appointment,<uuid>).status: Filter by status. Acceptstodo,in_progress,done. Example:in(status,[todo,in_progress]).done_reason: Filter by reason an operation is done. Acceptscompleted,cancelled,aborted. Only meaningful for operations whosestatusisdone. Example:and(eq(status,done),in(done_reason,[completed,cancelled])).dates.scheduled.start: Filter by scheduled start date. Accepts an RFC 3339 timestamp and supports the range operatorsge,le,gt,lt(andeq). Example:ge(dates.scheduled.start,2026-07-01T00:00:00Z).dates.scheduled.end: Filter by scheduled end date. Accepts an RFC 3339 timestamp and supports the range operatorsge,le,gt,lt(andeq). Example:le(dates.scheduled.end,2026-07-14T23:59:59Z).updated_at: Filter by the date an operation was last modified. Accepts an RFC 3339 timestamp and supports the range operatorsge,le,gt,lt(andeq). Example (modified since a given time):ge(updated_at,2026-07-01T00:00:00Z). Example (modified within a window):and(ge(updated_at,2026-07-01T00:00:00Z),le(updated_at,2026-07-14T23:59:59Z)).
For more information on using named filters, see the docs
Limit the number of resources returned by the API
Required range:
x >= 1Offset the resources returned by the API
Required range:
x >= 0A comma separated list of fields to sort by.
Available options:
code, -code, created_at, -created_at, name, -name, postcode, -postcode, updated_at, -updated_at Response
List of operations.
JSON API response object
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Related resources that can be included when an operation is returned
A form
- Option 1
- Option 2
- Option 3
- Option 4
- Option 5
- Option 6
- Option 7
- Option 8
- Option 9
- Option 10
- Option 11
- Option 12
Show child attributes
Show child attributes