Cases¶
Cases let you open and track issues directly in Kibana: add assignees and tags, set severity and status, attach comments, alerts, and files, and push cases to external incident-management systems. kibana-py exposes the full Cases API as client.cases.
Creating Cases¶
from kibana import Kibana
client = Kibana("http://localhost:5601", api_key="your_api_key")
case = client.cases.create(
title="Suspicious login activity",
description="Multiple failed logins detected from unusual locations.",
tags=["security", "auth"],
severity="high",
)
case_id = case.body["id"]
case_version = case.body["version"]
print(f"Created case {case_id} (status: {case.body['status']})")
Key arguments:
owner— the application that owns the case:"cases"(Stack Management, the default),"observability", or"securitySolution"severity—"low"(default),"medium","high", or"critical"connector— an external incident-management connector ({"id", "name", "type", "fields"}); defaults to the “none” connectorsettings— e.g.{"syncAlerts": True}; defaults to{"syncAlerts": False}assignees— a list of{"uid": ...}user profile objects (Platinum+ license)categoryandcustom_fields— additional classification
Reading and Searching Cases¶
# Get a case by ID
case = client.cases.get(case_id=case_id)
print(case.body["title"], case.body["status"])
# Search cases with filters
found = client.cases.find(
status="open",
severity="high",
tags="security",
search="login",
sort_field="createdAt",
sort_order="desc",
per_page=20,
)
print(f"{found.body['total']} matching cases "
f"({found.body['count_open_cases']} open)")
for c in found.body["cases"]:
print(f" {c['id']}: {c['title']}")
# Aggregations across your cases
tags = client.cases.get_tags() # all tags in use (JSON array)
reporters = client.cases.get_reporters() # all reporters (JSON array)
Updating Cases¶
The Cases update endpoint is a bulk PATCH with optimistic concurrency: every update must include the case’s current version. kibana-py offers a single-case-friendly form:
# Fetch the current version first
case = client.cases.get(case_id=case_id)
updated = client.cases.update(
id=case_id,
version=case.body["version"],
status="in-progress",
severity="critical",
)
# The response body is a LIST of updated cases
print(updated.body[0]["status"])
To update several cases at once, pass cases with raw API field names:
client.cases.update(
cases=[
{"id": "case-1", "version": "WzUsMV0=", "status": "closed"},
{"id": "case-2", "version": "WzYsMV0=", "tags": ["triaged"]},
]
)
Note
A stale version raises ConflictError — re-fetch the case and retry. This protects concurrent workflows from clobbering each other’s changes.
Attaching Files¶
with open("evidence.png", "rb") as f:
client.cases.add_file(
case_id=case_id,
file=f.read(),
filename="evidence.png",
mime_type="image/png",
)
Files are stored as externalReference attachments backed by a file saved object.
User Actions (Audit Trail)¶
Every change to a case is recorded as a user action:
actions = client.cases.find_user_actions(case_id=case_id, sort_order="asc")
for action in actions.body["userActions"]:
print(action["type"], action["action"], action["created_at"])
External Incident Management¶
Push a case to a configured connector (Jira, ServiceNow, etc.):
# Find connectors usable with cases (JSON array)
connectors = client.cases.find_connectors()
for conn in connectors.body:
print(conn["id"], conn["name"])
pushed = client.cases.push(case_id=case_id, connector_id="connector-id")
print(pushed.body["external_service"]["external_url"])
Case Configuration¶
Configure default connectors, closure behavior, custom fields, and templates per owner:
config = client.cases.get_configuration()
created = client.cases.create_configuration(
closure_type="close-by-user",
connector={"id": "none", "name": "none", "type": ".none", "fields": None},
owner="cases",
)
client.cases.update_configuration(
configuration_id=created.body["id"],
version=created.body["version"],
closure_type="close-by-pushing",
)
Deleting Cases¶
delete() accepts one ID or a list (max 100):
client.cases.delete(ids=case_id)
client.cases.delete(ids=["case-1", "case-2"])
Space-Scoped Cases¶
All operations accept space_id, or use a space-scoped client:
case = client.cases.create(
title="Marketing incident",
description="...",
space_id="marketing",
)
marketing = client.space("marketing")
found = marketing.cases.find(status="open")
Complete Example: Case Lifecycle¶
from kibana import Kibana
with Kibana("http://localhost:5601", api_key="your_api_key") as client:
# Open a case
case = client.cases.create(
title="kbnpy demo case",
description="Created by kibana-py.",
tags=["demo"],
severity="medium",
)
case_id = case.body["id"]
try:
# Work the case
client.cases.add_comment(case_id=case_id, comment="Looking into it.")
current = client.cases.get(case_id=case_id)
client.cases.update(
id=case_id,
version=current.body["version"],
status="in-progress",
)
# Resolve it
current = client.cases.get(case_id=case_id)
client.cases.update(
id=case_id,
version=current.body["version"],
status="closed",
)
finally:
client.cases.delete(ids=case_id)
Next Steps¶
Configure Connectors to push cases to external systems
Combine cases with Alerting to track alert investigations
Browse
examples/cases_management.pyin the repository for a runnable end-to-end script
Comments and Alerts¶