Notifications
Send events to Slack, webhooks, or email and filter them by resource and event type
Notifications send deployment events, resource changes, and operational alerts to Slack, webhooks, or email. A channel names a destination. Subscriptions choose which events it receives.
How It Works
A channel belongs to a tenant or division. Its subscriptions filter event types and resource scope, the resources that an event concerns. When an enabled channel has a matching subscription, the platform sends the event to its destination.
Notification Channels
Each channel has a name, delivery kind, and destination URL or email address. The platform encrypts destinations at rest.
Channel Kinds
| Kind | Destination Format | Description |
|---|---|---|
slack | HTTPS webhook URL | Sends a formatted message to a Slack channel via incoming webhook |
webhook | HTTPS URL | Sends a JSON payload ({"subject": "...", "body": "..."}) via HTTP POST |
email | Email address | Sends an HTML email with subject and body |
Slack and webhook destinations must use HTTPS. They cannot target private or internal IP addresses. This prevents the notification service from accessing private network targets through supplied URLs.
Channel Scoping
Tenant channels apply across the organization. Division channels belong to one division. Subscriptions can further limit the resources whose events reach a channel.
Channel Settings
Slack and webhook channels support these optional fields.
Slack:
{
"slack": {
"channel": "#alerts",
"username": "LaserData Bot",
"icon_emoji": ":bell:"
}
}Webhook:
{
"webhook": {
"headers": { "Authorization": "Bearer token123" },
"method": "POST"
}
}Notification Subscriptions
A subscription selects one or more event types. Its optional scope filters limit the resources that can trigger delivery.
Event Types
| Event Type | Description |
|---|---|
| Deployments | |
deployment_created | A new deployment was created |
deployment_initialized | A deployment finished initializing and is ready |
deployment_upgraded | A deployment was upgraded to a new tier or storage |
deployment_deleted | A deployment was deleted |
deployment_certificates_rotated | Deployment TLS certificates were rotated |
deployment_secrets_rotated | Deployment secrets were rotated |
| Invitations | |
invitation_created | A new team invitation was sent |
invitation_accepted | A team member accepted an invitation |
invitation_rejected | A team member rejected an invitation |
| Tenant | |
tenant_config_updated | Workspace settings (join policy, invitation locks) were changed |
tenant_join_requested | A same-domain user requested to join the tenant under request_to_join policy |
member_joined | A same-domain user auto-joined the tenant under open policy |
| Organization | |
division_created | A division was created |
division_updated | A division was updated |
division_deleted | A division was deleted |
environment_created | An environment was created |
environment_updated | An environment was updated |
environment_deleted | An environment was deleted |
| Health Alerts | |
high_cpu_usage | CPU usage exceeded threshold on a node |
high_memory_usage | Memory usage exceeded threshold on a node |
high_disk_usage | Disk usage exceeded threshold on a node |
node_unreachable | A deployment node or connector instance is unreachable |
cpu_usage_resolved | CPU usage returned to normal |
memory_usage_resolved | Memory usage returned to normal |
disk_usage_resolved | Disk usage returned to normal |
node_reachable | A previously unreachable node or connector instance is back online |
| Other | |
certificate_expiring | A TLS certificate is about to expire |
billing_limit_reached | A deployment's spend limit was reached |
node_unreachable and node_reachable apply to Warden, Iggy, the Connectors host, and individual connector instances. Connector events include connector_instance_name and connector_key. The subject therefore uses connector '<name>' (<key>, instance ID: <id>). Heartbeats for a deleted connector instance do not raise false node_unreachable alerts.
Scope Filtering
An event must match every scope level that a subscription specifies. The levels combine with AND. An empty or omitted array matches every resource at that level.
| Scope | Description |
|---|---|
scope_tenant_ids | Only events from these tenants |
scope_division_ids | Only events from these divisions |
scope_environment_ids | Only events from these environments |
scope_deployment_ids | Only events from these deployments |
For example, scope_deployment_ids: [611298765432109056] selects deployment 611298765432109056. With no other scope filters, its matching event types are delivered regardless of division or environment.
Subscription Behavior
Delivery follows these rules:
- A channel without subscriptions receives all events by default.
- A channel with subscriptions receives events that match at least one subscription.
- Throttling applies per channel, event type, and resource to limit repeated notifications.
Permissions
| Scope | Read | Manage |
|---|---|---|
| Tenant | notifications:read | notifications:manage |
| Division | notifications:read | notifications:manage |
manage includes read. See Roles & Permissions.
Plan Limits
| Resource | Basic | Pro | Enterprise |
|---|---|---|---|
| Notification channels (per tenant) | 1 | 3 | 20 |
| Subscriptions per channel | 3 | 10 | 20 |
API Reference
Create a Channel (Tenant)
curl -X POST https://api.laserdata.cloud/tenants/{tenant_id}/channels \
-H "ld-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"channel": "slack",
"name": "production-alerts",
"destination": "https://hooks.slack.com/services/T00/B00/xxx",
"settings": {
"slack": {
"channel": "#alerts",
"username": "LaserData"
}
},
"remarks": "Primary alerting channel"
}'| Field | Required | Description |
|---|---|---|
channel | Yes | Channel kind: slack, webhook, email |
name | Yes | Unique name (1-100 chars, alphanumeric with -, _, ., :, space) |
destination | Yes | Target URL or email (1-1000 chars) |
settings | No | Channel-specific settings (see Channel Settings) |
remarks | No | Notes (max 500 chars) |
A successful request returns 201 Created.
Create a Channel (Division)
curl -X POST https://api.laserdata.cloud/tenants/{tenant_id}/divisions/{division_id}/channels \
-H "ld-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"channel": "webhook",
"name": "ops-webhook",
"destination": "https://example.com/webhooks/laserdata"
}'Use the same body as for a tenant channel.
List Channels
curl "https://api.laserdata.cloud/tenants/{tenant_id}/channels?page=1&results=10&channel=slack" \
-H "ld-api-key: YOUR_API_KEY"Filter the list with these query parameters:
| Parameter | Type | Description |
|---|---|---|
page | integer | Page number (optional, default 1) |
results | integer | Results per page (optional, default 10, max 100) |
channel | string | Filter by kind: slack, webhook, email (optional) |
The response has this format:
{
"total_pages": 1,
"total_results": 2,
"page": 1,
"items": [
{
"id": 1,
"owner_kind": "tenant",
"owner_id": 100,
"channel": "slack",
"name": "production-alerts",
"enabled": true,
"created_at": "2025-06-01T10:00:00Z",
"updated_at": "2025-06-01T10:00:00Z"
}
]
}For a division, use GET /tenants/{tenant_id}/divisions/{division_id}/channels.
Get Channel Details
curl https://api.laserdata.cloud/tenants/{tenant_id}/channels/{channel_id} \
-H "ld-api-key: YOUR_API_KEY"{
"id": 1,
"owner_kind": "tenant",
"owner_id": 100,
"channel": "slack",
"name": "production-alerts",
"enabled": true,
"created_at": "2025-06-01T10:00:00Z",
"updated_at": "2025-06-01T10:00:00Z",
"destination": "https://hooks.slack.com/services/T00/B00/xxx",
"settings": {
"slack": {
"channel": "#alerts",
"username": "LaserData"
}
},
"remarks": "Primary alerting channel"
}Details include destination, settings, and remarks. List responses omit these fields. For a division, use GET /tenants/{tenant_id}/divisions/{division_id}/channels/{channel_id}.
Update a Channel
curl -X PUT https://api.laserdata.cloud/tenants/{tenant_id}/channels/{channel_id} \
-H "ld-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "updated-channel-name",
"destination": "https://hooks.slack.com/services/T00/B00/new",
"enabled": false
}'Include only fields that you want to change. Set enabled to false to disable delivery without deleting the channel. A successful request returns 204 No Content.
For a division, use PUT /tenants/{tenant_id}/divisions/{division_id}/channels/{channel_id}.
Delete a Channel
Deleting a channel also permanently deletes its subscriptions. This cannot be undone.
curl -X DELETE https://api.laserdata.cloud/tenants/{tenant_id}/channels/{channel_id} \
-H "ld-api-key: YOUR_API_KEY"A successful request returns 204 No Content. For a division, use DELETE /tenants/{tenant_id}/divisions/{division_id}/channels/{channel_id}.
Test a Channel
Send a test message to make sure that the destination receives notifications:
curl -X POST https://api.laserdata.cloud/tenants/{tenant_id}/channels/{channel_id}/test \
-H "ld-api-key: YOUR_API_KEY"A channel permits one test per 10 seconds. A successful request returns 204 No Content. For a division, use POST /tenants/{tenant_id}/divisions/{division_id}/channels/{channel_id}/test.
Create a Subscription
curl -X POST https://api.laserdata.cloud/tenants/{tenant_id}/channels/{channel_id}/subscriptions \
-H "ld-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"message_types": ["deployment_created", "deployment_deleted", "node_unreachable"],
"scope_division_ids": [1],
"scope_environment_ids": [10, 20]
}'| Field | Required | Description |
|---|---|---|
message_types | Yes | Non-empty array of event types to subscribe to |
scope_tenant_ids | No | Filter to events from specific tenants |
scope_division_ids | No | Filter to events from specific divisions |
scope_environment_ids | No | Filter to events from specific environments |
scope_deployment_ids | No | Filter to events from specific deployments |
Scope IDs must refer to resources in the tenant. A successful request returns 201 Created. For a division, use POST /tenants/{tenant_id}/divisions/{division_id}/channels/{channel_id}/subscriptions.
Set Subscriptions (Replace All)
Replace all channel subscriptions in one transaction:
curl -X PUT https://api.laserdata.cloud/tenants/{tenant_id}/channels/{channel_id}/subscriptions \
-H "ld-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"subscriptions": [
{
"message_types": ["deployment_created", "deployment_deleted"],
"scope_division_ids": [1]
},
{
"message_types": ["high_cpu_usage", "high_memory_usage", "high_disk_usage"],
"scope_deployment_ids": [611298765432109056, 611298765432109057]
}
]
}'The supplied list replaces every existing subscription for the channel. Its size must stay within notification_subscriptions_limit. A successful request returns 204 No Content.
For a division, use PUT /tenants/{tenant_id}/divisions/{division_id}/channels/{channel_id}/subscriptions.
List Subscriptions
curl "https://api.laserdata.cloud/tenants/{tenant_id}/channels/{channel_id}/subscriptions?page=1&results=10" \
-H "ld-api-key: YOUR_API_KEY"Filter the list with these query parameters:
| Parameter | Type | Description |
|---|---|---|
page | integer | Page number (optional, default 1) |
results | integer | Results per page (optional, default 10, max 100) |
message_type | string | Filter by event type (optional) |
The response has this format:
{
"total_pages": 1,
"total_results": 2,
"page": 1,
"items": [
{
"id": 1,
"channel_id": 100,
"message_types": ["deployment_created", "deployment_deleted"],
"scope_tenants": [{ "id": 1, "name": "Acme Corp" }],
"scope_divisions": [{ "id": 10, "name": "Platform Eng" }],
"created_at": "2025-06-01T10:00:00Z",
"updated_at": "2025-06-01T10:00:00Z"
}
]
}Empty scope arrays are omitted. Each scope entry includes id and name from the database. For a division, use GET /tenants/{tenant_id}/divisions/{division_id}/channels/{channel_id}/subscriptions.
Get Subscription Details
curl https://api.laserdata.cloud/tenants/{tenant_id}/channels/{channel_id}/subscriptions/{subscription_id} \
-H "ld-api-key: YOUR_API_KEY"{
"id": 1,
"channel_id": 100,
"message_types": ["deployment_created", "deployment_deleted"],
"scope_tenants": [{ "id": 1, "name": "Acme Corp" }],
"scope_divisions": [{ "id": 10, "name": "Platform Eng" }],
"scope_environments": [{ "id": 100, "name": "Production" }],
"scope_deployments": [{ "id": 611298765432109056, "name": "prod-cluster" }],
"created_at": "2025-06-01T10:00:00Z",
"updated_at": "2025-06-01T10:00:00Z"
}For a division, use GET /tenants/{tenant_id}/divisions/{division_id}/channels/{channel_id}/subscriptions/{subscription_id}.
Delete a Subscription
curl -X DELETE https://api.laserdata.cloud/tenants/{tenant_id}/channels/{channel_id}/subscriptions/{subscription_id} \
-H "ld-api-key: YOUR_API_KEY"A successful request returns 204 No Content. For a division, use DELETE /tenants/{tenant_id}/divisions/{division_id}/channels/{channel_id}/subscriptions/{subscription_id}.
Get Notification Types
Retrieve the available event types:
curl https://api.laserdata.cloud/notifications/types \
-H "ld-api-key: YOUR_API_KEY"[
{ "type": "deployment_created", "name": "Deployment created" },
{ "type": "deployment_initialized", "name": "Deployment initialized" },
{ "type": "high_cpu_usage", "name": "High CPU usage" },
{ "type": "node_unreachable", "name": "Node unreachable" }
]Browse Notifications
Read a channel's notification history:
curl "https://api.laserdata.cloud/tenants/{tenant_id}/channels/{channel_id}/notifications?page=1&results=10" \
-H "ld-api-key: YOUR_API_KEY"Filter the history with these query parameters:
| Parameter | Type | Description |
|---|---|---|
page | integer | Page number (optional, default 1) |
results | integer | Results per page (optional, default 10, max 100) |
message_type | string | Filter by event type (optional) |
{
"total_pages": 1,
"total_results": 5,
"page": 1,
"items": [
{
"id": 1,
"tenant_id": 100,
"division_id": 10,
"environment_id": 1,
"deployment_id": 611298765432109056,
"message_type": "deployment_initialized",
"content": "Deployment prod-cluster has been initialized",
"created_at": "2025-06-01T10:30:00Z"
}
]
}For a division, use GET /tenants/{tenant_id}/divisions/{division_id}/channels/{channel_id}/notifications.