LaserData Cloud
Observability

Notifications

Multi-channel notification system with configurable subscriptions and event filtering

Notifications are an experimental feature. APIs and behavior may change without prior notice.

LaserData Cloud includes a notification system that delivers real-time alerts about deployment events, resource changes, and operational issues to your preferred channels.

How It Works

The notification system operates at two levels:

  1. Channels define where notifications are sent (Slack, webhook, email, SMS)
  2. Subscriptions define which events trigger notifications on a channel, with optional scope filtering

Channels can be scoped to a tenant (organization-wide) or a division. Subscriptions are attached to channels and filter events by type and resource scope.

When an event occurs, the dispatch engine checks each enabled channel's subscriptions. If a subscription matches the event type and scope, the notification is delivered to the channel's destination.

Notification Channels

A channel represents a delivery destination. Each channel has a kind (delivery method), a name, and a destination (URL, email address, or phone number).

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
smsPhone number (+1234567890)SMS delivery (coming soon)

Slack and webhook destinations must use HTTPS and cannot target private/internal IP addresses (SSRF protection). Destinations are encrypted at rest.

Channel Scoping

Channels can be created at two levels:

  • Tenant-scoped — organization-wide channels available to all divisions
  • Division-scoped — channels specific to a division

Channel Settings

Each channel kind supports optional settings:

Slack:

{
  "slack": {
    "channel": "#alerts",
    "username": "LaserData Bot",
    "icon_emoji": ":bell:"
  }
}

Webhook:

{
  "webhook": {
    "headers": { "Authorization": "Bearer token123" },
    "method": "POST"
  }
}

SMS:

{
  "sms": {
    "sender_id": "LaserData"
  }
}

Notification Subscriptions

Subscriptions determine which events trigger notifications on a channel. Each subscription specifies one or more event types and optional scope filters.

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
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 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 is back online
Other
certificate_expiringA TLS certificate is about to expire
billing_limit_reachedA deployment's spend limit was reached

Scope Filtering

Each subscription can filter events by resource scope. Scope levels are ANDed together — an event must match all specified scope levels to trigger the notification.

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

If a scope array is empty or omitted, that level matches all resources. For example, setting only scope_deployment_ids: [42] delivers notifications for all matching event types on deployment 42, regardless of which division or environment it belongs to.

Subscription Behavior

  • If a channel has no subscriptions, all events are delivered to it (default: send all)
  • If a channel has subscriptions, only events matching at least one subscription are delivered
  • Notifications are throttled per channel + event type + resource to prevent flooding

Permissions

ScopeReadManage
Tenantnotifications:readnotifications:manage
Divisionnotifications:readnotifications:manage

manage implies read. See Roles & Permissions for details.

Plan Limits

ResourceBasicProEnterprise
Notification channels (per tenant)1310
Subscriptions per channel31050

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, sms
nameYesUnique name (1-100 chars, alphanumeric with -, _, ., :, space)
destinationYesTarget URL, email, or phone number (1-1000 chars)
settingsNoChannel-specific settings (see Channel Settings)
remarksNoNotes (max 500 chars)

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"
  }'

Same request body as tenant-scoped channels.

List Channels

curl "https://api.laserdata.cloud/tenants/{tenant_id}/channels?page=1&results=10&channel=slack" \
  -H "ld-api-key: YOUR_API_KEY"

Query parameters:

ParameterTypeDescription
pageintegerPage number (optional, default 1)
resultsintegerResults per page (optional, default 10, max 100)
channelstringFilter by kind: slack, webhook, email, sms (optional)

Response:

{
  "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"
    }
  ]
}

Division-scoped: 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"
}

The detailed response includes destination, settings, and remarks which are omitted in the list response.

Division-scoped: 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
  }'

All fields are optional — include only the fields you want to change. Set enabled to false to temporarily disable a channel without deleting it.

Returns 204 No Content.

Division-scoped: PUT /tenants/{tenant_id}/divisions/{division_id}/channels/{channel_id}

Delete a Channel

curl -X DELETE https://api.laserdata.cloud/tenants/{tenant_id}/channels/{channel_id} \
  -H "ld-api-key: YOUR_API_KEY"

Deleting a channel also deletes all its subscriptions. This action is irreversible.

Returns 204 No Content.

Division-scoped: DELETE /tenants/{tenant_id}/divisions/{division_id}/channels/{channel_id}

Test a Channel

Send a test notification to verify the channel is configured correctly:

curl -X POST https://api.laserdata.cloud/tenants/{tenant_id}/channels/{channel_id}/test \
  -H "ld-api-key: YOUR_API_KEY"

Sends a test message to the channel's destination. Rate limited to one test per 10 seconds per channel.

Returns 204 No Content.

Division-scoped: 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

All scope IDs must reference existing resources within the tenant.

Returns 201 Created.

Division-scoped: POST /tenants/{tenant_id}/divisions/{division_id}/channels/{channel_id}/subscriptions

Set Subscriptions (Replace All)

Atomically replace all subscriptions for a channel in a single 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": [42, 43]
      }
    ]
  }'

All existing subscriptions for the channel are deleted and replaced with the provided list. The total count must be within the plan's notification_subscriptions_limit.

Returns 204 No Content.

Division-scoped: 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"

Query parameters:

ParameterTypeDescription
pageintegerPage number (optional, default 1)
resultsintegerResults per page (optional, default 10, max 100)
message_typestringFilter by event type (optional)

Response:

{
  "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"
    }
  ]
}

Scope arrays are omitted when empty. Scope entries include both id and name (resolved from the database).

Division-scoped: 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": 42, "name": "prod-cluster" }],
  "created_at": "2025-06-01T10:00:00Z",
  "updated_at": "2025-06-01T10:00:00Z"
}

Division-scoped: 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"

Returns 204 No Content.

Division-scoped: DELETE /tenants/{tenant_id}/divisions/{division_id}/channels/{channel_id}/subscriptions/{subscription_id}

Get Notification Types

Returns all available notification 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

Browse notification history for a channel:

curl "https://api.laserdata.cloud/tenants/{tenant_id}/channels/{channel_id}/notifications?page=1&results=10" \
  -H "ld-api-key: YOUR_API_KEY"

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": 42,
      "message_type": "deployment_initialized",
      "content": "Deployment prod-cluster has been initialized",
      "created_at": "2025-06-01T10:30:00Z"
    }
  ]
}

Division-scoped: GET /tenants/{tenant_id}/divisions/{division_id}/channels/{channel_id}/notifications

On this page