LaserData Cloud
Observability

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

KindDestination FormatDescription
slackHTTPS webhook URLSends a formatted message to a Slack channel via incoming webhook
webhookHTTPS URLSends a JSON payload ({"subject": "...", "body": "..."}) via HTTP POST
emailEmail addressSends 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 TypeDescription
Deployments
deployment_createdA new deployment was created
deployment_initializedA deployment finished initializing and is ready
deployment_upgradedA deployment was upgraded to a new tier or storage
deployment_deletedA deployment was deleted
deployment_certificates_rotatedDeployment TLS certificates were rotated
deployment_secrets_rotatedDeployment secrets were rotated
Invitations
invitation_createdA new team invitation was sent
invitation_acceptedA team member accepted an invitation
invitation_rejectedA team member rejected an invitation
Tenant
tenant_config_updatedWorkspace settings (join policy, invitation locks) were changed
tenant_join_requestedA same-domain user requested to join the tenant under request_to_join policy
member_joinedA same-domain user auto-joined the tenant under open policy
Organization
division_createdA division was created
division_updatedA division was updated
division_deletedA division was deleted
environment_createdAn environment was created
environment_updatedAn environment was updated
environment_deletedAn environment was deleted
Health Alerts
high_cpu_usageCPU usage exceeded threshold on a node
high_memory_usageMemory usage exceeded threshold on a node
high_disk_usageDisk usage exceeded threshold on a node
node_unreachableA deployment node or connector instance is unreachable
cpu_usage_resolvedCPU usage returned to normal
memory_usage_resolvedMemory usage returned to normal
disk_usage_resolvedDisk usage returned to normal
node_reachableA previously unreachable node or connector instance is back online
Other
certificate_expiringA TLS certificate is about to expire
billing_limit_reachedA 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.

ScopeDescription
scope_tenant_idsOnly events from these tenants
scope_division_idsOnly events from these divisions
scope_environment_idsOnly events from these environments
scope_deployment_idsOnly 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

ScopeReadManage
Tenantnotifications:readnotifications:manage
Divisionnotifications:readnotifications:manage

manage includes read. See Roles & Permissions.

Plan Limits

ResourceBasicProEnterprise
Notification channels (per tenant)1320
Subscriptions per channel31020

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"
  }'
FieldRequiredDescription
channelYesChannel kind: slack, webhook, email
nameYesUnique name (1-100 chars, alphanumeric with -, _, ., :, space)
destinationYesTarget URL or email (1-1000 chars)
settingsNoChannel-specific settings (see Channel Settings)
remarksNoNotes (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:

ParameterTypeDescription
pageintegerPage number (optional, default 1)
resultsintegerResults per page (optional, default 10, max 100)
channelstringFilter 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]
  }'
FieldRequiredDescription
message_typesYesNon-empty array of event types to subscribe to
scope_tenant_idsNoFilter to events from specific tenants
scope_division_idsNoFilter to events from specific divisions
scope_environment_idsNoFilter to events from specific environments
scope_deployment_idsNoFilter 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:

ParameterTypeDescription
pageintegerPage number (optional, default 1)
resultsintegerResults per page (optional, default 10, max 100)
message_typestringFilter 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:

ParameterTypeDescription
pageintegerPage number (optional, default 1)
resultsintegerResults per page (optional, default 10, max 100)
message_typestringFilter 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.

On this page