Resource graph
Overview
The resource graph records which resource is the parent of which — a virtual machine on a hypervisor, a hypervisor in a cluster, a database used by an application. Relations are scoped by environment: an alert's environment and resource identify a resource, and an empty environment is allowed. Environments are compared exactly, so "" and "Production" are different resources even when the name matches.
A resource may have several parents (a database shared by two applications), and one parent may have any number of children. The graph is shared by alert events, policies, service topologies, and CMDB imports. Each relation remembers who declared it:
source | Declared by |
|---|---|
manual | This API (POST /api/resources/relations, bulk) |
event | An alert payload with resourceParent / resourceChildren — see Sending alerts |
cmdb | A CMDB import: flat records with a parent column, loaded through the API or the web UI. sourceRef is the name given to the source (default cmdb) |
topology | A saved service topology: every resource → resource edge of the graph you draw. sourceRef is the topology ID |
policy | The Resource relation policy modifier with source Value — see Modifiers and tools — Resource relation. sourceRef is the policy ID |
When the same relation (environment, resource, parent) arrives from several sources, the highest-ranked source wins and is kept on the relation: manual > event > cmdb > topology > policy. A relation you add manually is never overwritten by an alert payload, a topology, or a policy. Relations come from alerts, from service topologies, from policies, from CMDB imports, and from this API.
Policies also read the graph: the Resource relation modifier can copy a resource's main parent into the alert (resourceParent), and Mark as effect can take the cause from an open alert on a parent resource — see Cause and effect alerts — Cause on a parent resource.
The alert detail (GET /api/alert/<alert_id>) already includes the parents, children, and ancestors of the alert's resource — see Querying alerts — Resource relation fields. The alert details drawer shows the same information.
Authentication and permissions
All endpoints take an API key:
Authorization: Key YOUR_API_KEY_HERE
| Action | Scope |
|---|---|
| Read the graph or list relations | read:service_topologies |
| Create, bulk-load, or delete relations | write:service_topologies |
| Import CMDB records | write:service_topologies (relations get source cmdb without the admin scope) |
| Download the CMDB import template | read:service_topologies |
Write a relation with a source other than manual | admin:service_topologies (or admin) in addition to write |
See API keys for scopes.
Relation object
Every relation returned by the API has the same shape:
{
"id": "6f1c0b2e-4a7d-4f2e-9d3a-1b2c3d4e5f60",
"environment": "Production",
"resource": "vm-7",
"parent": "esx-01",
"source": "event",
"sourceRef": null,
"createTime": "2026-10-09T08:15:00.000Z",
"updateTime": "2026-10-09T08:15:00.000Z"
}
| Field | Meaning |
|---|---|
id | Relation ID, used by DELETE /api/resources/relations/<id> |
environment | Environment of both resources ("" when none) |
resource | The child |
parent | The parent |
source | Who declared the relation (see table above) |
sourceRef | Optional free text identifying the origin (an import batch, a ticket); set automatically to the topology ID for topology relations and to the policy ID for policy relations |
createTime / updateTime | When the relation was first stored and last confirmed or changed |
Resource and parent names are trimmed. A relation is unique per (environment, resource, parent): sending it again updates the existing row instead of creating a second one.
Get the graph of a resource
GET /api/resources/graph?resource=<name>&environment=<env>
Authorization: Key YOUR_API_KEY
Returns the direct parents and children of a resource and, depending on direction, its ancestors (parents of parents) and descendants.
Query parameters
| Parameter | Required | Default | Description |
|---|---|---|---|
resource | Yes | — | Resource name |
environment | No | "" | Environment of the resource. Omit it for resources with no environment |
direction | No | both | ancestors, descendants, or both |
depth | No | 10 | How many levels to walk, 1–20 |
Example
Read the ancestors of vm-7 in Production
- Bash
- Python
curl -X GET "http://your-tobealert.com:9000/api/resources/graph?resource=vm-7&environment=Production&direction=ancestors" \
-H "Content-Type: application/json" \
-H "Authorization: Key YOUR_API_KEY"
import requests
response = requests.get(
"http://your-tobealert.com:9000/api/resources/graph",
headers={
"Content-Type": "application/json",
"Authorization": "Key YOUR_API_KEY"
},
params={
"resource": "vm-7",
"environment": "Production",
"direction": "ancestors"
}
)
if response.status_code in [200, 201]:
print("Request successful")
print(response.json())
else:
print(f"Error: {response.status_code}")
print(response.text)
Response
{
"status": "ok",
"environment": "Production",
"resource": "vm-7",
"parents": [
{
"id": "6f1c0b2e-4a7d-4f2e-9d3a-1b2c3d4e5f60",
"environment": "Production",
"resource": "vm-7",
"parent": "esx-01",
"source": "event",
"sourceRef": null,
"createTime": "2026-10-09T08:15:00.000Z",
"updateTime": "2026-10-09T08:15:00.000Z"
}
],
"children": [],
"ancestors": [
{ "resource": "esx-01", "depth": 1, "path": ["vm-7", "esx-01"] },
{ "resource": "cluster-a", "depth": 2, "path": ["vm-7", "esx-01", "cluster-a"] }
]
}
| Field | Meaning |
|---|---|
parents / children | Direct relations, as relation objects. Always present |
ancestors | Resources above this one, with depth (1 = direct parent) and the path walked from the resource. Present with direction=ancestors or both |
descendants | Resources below this one, same shape. Present with direction=descendants or both |
Traversals are cycle-safe: a loop in the graph (for example a → b → a, which the storage tolerates) is walked once and stops; the walk also stops at depth.
Errors
| Code | Reason |
|---|---|
400 | resource missing, invalid direction, or depth outside 1–20 |
List relations
GET /api/resources/relations
Authorization: Key YOUR_API_KEY
Returns a paginated list of relations with optional filters.
Query parameters
| Parameter | Description | Example |
|---|---|---|
environment | Filter by environment | ?environment=Production |
resource | Filter by child | ?resource=vm-7 |
parent | Filter by parent | ?parent=esx-01 |
source | Filter by source | ?source=manual |
sourceRef | Filter by source reference | ?sourceRef=cmdb-2026-10 |
sort-by | Sort column; prefix with - for descending. Columns: environment, resource, parent, source, sourceRef, createTime, updateTime | ?sort-by=-updateTime |
page / page-size | Pagination | ?page=2&page-size=100 |
Example
List everything that hangs from esx-01 in Production
- Bash
- Python
curl -X GET "http://your-tobealert.com:9000/api/resources/relations?parent=esx-01&environment=Production&sort-by=-updateTime" \
-H "Content-Type: application/json" \
-H "Authorization: Key YOUR_API_KEY"
import requests
response = requests.get(
"http://your-tobealert.com:9000/api/resources/relations",
headers={
"Content-Type": "application/json",
"Authorization": "Key YOUR_API_KEY"
},
params={
"parent": "esx-01",
"environment": "Production",
"sort-by": "-updateTime"
}
)
if response.status_code in [200, 201]:
print("Request successful")
print(response.json())
else:
print(f"Error: {response.status_code}")
print(response.text)
Response
{
"status": "ok",
"relations": [ /* array of relation objects */ ],
"total": 2,
"page": 1,
"pageSize": 50,
"pages": 1,
"more": false
}
Create a relation
POST /api/resources/relations
Content-Type: application/json
Authorization: Key YOUR_API_KEY
Adds one relation. Requires write:service_topologies. The call is idempotent: repeating it for the same (environment, resource, parent) updates the existing relation (its source, sourceRef, and updateTime) instead of creating a duplicate.
Request body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
resource | String | Yes | — | The child |
parent | String | Yes | — | The parent. Must differ from resource |
environment | String | No | "" | Environment of both resources |
source | String | No | manual | One of manual, event, cmdb, topology, policy. Anything other than manual requires admin:service_topologies |
sourceRef | String | No | null | Free text identifying the origin |
Example
Declare orders-app as a parent of orders-db (source manual)
- Bash
- Python
curl -X POST "http://your-tobealert.com:9000/api/resources/relations" \
-H "Content-Type: application/json" \
-H "Authorization: Key YOUR_API_KEY" \
-d '{
"environment": "Production",
"resource": "orders-db",
"parent": "orders-app",
"sourceRef": "CHG-1042"
}'
import requests
response = requests.post(
"http://your-tobealert.com:9000/api/resources/relations",
headers={
"Content-Type": "application/json",
"Authorization": "Key YOUR_API_KEY"
},
json={
"environment": "Production",
"resource": "orders-db",
"parent": "orders-app",
"sourceRef": "CHG-1042"
}
)
if response.status_code in [200, 201]:
print("Request successful")
print(response.json())
else:
print(f"Error: {response.status_code}")
print(response.text)
Response
201 Created:
{
"status": "ok",
"relation": {
"id": "0c9f2a61-7b3e-4d1c-a8e5-2f6b7c8d9e01",
"environment": "Production",
"resource": "orders-db",
"parent": "orders-app",
"source": "manual",
"sourceRef": "CHG-1042",
"createTime": "2026-10-09T09:00:00.000Z",
"updateTime": "2026-10-09T09:00:00.000Z"
}
}
Errors
| Code | Reason |
|---|---|
400 | resource or parent missing, resource equal to parent (a resource cannot be its own parent), or unknown source |
403 | source other than manual without admin:service_topologies |
Bulk-load relations
POST /api/resources/relations/bulk
Content-Type: application/json
Authorization: Key YOUR_API_KEY
Stores up to 10,000 relations in one request (for example an export from a CMDB). Requires write:service_topologies.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
relations | Array | Yes | Non-empty list of {resource, parent, environment?}. An item without environment uses the top-level one |
environment | String | No | Default environment for items that do not set their own ("" if omitted) |
source | String | No | Source for the whole batch, default manual. Other values require admin:service_topologies |
sourceRef | String | No | Reference stored on every relation of the batch |
The whole batch is validated before anything is written: one invalid item (missing resource or parent, or resource equal to parent) rejects the request with 400 and the item index.
Example
Load a small hierarchy; orders-db gets two parents
- Bash
- Python
curl -X POST "http://your-tobealert.com:9000/api/resources/relations/bulk" \
-H "Content-Type: application/json" \
-H "Authorization: Key YOUR_API_KEY" \
-d '{
"environment": "Production",
"sourceRef": "cmdb-2026-10",
"relations": [
{
"resource": "esx-01",
"parent": "cluster-a"
},
{
"resource": "vm-7",
"parent": "esx-01"
},
{
"resource": "vm-8",
"parent": "esx-01"
},
{
"resource": "orders-db",
"parent": "orders-app"
},
{
"resource": "orders-db",
"parent": "billing-app"
}
]
}'
import requests
response = requests.post(
"http://your-tobealert.com:9000/api/resources/relations/bulk",
headers={
"Content-Type": "application/json",
"Authorization": "Key YOUR_API_KEY"
},
json={
"environment": "Production",
"sourceRef": "cmdb-2026-10",
"relations": [
{
"resource": "esx-01",
"parent": "cluster-a"
},
{
"resource": "vm-7",
"parent": "esx-01"
},
{
"resource": "vm-8",
"parent": "esx-01"
},
{
"resource": "orders-db",
"parent": "orders-app"
},
{
"resource": "orders-db",
"parent": "billing-app"
}
]
}
)
if response.status_code in [200, 201]:
print("Request successful")
print(response.json())
else:
print(f"Error: {response.status_code}")
print(response.text)
Response
{
"status": "ok",
"inserted": 4,
"updated": 1
}
inserted counts new relations; updated counts relations that already existed and were refreshed.
Errors
| Code | Reason |
|---|---|
400 | relations empty or not a list, more than 10,000 items, an item that is not an object, an item without resource / parent, a resource that is its own parent, or unknown source |
403 | source other than manual without admin:service_topologies |
Delete a relation by ID
DELETE /api/resources/relations/<relation_id>
Authorization: Key YOUR_API_KEY
Removes one relation. Requires write:service_topologies.
Delete a single relation
- Bash
- Python
curl -X DELETE "http://your-tobealert.com:9000/api/resources/relations/0c9f2a61-7b3e-4d1c-a8e5-2f6b7c8d9e01" \
-H "Content-Type: application/json" \
-H "Authorization: Key YOUR_API_KEY"
import requests
response = requests.delete(
"http://your-tobealert.com:9000/api/resources/relations/0c9f2a61-7b3e-4d1c-a8e5-2f6b7c8d9e01",
headers={
"Content-Type": "application/json",
"Authorization": "Key YOUR_API_KEY"
}
)
if response.status_code in [200, 201]:
print("Request successful")
print(response.json())
else:
print(f"Error: {response.status_code}")
print(response.text)
Response
{ "status": "ok" }
Errors
| Code | Reason |
|---|---|
404 | Relation not found |
Delete the relations of a resource
DELETE /api/resources/relations?resource=<name>&environment=<env>
Authorization: Key YOUR_API_KEY
Removes every relation in which the resource is the child, narrowed by the optional filters. Requires write:service_topologies.
Query parameters
| Parameter | Required | Description |
|---|---|---|
resource | Yes | The child whose relations are removed |
environment | No | Environment ("" if omitted) |
parent | No | Only the relation to this parent |
source | No | Only relations with this source |
sourceRef | No | Only relations with this reference |
Example
Remove the manual parents of orders-db in Production
- Bash
- Python
curl -X DELETE "http://your-tobealert.com:9000/api/resources/relations?resource=orders-db&environment=Production&source=manual" \
-H "Content-Type: application/json" \
-H "Authorization: Key YOUR_API_KEY"
import requests
response = requests.delete(
"http://your-tobealert.com:9000/api/resources/relations",
headers={
"Content-Type": "application/json",
"Authorization": "Key YOUR_API_KEY"
},
params={
"resource": "orders-db",
"environment": "Production",
"source": "manual"
}
)
if response.status_code in [200, 201]:
print("Request successful")
print(response.json())
else:
print(f"Error: {response.status_code}")
print(response.text)
Response
{
"status": "ok",
"deleted": 2
}
Deleting a relation does not change the resourceParent stored on existing alerts; it only removes the row from the graph. Child relations of a deleted parent are kept (the parent's own children still point to it).
Import from a CMDB
POST /api/resources/import?mode=merge|replace
Content-Type: application/json
Authorization: Key YOUR_API_KEY
Loads a flat list of resources — one record per resource with its environment, its parent and the services it belongs to — into the resource graph and into one service topology per service. Records carry no IDs, edges or positions, so the same CMDB extract can be imported again and again: an unchanged file changes nothing. Requires write:service_topologies (relations are written with source cmdb without admin:service_topologies).
The web UI offers the same import in the Import dialog of the Services tab, format CMDB (simple) — see Importing from a CMDB for the full semantics.
Query parameters
| Parameter | Required | Default | Description |
|---|---|---|---|
format | No | by content | json or csv. Without it: the file extension of a file upload, text/csv as Content-Type, otherwise a body starting with { or [ is JSON and anything else CSV |
mode | No | merge | merge: add and update, never remove. replace: also remove the relations and topology nodes of this source that are not in the file (nodes drawn by hand or imported from another source are kept) |
dryRun | No | 0 | 1 / true: plan and return the counters, write nothing |
source | No | cmdb | Name of the CMDB (at most 100 characters). Stored as sourceRef on every relation and as params.cmdbSource on every topology node, and used to scope replace |
environment | No | "" | Environment for records without one (warning environmentDefaulted) |
Request body
JSON (a list, or { "items": [ ... ] }), raw CSV (Content-Type: text/csv), or multipart/form-data with a file part. At most 50,000 records (tooManyRecords) and 20 MB (bodyTooLarge).
| Field | Required | Description |
|---|---|---|
resource | Yes | Resource name (≤ 255 characters, trimmed) |
environment | No | Environment (≤ 255). Empty → ?environment=, otherwise "". environment + resource identify the record |
parent | No | Parent resource in the same environment; need not be in the file (unknownParent) |
services | No | JSON list, or a string separated by ; or |; service (singular) is also read. Empty → the record only feeds the graph |
type | No | ≤ 100 characters, stored as params.type of the node |
attributes | No | Object of strings, stored as params.attributes. In CSV: columns attr.<key> |
CSV: UTF-8 (BOM tolerated), header row with at least resource, , or ; as separator (detected), empty lines skipped, unknown columns ignored with unknownColumn.
Template
GET /api/resources/import/template?format=json|csv
Returns a six-record example (cmdb-import-template.json / .csv) with two environments, a parent that is not in the file and a record without services. Requires read:service_topologies.
What is written
- Relations:
resource → parentper record with aparent, sourcecmdb,sourceRef=source. A resource keeps one parent per source: a differentparentreplaces the previous relation (update). A relation that already exists from a higher-ranked source (manual,event) keeps its source. - Catalog: a service entry per distinct service name, created when missing (
serviceCreated); existing entries are not modified. - Topologies: per service, created when missing (
topologyCreated): one environment node per environment, one resource node per record (params.cmdbSource,type,attributes), edges service → environment, parent → resource when the parent is in the service (or already a node), environment → resource otherwise (parentNotInService). Existing nodes keep their positions; new ones are laid out automatically. The resulting graph goes through the validation rules.
Example
Dry run of a CMDB extract named servicenow
- Bash
- Python
curl -X POST "http://your-tobealert.com:9000/api/resources/import?mode=replace&dryRun=1&source=servicenow" \
-H "Content-Type: application/json" \
-H "Authorization: Key YOUR_API_KEY" \
-d '{
"items": [
{
"resource": "host-3",
"environment": "prod",
"services": [
"payments"
],
"type": "hypervisor",
"attributes": {
"site": "madrid-1"
}
},
{
"resource": "vm-7",
"environment": "prod",
"parent": "host-3",
"services": [
"payments"
],
"type": "vm"
},
{
"resource": "db-01",
"environment": "prod",
"parent": "vm-7",
"services": [
"payments",
"billing"
],
"type": "database"
},
{
"resource": "api-1",
"environment": "prod",
"parent": "k8s-cluster",
"services": [
"payments"
],
"type": "deployment"
},
{
"resource": "db-01",
"environment": "staging",
"services": [
"payments"
],
"type": "database"
},
{
"resource": "backup-nas",
"environment": "prod",
"services": [],
"type": "storage"
}
]
}'
import requests
response = requests.post(
"http://your-tobealert.com:9000/api/resources/import",
headers={
"Content-Type": "application/json",
"Authorization": "Key YOUR_API_KEY"
},
params={
"mode": "replace",
"dryRun": "1",
"source": "servicenow"
},
json={
"items": [
{
"resource": "host-3",
"environment": "prod",
"services": [
"payments"
],
"type": "hypervisor",
"attributes": {
"site": "madrid-1"
}
},
{
"resource": "vm-7",
"environment": "prod",
"parent": "host-3",
"services": [
"payments"
],
"type": "vm"
},
{
"resource": "db-01",
"environment": "prod",
"parent": "vm-7",
"services": [
"payments",
"billing"
],
"type": "database"
},
{
"resource": "api-1",
"environment": "prod",
"parent": "k8s-cluster",
"services": [
"payments"
],
"type": "deployment"
},
{
"resource": "db-01",
"environment": "staging",
"services": [
"payments"
],
"type": "database"
},
{
"resource": "backup-nas",
"environment": "prod",
"services": [],
"type": "storage"
}
]
}
)
if response.status_code in [200, 201]:
print("Request successful")
print(response.json())
else:
print(f"Error: {response.status_code}")
print(response.text)
The same records as CSV:
curl -X POST "http://your-tobealert.com:9000/api/resources/import?mode=replace&source=servicenow" \
-H "Authorization: Key YOUR_API_KEY" \
-H "Content-Type: text/csv" \
--data-binary @cmdb-export.csv
Response
The same document for a dry run and a real import; applied says which one it was. topologyId is null in a dry run for topologies that would be created.
{
"status": "ok",
"applied": false,
"source": "servicenow",
"mode": "replace",
"records": { "total": 6, "withServices": 5, "graphOnly": 1 },
"relations": { "create": 3, "update": 0, "remove": 0, "unchanged": 0 },
"topologies": [
{
"service": "billing",
"action": "create",
"topologyId": null,
"nodes": { "create": 3, "update": 0, "remove": 0, "unchanged": 0 },
"edges": { "create": 2, "update": 0, "remove": 0, "unchanged": 0 },
"errors": []
},
{
"service": "payments",
"action": "create",
"topologyId": null,
"nodes": { "create": 8, "update": 0, "remove": 0, "unchanged": 0 },
"edges": { "create": 7, "update": 0, "remove": 0, "unchanged": 0 },
"errors": []
}
],
"catalogServices": { "create": 2, "unchanged": 0 },
"warnings": [
{ "code": "unknownParent", "message": "parent \"k8s-cluster\" of \"api-1\" is not in the import; the relation is created anyway", "row": 3, "params": { "resource": "api-1", "parent": "k8s-cluster" } },
{ "code": "serviceCreated", "message": "catalog service \"billing\" will be created", "row": null, "params": { "service": "billing" } },
{ "code": "topologyCreated", "message": "topology for service \"billing\" will be created", "row": null, "params": { "service": "billing" } },
{ "code": "parentNotInService", "message": "parent \"vm-7\" of \"db-01\" is not in service \"billing\"; the resource hangs from the environment", "row": 2, "params": { "resource": "db-01", "parent": "vm-7", "service": "billing" } },
{ "code": "serviceCreated", "message": "catalog service \"payments\" will be created", "row": null, "params": { "service": "payments" } },
{ "code": "topologyCreated", "message": "topology for service \"payments\" will be created", "row": null, "params": { "service": "payments" } },
{ "code": "parentNotInService", "message": "parent \"k8s-cluster\" of \"api-1\" is not in service \"payments\"; the resource hangs from the environment", "row": 3, "params": { "resource": "api-1", "parent": "k8s-cluster", "service": "payments" } }
],
"errors": []
}
| Field | Meaning |
|---|---|
records | total records read; withServices feed topologies; graphOnly only feed the graph |
relations | Graph relations of this source to create, update (parent changed), remove (replace only) or leave unchanged |
topologies[] | One entry per service: action (create / update), topologyId, and node / edge counters |
catalogServices | Catalog entries to create or leave unchanged |
warnings[] | code, message, row (0-based index of the record; null for warnings about a service, 0 for CSV header warnings) and params |
Re-importing the same file yields unchanged everywhere. A real import writes a resources-cmdb-imported audit entry.
Errors
400 with message: "invalid CMDB import" and an errors list (code, path, message); nothing is written when any record is invalid.
| Code | Reason |
|---|---|
missingResource | A record without resource or that is not an object |
duplicateRecord | The same environment + resource twice |
selfParent | parent equal to resource |
cycle | The parents of one environment form a loop |
valueTooLong | A name over 255 characters, or type over 100 |
invalidCsvHeader | Empty CSV or no resource column |
invalidJson | Not valid JSON / UTF-8, or not a list of records |
tooManyRecords / bodyTooLarge | More than 50,000 records / more than 20 MB |
| Any topology validation code | The resulting topology of a service would be invalid; path is prefixed with topologies[<service>]. |
Warnings (unknownParent, parentNotInService, unknownColumn, environmentDefaulted, serviceCreated, topologyCreated) do not stop the import.
Audit trail
Writes through this API are written to the administrator audit trail with the type resource-relation:
| Event | When |
|---|---|
resource-relation-created | POST /api/resources/relations |
resource-relations-bulk | POST /api/resources/relations/bulk (one entry per request, with the inserted / updated counts) |
resource-relation-deleted | Either DELETE endpoint (by ID, or by resource with the number of rows removed) |
A CMDB import writes a resources-cmdb-imported entry (type resources) with the mode, the source name and the number of records; dry runs write nothing.
Relations declared by alert payloads (source: event) or by policies (source: policy) do not create audit entries; the alert and its history are the record.
Putting it together
- Declare a parent from the alert — the monitoring system already knows the hierarchy, so send it with the event. This stores
resourceParenton the alert and a relation with sourceevent:
Alert that declares its parent
- Bash
- Python
curl -X POST "http://your-tobealert.com:9000/api/alert" \
-H "Content-Type: application/json" \
-H "Authorization: Key YOUR_API_KEY" \
-d '{
"resource": "vm-7",
"event": "DiskFull",
"environment": "Production",
"severity": "major",
"resourceParent": "esx-01"
}'
import requests
response = requests.post(
"http://your-tobealert.com:9000/api/alert",
headers={
"Content-Type": "application/json",
"Authorization": "Key YOUR_API_KEY"
},
json={
"resource": "vm-7",
"event": "DiskFull",
"environment": "Production",
"severity": "major",
"resourceParent": "esx-01"
}
)
if response.status_code in [200, 201]:
print("Request successful")
print(response.json())
else:
print(f"Error: {response.status_code}")
print(response.text)
- Add a relation by hand — something the events never mention, such as the application that depends on a database:
Manual relation (source manual)
- Bash
- Python
curl -X POST "http://your-tobealert.com:9000/api/resources/relations" \
-H "Content-Type: application/json" \
-H "Authorization: Key YOUR_API_KEY" \
-d '{
"environment": "Production",
"resource": "orders-db",
"parent": "orders-app"
}'
import requests
response = requests.post(
"http://your-tobealert.com:9000/api/resources/relations",
headers={
"Content-Type": "application/json",
"Authorization": "Key YOUR_API_KEY"
},
json={
"environment": "Production",
"resource": "orders-db",
"parent": "orders-app"
}
)
if response.status_code in [200, 201]:
print("Request successful")
print(response.json())
else:
print(f"Error: {response.status_code}")
print(response.text)
- Read the ancestors of a resource when triaging — where does this alert sit in the hierarchy?
Ancestors of vm-7, five levels at most
- Bash
- Python
curl -X GET "http://your-tobealert.com:9000/api/resources/graph?resource=vm-7&environment=Production&direction=ancestors&depth=5" \
-H "Content-Type: application/json" \
-H "Authorization: Key YOUR_API_KEY"
import requests
response = requests.get(
"http://your-tobealert.com:9000/api/resources/graph",
headers={
"Content-Type": "application/json",
"Authorization": "Key YOUR_API_KEY"
},
params={
"resource": "vm-7",
"environment": "Production",
"direction": "ancestors",
"depth": "5"
}
)
if response.status_code in [200, 201]:
print("Request successful")
print(response.json())
else:
print(f"Error: {response.status_code}")
print(response.text)
Best practices
- Use the same
environmenteverywhere — a relation declared inProductionis invisible to alerts whose environment is empty or spelled differently. - Let events declare what they know (
resourceParent,resourceChildren), let a Resource relation policy fill the gaps from attributes, and reserve manual relations for what monitoring cannot see; manual relations rank highest and will not be overwritten. - Set
sourceRefon bulk loads so you can list or delete one import (?sourceRef=…) without touching relations from other sources. For a CMDB, prefer the CMDB import: it is idempotent,replacemode removes what left the CMDB, and it keeps the service topologies in step with the graph. - Avoid loops — storage tolerates
a → b → a, but ancestors and descendants stop at the first repeated resource, so the output is less useful. - Keep names stable — relations match on exact, trimmed strings; renaming a host in monitoring leaves its old relations behind until you delete them.