Back to Documentation

API Reference

Complete REST API reference for ProLayer. Everything you need to send internal team alerts and monitoring notifications across six channels with a single, unified API.

Base URLhttps://api.prolayer.io
Versionv1

Authentication

All API requests require a valid API key passed in the Authorization header.

ProLayer uses API keys to authenticate requests. You can manage your keys in the Dashboard → API Keys. Include your key in every request using the Authorization header.

Authorization header
Authorization: Bearer sk_live_your_api_key_here
cURL example
curl -X POST https://api.prolayer.io/v1/notifications/send \
  -H "Authorization: Bearer sk_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "slack",
    "to": "team_engineering",
    "message": "Deploy v2.4.1 completed on production",
    "severity": "info"
  }'
Key prefixEnvironmentDescription
sk_live_ProductionLive key — alerts are delivered to approved team members.
sk_test_SandboxTest key — alerts are simulated. No credits consumed.
Keep your API keys secret. Never expose them in client-side code, public repositories, or browser requests. If a key is compromised, rotate it immediately from the dashboard.

Alerts

Send, retrieve, list, and cancel team alerts across all supported channels. All recipients must be approved, opted-in team members.

POST/v1/notifications/send
Send an alert to approved team members through any supported channel. Supports real-time and scheduled delivery, templates, idempotency, severity levels, and per-alert metadata.

Request Body Parameters

ParameterTypeRequiredDescription
channelstringRequiredDelivery channel. One of: whatsapp, telegram, email, push, slack, in-app.
tostringRequiredApproved team member or team identifier. Team ID (team_xxx), approved contact phone (E.164) for WhatsApp/Telegram, team email for Email, team member ID for Push/In-App, channel name for Slack.
messagestringOptionalThe alert content. Required unless template_id is provided.
template_idstringOptionalID of a predefined template (tpl_xxx). Template variables are populated from metadata.
severitystringOptionalAlert severity level. One of: info, warning, critical. Defaults to "info". Critical alerts bypass Do Not Disturb settings for on-call team members.
metadataobjectOptionalArbitrary key-value pairs attached to the alert. Used for template variable substitution, incident correlation, and your own tracking.
schedule_atstringOptionalISO 8601 timestamp for scheduled delivery. Must be at least 60 seconds in the future and no more than 30 days ahead.
prioritystringOptionalDelivery priority. One of: low, normal, high. Defaults to "normal". High-priority messages bypass batching queues.
idempotency_keystringOptionalUnique key (max 255 chars) to prevent duplicate sends. Keys are enforced for 24 hours.

Request Body

POST /v1/notifications/send
{
  "channel": "slack",
  "to": "team_engineering",
  "message": "CPU usage above 90% on prod-server-3",
  "severity": "warning",
  "template_id": "tpl_cpu_alert",
  "metadata": {
    "server": "prod-3",
    "metric": "cpu",
    "value": 92,
    "threshold": 90
  },
  "priority": "high",
  "idempotency_key": "cpu_alert_prod3_20260414"
}

Response 201 Created

response.json
{
  "id": "ntf_a1b2c3d4e5f6",
  "object": "notification",
  "status": "queued",
  "channel": "slack",
  "to": "team_engineering",
  "message": "CPU usage above 90% on prod-server-3",
  "severity": "warning",
  "template_id": "tpl_cpu_alert",
  "metadata": {
    "server": "prod-3",
    "metric": "cpu",
    "value": 92,
    "threshold": 90
  },
  "priority": "high",
  "acknowledged": false,
  "idempotency_key": "cpu_alert_prod3_20260414",
  "credits_used": 1,
  "created_at": "2026-04-14T10:30:00Z"
}
When schedule_at is set, the alert is placed in queued status and can be cancelled before the scheduled time via DELETE /v1/notifications/:id. Non-scheduled notifications are processed immediately.
POST/v1/notifications/send-batch
Send up to 1,000 alerts in a single request. Each item in the array follows the same schema as the single send endpoint. Partial failures are reported per-item.

Request Body Parameters

ParameterTypeRequiredDescription
notificationsarrayRequiredArray of notification objects (max 1,000). Each object has the same shape as the /send endpoint body.

Request Body

POST /v1/notifications/send-batch
{
  "notifications": [
    {
      "channel": "email",
      "to": "oncall@company.com",
      "template_id": "tpl_incident_report",
      "metadata": { "incident_id": "INC-482", "severity": "critical" }
    },
    {
      "channel": "push",
      "to": "team_oncall",
      "message": "P1 Incident: Payment service degraded. Error rate: 12%.",
      "priority": "high",
      "severity": "critical"
    },
    {
      "channel": "slack",
      "to": "#incidents",
      "message": "Deploy v2.4.1 completed successfully on production.",
      "metadata": { "service": "api-gateway", "version": "2.4.1" }
    },
    {
      "channel": "telegram",
      "to": "team_devops",
      "message": "CI/CD Pipeline: Build #4821 passed. Ready for deployment.",
      "severity": "info"
    },
    {
      "channel": "in-app",
      "to": "team_engineering",
      "message": "Scheduled maintenance window starts in 30 minutes.",
      "metadata": { "deep_link": "/dashboard/maintenance" }
    }
  ]
}

Response 200 OK

response.json
{
  "object": "batch",
  "id": "bat_q1w2e3r4t5",
  "total": 5,
  "accepted": 5,
  "rejected": 0,
  "notifications": [
    { "id": "ntf_bat_001", "status": "queued", "channel": "email" },
    { "id": "ntf_bat_002", "status": "queued", "channel": "push" },
    { "id": "ntf_bat_003", "status": "queued", "channel": "slack" },
    { "id": "ntf_bat_004", "status": "queued", "channel": "telegram" },
    { "id": "ntf_bat_005", "status": "queued", "channel": "in-app" }
  ],
  "created_at": "2026-04-14T10:35:00Z"
}
Batch requests are atomic at the validation level — if any item has invalid parameters, the entire batch is rejected with a 422 error. Once accepted, each notification is processed independently and may succeed or fail on its own.
GET/v1/notifications/:id
Retrieve the full details and delivery status of a specific notification by its ID. Includes timestamps for every lifecycle state transition.

Path Parameters

ParameterTypeRequiredDescription
idstringRequiredThe notification ID (starts with "ntf_").

Response 200 OK

response.json
{
  "id": "ntf_a1b2c3d4e5f6",
  "object": "notification",
  "status": "delivered",
  "channel": "slack",
  "to": "team_engineering",
  "message": "CPU usage above 90% on prod-server-3",
  "severity": "warning",
  "template_id": null,
  "metadata": {
    "server": "prod-3",
    "metric": "cpu",
    "value": 92
  },
  "priority": "high",
  "acknowledged": true,
  "acknowledged_by": "eng_sarah",
  "acknowledged_at": "2026-04-14T10:32:00Z",
  "credits_used": 1,
  "created_at": "2026-04-14T10:30:00Z",
  "sent_at": "2026-04-14T10:30:01Z",
  "delivered_at": "2026-04-14T10:30:03Z",
  "read_at": null,
  "failed_at": null,
  "failure_reason": null
}
GET/v1/notifications
List alerts with optional filters. Results are returned in reverse chronological order using cursor-based pagination.

Query Parameters

ParameterTypeRequiredDescription
channelstringOptionalFilter by channel: whatsapp, telegram, email, push, slack, in-app.
statusstringOptionalFilter by delivery status: queued, sent, delivered, read, failed, cancelled.
fromstringOptionalStart of date range (ISO 8601). Inclusive.
tostringOptionalEnd of date range (ISO 8601). Inclusive.
cursorstringOptionalPagination cursor returned by a previous request.
limitintegerOptionalNumber of results per page. Min 1, max 100, default 25.

Example Request

cURL
curl "https://api.prolayer.io/v1/notifications?channel=slack&status=delivered&limit=2" \
  -H "Authorization: Bearer sk_live_your_api_key_here"

Response 200 OK

response.json
{
  "object": "list",
  "data": [
    {
      "id": "ntf_a1b2c3d4e5f6",
      "object": "notification",
      "status": "delivered",
      "channel": "slack",
      "to": "team_engineering",
      "message": "CPU usage above 90% on prod-server-3",
      "severity": "warning",
      "acknowledged": true,
      "credits_used": 1,
      "created_at": "2026-04-14T10:30:00Z",
      "delivered_at": "2026-04-14T10:30:03Z"
    },
    {
      "id": "ntf_g7h8i9j0k1l2",
      "object": "notification",
      "status": "delivered",
      "channel": "telegram",
      "to": "team_devops",
      "message": "Deploy v2.4.0 completed on staging",
      "severity": "info",
      "acknowledged": false,
      "credits_used": 1,
      "created_at": "2026-04-14T10:25:00Z",
      "delivered_at": "2026-04-14T10:25:02Z"
    }
  ],
  "has_more": true,
  "next_cursor": "cur_ntf_g7h8i9j0k1l2"
}
DELETE/v1/notifications/:id
Cancel a scheduled alert that has not yet been sent. Only alerts in queued status can be cancelled. Already-sent alerts return a 409 error.

Path Parameters

ParameterTypeRequiredDescription
idstringRequiredThe notification ID to cancel (starts with "ntf_").

Response 200 OK

response.json
{
  "id": "ntf_a1b2c3d4e5f6",
  "object": "notification",
  "status": "cancelled",
  "cancelled_at": "2026-04-14T11:00:00Z"
}
Cancellation is best-effort for notifications nearing their scheduled time. If the notification enters the delivery pipeline within the same second as your cancellation request, it may still be delivered.

Alert Templates

Create, manage, and version reusable alert templates with dynamic variable substitution. Ideal for standardized monitoring alerts, incident reports, and deployment notifications.

POST/v1/templates
Create a reusable alert template. Variables use double-brace syntax (e.g. {{name}}) and are automatically extracted from the body.

Request Body Parameters

ParameterTypeRequiredDescription
namestringRequiredUnique template name. Alphanumeric, underscores, and hyphens only. Max 64 chars.
channelstringRequiredTarget channel: whatsapp, telegram, email, push, slack, in-app.
bodystringRequiredTemplate content with {{variable}} placeholders. Max 4,096 chars.
descriptionstringOptionalHuman-readable description for dashboard reference. Max 256 chars.
metadataobjectOptionalArbitrary key-value pairs for your own categorization and filtering.

Request Body

POST /v1/templates
{
  "name": "cpu_alert",
  "channel": "slack",
  "body": "🔴 CPU Alert on {{server}}\nCurrent: {{value}}% | Threshold: {{threshold}}%\nDuration: {{duration}}\nDashboard: {{dashboard_url}}",
  "description": "CPU threshold alert for infrastructure monitoring",
  "metadata": {
    "category": "monitoring",
    "team": "infrastructure"
  }
}

Response 201 Created

response.json
{
  "id": "tpl_x7y8z9w0",
  "object": "template",
  "name": "cpu_alert",
  "channel": "slack",
  "body": "🔴 CPU Alert on {{server}}\nCurrent: {{value}}% | Threshold: {{threshold}}%\nDuration: {{duration}}\nDashboard: {{dashboard_url}}",
  "variables": ["server", "value", "threshold", "duration", "dashboard_url"],
  "description": "CPU threshold alert for infrastructure monitoring",
  "metadata": {
    "category": "monitoring",
    "team": "infrastructure"
  },
  "usage_count": 0,
  "last_used_at": null,
  "created_at": "2026-04-14T08:00:00Z",
  "updated_at": "2026-04-14T08:00:00Z"
}
GET/v1/templates
List all templates in your account. Supports cursor-based pagination and optional channel filtering.

Query Parameters

ParameterTypeRequiredDescription
channelstringOptionalFilter templates by channel.
cursorstringOptionalPagination cursor from a previous response.
limitintegerOptionalResults per page. Min 1, max 100, default 25.

Response 200 OK

response.json
{
  "object": "list",
  "data": [
    {
      "id": "tpl_x7y8z9w0",
      "object": "template",
      "name": "cpu_alert",
      "channel": "slack",
      "description": "CPU threshold alert for infrastructure monitoring",
      "variables": ["server", "value", "threshold", "duration", "dashboard_url"],
      "usage_count": 1420,
      "created_at": "2026-04-14T08:00:00Z"
    },
    {
      "id": "tpl_m3n4o5p6",
      "object": "template",
      "name": "incident_escalation",
      "channel": "email",
      "description": "Incident escalation report sent to on-call team",
      "variables": ["incident_id", "severity", "summary", "responder"],
      "usage_count": 8903,
      "created_at": "2026-04-13T15:20:00Z"
    },
    {
      "id": "tpl_a9b8c7d6",
      "object": "template",
      "name": "deployment_complete",
      "channel": "slack",
      "description": "Post-deploy alert to engineering team",
      "variables": ["service", "version", "environment", "deployer"],
      "usage_count": 347,
      "created_at": "2026-04-10T11:05:00Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
GET/v1/templates/:id
Retrieve the full details of a template, including its body, extracted variables, and usage statistics.

Path Parameters

ParameterTypeRequiredDescription
idstringRequiredThe template ID (starts with "tpl_").

Response 200 OK

response.json
{
  "id": "tpl_x7y8z9w0",
  "object": "template",
  "name": "cpu_alert",
  "channel": "slack",
  "body": "🔴 CPU Alert on {{server}}\nCurrent: {{value}}% | Threshold: {{threshold}}%\nDuration: {{duration}}\nDashboard: {{dashboard_url}}",
  "variables": ["server", "value", "threshold", "duration", "dashboard_url"],
  "description": "CPU threshold alert for infrastructure monitoring",
  "metadata": {
    "category": "monitoring",
    "team": "infrastructure"
  },
  "usage_count": 1420,
  "last_used_at": "2026-04-14T09:55:00Z",
  "created_at": "2026-04-14T08:00:00Z",
  "updated_at": "2026-04-14T08:00:00Z"
}
PUT/v1/templates/:id
Update an existing template. Only the fields you include in the request body will be modified — all other fields remain unchanged. Variable extraction is re-run automatically when the body changes.

Path Parameters

ParameterTypeRequiredDescription
idstringRequiredThe template ID to update.

Request Body Parameters

ParameterTypeRequiredDescription
namestringOptionalNew template name.
bodystringOptionalNew template body. Variables are re-extracted automatically.
descriptionstringOptionalUpdated description.
metadataobjectOptionalReplaces the entire metadata object (not merged).

Request Body

PUT /v1/templates/tpl_x7y8z9w0
{
  "body": "🔴 CPU Alert on {{server}}\nCurrent: {{value}}% | Threshold: {{threshold}}%\nDuration: {{duration}}\nRunbook: {{runbook_url}}\nDashboard: {{dashboard_url}}",
  "description": "Updated CPU alert with runbook link"
}

Response 200 OK

response.json
{
  "id": "tpl_x7y8z9w0",
  "object": "template",
  "name": "cpu_alert",
  "channel": "slack",
  "body": "🔴 CPU Alert on {{server}}\nCurrent: {{value}}% | Threshold: {{threshold}}%\nDuration: {{duration}}\nRunbook: {{runbook_url}}\nDashboard: {{dashboard_url}}",
  "variables": ["server", "value", "threshold", "duration", "runbook_url", "dashboard_url"],
  "description": "Updated CPU alert with runbook link",
  "metadata": {
    "category": "monitoring",
    "team": "infrastructure"
  },
  "usage_count": 1420,
  "last_used_at": "2026-04-14T09:55:00Z",
  "created_at": "2026-04-14T08:00:00Z",
  "updated_at": "2026-04-14T12:15:00Z"
}
Updating a template does not affect alerts that have already been sent or are currently queued. Only future alerts referencing this template will use the new body.
DELETE/v1/templates/:id
Permanently delete a template. Alerts currently queued with this template will still be delivered using the template body at the time they were created.

Path Parameters

ParameterTypeRequiredDescription
idstringRequiredThe template ID to delete.

Response 200 OK

response.json
{
  "id": "tpl_x7y8z9w0",
  "object": "template",
  "deleted": true
}

Account

Retrieve your credit balance and detailed usage analytics.

GET/v1/balance
Retrieve your current credit balance, usage for today and the current billing cycle, plan details, and the next reset date.

Response 200 OK

response.json
{
  "object": "balance",
  "credits_remaining": 8742,
  "credits_used_today": 258,
  "credits_used_this_month": 4521,
  "plan": "growth",
  "credits_included": 10000,
  "overage_rate": 0.003,
  "overage_credits_used": 0,
  "next_reset": "2026-05-01T00:00:00Z",
  "last_updated": "2026-04-14T10:30:00Z"
}
When credits_remaining reaches zero on the Starter plan, subsequent sends will return 402 Payment Required. Growth and Business plans continue sending at the overage_rate per credit.
GET/v1/usage
Retrieve detailed usage statistics for a given date range, broken down by channel and time period. Useful for building internal dashboards and cost tracking.

Query Parameters

ParameterTypeRequiredDescription
fromstringRequiredStart of date range (ISO 8601). Example: 2026-04-01T00:00:00Z
tostringRequiredEnd of date range (ISO 8601). Example: 2026-04-14T23:59:59Z
granularitystringOptionalTime bucket size for the breakdown array. One of: "hour", "day". Defaults to "day".
channelstringOptionalFilter stats to a single channel.

Example Request

cURL
curl "https://api.prolayer.io/v1/usage?from=2026-04-01T00:00:00Z&to=2026-04-14T23:59:59Z&granularity=day" \
  -H "Authorization: Bearer sk_live_your_api_key_here"

Response 200 OK

response.json
{
  "object": "usage",
  "from": "2026-04-01T00:00:00Z",
  "to": "2026-04-14T23:59:59Z",
  "total_sent": 4521,
  "total_delivered": 4389,
  "total_failed": 132,
  "total_credits_used": 4521,
  "delivery_rate": 0.9708,
  "by_channel": {
    "whatsapp": { "sent": 1823, "delivered": 1790, "failed": 33, "credits": 1823 },
    "email":    { "sent": 1245, "delivered": 1200, "failed": 45, "credits": 1245 },
    "push":     { "sent": 876,  "delivered": 842,  "failed": 34, "credits": 876 },
    "telegram": { "sent": 312,  "delivered": 305,  "failed": 7,  "credits": 312 },
    "slack":    { "sent": 198,  "delivered": 192,  "failed": 6,  "credits": 198 },
    "in-app":   { "sent": 67,   "delivered": 60,   "failed": 7,  "credits": 67 }
  },
  "daily": [
    {
      "date": "2026-04-14",
      "sent": 258,
      "delivered": 251,
      "failed": 7,
      "credits": 258
    },
    {
      "date": "2026-04-13",
      "sent": 312,
      "delivered": 304,
      "failed": 8,
      "credits": 312
    },
    {
      "date": "2026-04-12",
      "sent": 287,
      "delivered": 280,
      "failed": 7,
      "credits": 287
    }
  ]
}
The daily array is truncated in the example above. The actual response includes one entry per day (or per hour when granularity=hour) for the entire requested range. Maximum queryable range is 90 days.

Webhooks

Subscribe to real-time alert delivery and acknowledgment events. Webhook payloads are signed with HMAC-SHA256 for verification. Use webhooks to build automated escalation workflows.

POST/v1/webhooks
Register a new webhook endpoint. ProLayer will send POST requests to your URL for each subscribed event. Failed deliveries are retried with exponential backoff for up to 72 hours.

Request Body Parameters

ParameterTypeRequiredDescription
urlstringRequiredThe HTTPS endpoint URL that will receive webhook events. Must be publicly accessible.
eventsstring[]RequiredArray of event types to subscribe to. Use ["*"] for all events.
secretstringOptionalYour signing secret for HMAC-SHA256 verification. Auto-generated if omitted.
descriptionstringOptionalHuman-readable label for this endpoint.
metadataobjectOptionalArbitrary key-value pairs for your reference.

Request Body

POST /v1/webhooks
{
  "url": "https://api.yourapp.com/webhooks/prolayer",
  "events": [
    "alert.delivered",
    "alert.acknowledged",
    "alert.failed"
  ],
  "secret": "whsec_k8m2n4p6q8r0s2t4v6w8x0y2z4",
  "description": "Production alert webhook endpoint",
  "metadata": {
    "environment": "production",
    "team": "backend"
  }
}

Response 201 Created

response.json
{
  "id": "whk_p1q2r3s4t5u6",
  "object": "webhook",
  "url": "https://api.yourapp.com/webhooks/prolayer",
  "events": [
    "alert.delivered",
    "alert.acknowledged",
    "alert.failed"
  ],
  "status": "active",
  "description": "Production alert webhook endpoint",
  "metadata": {
    "environment": "production",
    "team": "backend"
  },
  "signing_secret": "whsec_k8m2n4p6q8r0s2t4v6w8x0y2z4",
  "created_at": "2026-04-14T10:00:00Z"
}

Webhook Payload Example

POST https://api.yourapp.com/webhooks/prolayer
{
  "id": "evt_v1w2x3y4z5",
  "object": "event",
  "type": "alert.delivered",
  "created_at": "2026-04-14T10:30:03Z",
  "data": {
    "id": "ntf_a1b2c3d4e5f6",
    "object": "notification",
    "status": "delivered",
    "channel": "slack",
    "to": "team_engineering",
    "severity": "warning",
    "acknowledged": false,
    "delivered_at": "2026-04-14T10:30:03Z",
    "latency_ms": 2140
  }
}
Verify webhook signatures by computing HMAC-SHA256(signing_secret, raw_body) and comparing it to the X-ProLayer-Signature header. Always use a constant-time comparison function to prevent timing attacks.
GET/v1/webhooks
List all registered webhook endpoints. Includes their status, subscribed events, and creation timestamps.

Query Parameters

ParameterTypeRequiredDescription
statusstringOptionalFilter by status: "active" or "disabled".
cursorstringOptionalPagination cursor.
limitintegerOptionalResults per page. Min 1, max 100, default 25.

Response 200 OK

response.json
{
  "object": "list",
  "data": [
    {
      "id": "whk_p1q2r3s4t5u6",
      "object": "webhook",
      "url": "https://api.yourapp.com/webhooks/prolayer",
      "events": ["alert.delivered", "alert.acknowledged", "alert.failed"],
      "status": "active",
      "description": "Production webhook endpoint",
      "created_at": "2026-04-14T10:00:00Z"
    },
    {
      "id": "whk_j7k8l9m0n1o2",
      "object": "webhook",
      "url": "https://staging.yourapp.com/hooks/prolayer",
      "events": ["*"],
      "status": "active",
      "description": "Staging — all events",
      "created_at": "2026-03-28T14:20:00Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
The signing_secret is only returned when creating a webhook. If you need to retrieve it later, rotate the secret from the dashboard.
DELETE/v1/webhooks/:id
Delete a webhook endpoint. Any in-flight deliveries to this endpoint will be abandoned. Pending retries are cancelled immediately.

Path Parameters

ParameterTypeRequiredDescription
idstringRequiredThe webhook endpoint ID (starts with "whk_").

Response 200 OK

response.json
{
  "id": "whk_p1q2r3s4t5u6",
  "object": "webhook",
  "deleted": true
}

Pagination

All list endpoints use cursor-based pagination for consistent, performant traversal of large datasets.

Unlike offset-based pagination, cursor-based pagination guarantees stable results even when new records are inserted between page requests. Every list response includes:

FieldTypeDescription
dataarrayThe page of results, ordered by creation time (newest first).
has_morebooleanWhether additional pages exist beyond this one.
next_cursorstring | nullOpaque cursor string to pass as the cursor query parameter on the next request. null when there are no more pages.
Paginated fetch example (JavaScript)
async function fetchAllNotifications(apiKey) {
  const results = [];
  let cursor = null;

  do {
    const url = new URL("https://api.prolayer.io/v1/notifications");
    url.searchParams.set("limit", "100");
    if (cursor) url.searchParams.set("cursor", cursor);

    const res = await fetch(url, {
      headers: { Authorization: `Bearer ${apiKey}` },
    });
    const page = await res.json();

    results.push(...page.data);
    cursor = page.next_cursor;
  } while (cursor);

  return results;
}
Cursors are opaque strings — do not attempt to parse, modify, or construct them. They are valid for 24 hours after issuance. After expiry, start a new traversal from the beginning.

Error Codes

All errors follow a consistent JSON structure with a machine-readable code and a human-readable message.

Error response shape
{
  "error": {
    "type": "invalid_request_error",
    "code": "missing_required_field",
    "message": "The 'channel' field is required.",
    "param": "channel",
    "doc_url": "https://prolayer.io/docs/api#send-notification"
  }
}
StatusTypeDescription
400invalid_request_errorThe request body is malformed, missing required fields, or contains invalid values.
401authentication_errorNo API key provided, or the key is invalid or revoked.
402payment_requiredYour credit balance is exhausted and your plan does not allow overage.
403forbiddenThe API key does not have permission for this action (e.g., test key used on a production-only endpoint).
404not_foundThe requested resource (notification, template, webhook) does not exist.
409conflictIdempotency key already used for a different request body, or attempting to cancel an already-sent notification.
422validation_errorRequest is well-formed but semantically invalid (e.g., invalid phone number format, unknown channel).
429rate_limit_errorToo many requests. Check the Retry-After header for backoff duration in seconds.
500api_errorAn unexpected error on our side. These are automatically reported and investigated.
502upstream_errorAn upstream delivery provider (WhatsApp Business API, Telegram Bot API, etc.) returned an error.
503service_unavailableThe API is temporarily unavailable due to maintenance or overload. Retry with exponential backoff.
For 429 and 503 errors, always respect the Retry-After header. We recommend exponential backoff with jitter: wait min(2^attempt * 1000 + random(0, 1000), 30000) milliseconds between retries, up to 5 attempts.

Rate Limits

Rate limits protect the API from abuse and ensure fair usage across all teams.

PlanRequests / minBurst (10s window)Notifications / dayBatch size
Starter60151,000100
Growth3007550,000500
Business1,000250500,0001,000
EnterpriseCustomCustomUnlimited1,000

Rate limit status is returned in every response via these headers:

HeaderDescription
X-RateLimit-LimitMaximum requests allowed per minute for your plan.
X-RateLimit-RemainingRequests remaining in the current window.
X-RateLimit-ResetUnix timestamp when the current window resets.
Retry-AfterSeconds to wait before retrying (only present on 429 responses).
Rate limit exceeded response
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1745667600
Retry-After: 42

{
  "error": {
    "type": "rate_limit_error",
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded. Retry after 42 seconds.",
    "doc_url": "https://prolayer.io/docs/api#rate-limits"
  }
}

Alert Statuses

Every alert progresses through a defined lifecycle. These statuses appear in the status field of alert objects.

StatusTerminal?Description
queuedNoAlert accepted and waiting in the processing queue. Scheduled alerts remain in this state until their delivery time.
sentNoHanded off to the downstream delivery provider (e.g., Slack API, Telegram Bot API). Awaiting provider confirmation.
deliveredNoThe provider confirmed the alert was delivered to the team member's device, channel, or inbox.
acknowledgedYesA team member acknowledged the alert via an action button, API call, or dashboard. Includes acknowledged_by and acknowledged_at fields.
readYesThe team member opened or viewed the alert. Only available on channels that support read receipts (WhatsApp, In-App, Email with pixel tracking).
failedYesDelivery failed permanently. Check the "failure_reason" field for details (e.g., unapproved recipient, provider outage, content policy violation).
cancelledYesThe alert was cancelled via DELETE /v1/notifications/:id before it left the queue.

Status Flow

lifecycle
queued → sent → delivered → acknowledged
                          ↘ read
                ↘ failed
queued → cancelled
The read status is best-effort and depends on the channel's support for read receipts. Not all channels or recipients report reads. Do not use read status for critical business logic.

Webhook Events

Subscribe to any combination of these events when creating a webhook endpoint.

Event TypeCategoryDescription
alert.queuedAlertAn alert was accepted and placed in the processing queue.
alert.sentAlertAn alert was handed off to the downstream delivery provider.
alert.deliveredAlertAn alert was confirmed delivered to the approved team member.
alert.acknowledgedAlertAn alert was acknowledged by a team member. Includes acknowledged_by in the payload.
alert.readAlertAn alert was opened or viewed by the team member.
alert.failedAlertAn alert failed to deliver. Includes failure_reason in the payload.
alert.cancelledAlertA scheduled alert was cancelled before delivery.
template.createdTemplateA new template was created.
template.updatedTemplateAn existing template was modified.
template.deletedTemplateA template was permanently deleted.
balance.lowAccountCredit balance dropped below 10% of your plan's included credits.
balance.depletedAccountCredit balance reached zero. Subsequent sends may fail on the Starter plan.

Example: alert.failed event payload

webhook-payload.json
{
  "id": "evt_f1g2h3i4j5",
  "object": "event",
  "type": "alert.failed",
  "created_at": "2026-04-14T10:31:05Z",
  "data": {
    "id": "ntf_k6l7m8n9o0",
    "object": "notification",
    "status": "failed",
    "channel": "slack",
    "to": "team_oncall",
    "message": "P1 Incident: Payment service errors above 5%",
    "severity": "critical",
    "failure_reason": "recipient_not_approved",
    "failure_details": "The recipient team_oncall contains members who are not approved contacts. Update your Approved Contacts list.",
    "failed_at": "2026-04-14T10:31:05Z",
    "created_at": "2026-04-14T10:31:00Z"
  }
}

Example: balance.low event payload

webhook-payload.json
{
  "id": "evt_z9y8x7w6v5",
  "object": "event",
  "type": "balance.low",
  "created_at": "2026-04-14T18:00:00Z",
  "data": {
    "object": "balance",
    "credits_remaining": 847,
    "credits_included": 10000,
    "percentage_remaining": 8.47,
    "plan": "growth",
    "next_reset": "2026-05-01T00:00:00Z"
  }
}
Use "events": ["*"] when creating a webhook to subscribe to all current and future event types. This is useful for logging and audit trail endpoints.

SDKs & Libraries

Official client libraries wrap this REST API with idiomatic interfaces, automatic retries, and type safety.

JSNode.js / TypeScript
npm install @prolayer/node
PYPython
pip install prolayer
GOGo
go get github.com/prolayer/prolayer-go
RBRuby
gem install prolayer
PHPPHP
composer require prolayer/prolayer-php
JVJava / Kotlin
implementation 'io.prolayer:prolayer-java:1.x'

Quickstart Snippets

Copy-paste examples to send your first team alert in seconds.

Node.js / TypeScript
send.ts
import ProLayer from "@prolayer/node";

const prolayer = new ProLayer("sk_live_your_api_key_here");

const alert = await prolayer.notifications.send({
  channel: "slack",
  to: "#incidents",
  message: "CPU usage above 90% on prod-server-3",
  severity: "warning",
  metadata: {
    server: "prod-3",
    metric: "cpu",
    value: 92,
  },
});

console.log(alert.id);     // ntf_a1b2c3d4e5f6
console.log(alert.status); // queued
Python
send.py
import prolayer

client = prolayer.Client("sk_live_your_api_key_here")

alert = client.notifications.send(
    channel="email",
    to="oncall@company.com",
    template_id="tpl_incident_report",
    severity="critical",
    metadata={
        "incident_id": "INC-482",
        "severity": "critical",
        "summary": "Payment service error rate above 5%",
    },
)

print(alert.id)      # ntf_a1b2c3d4e5f6
print(alert.status)  # queued
cURL
terminal
curl -X POST https://api.prolayer.io/v1/notifications/send \
  -H "Authorization: Bearer sk_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "telegram",
    "to": "team_devops",
    "message": "Deploy v2.4.1 completed. All health checks passing.",
    "severity": "info",
    "metadata": {
      "service": "api-gateway",
      "version": "2.4.1",
      "environment": "production"
    }
  }'

Idempotency

Safely retry requests without creating duplicate alerts.

Pass an idempotency_key in the request body of any POST endpoint. If a request with the same key was already processed successfully, the API returns the original response with a 200 status instead of creating a duplicate.

ScenarioStatusBehavior
First request201Alert created normally.
Retry with same key & body200Returns the original alert. No duplicate created.
Same key, different body409Conflict — the key is already associated with a different request body.
Idempotent request
curl -X POST https://api.prolayer.io/v1/notifications/send \
  -H "Authorization: Bearer sk_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "slack",
    "to": "team_engineering",
    "message": "Deploy v2.4.1 completed on production",
    "severity": "info",
    "idempotency_key": "deploy_v241_prod_20260414"
  }'
Idempotency keys are scoped to your API key and expire after 24 hours. After expiry, the same key can be reused for a new request. We recommend including a timestamp or unique event ID in your key to ensure uniqueness (e.g., cpu_alert_prod3_20260414).

Versioning & Stability

How we evolve the API without breaking your integration.

VersionStatusNotes
v1StableCurrent version. All endpoints documented on this page. Breaking changes will only be introduced in a new major version.

We follow these stability guarantees for v1:

  • Existing fields will not be removed or have their type changed.
  • New optional fields may be added to response objects at any time.
  • New optional query/body parameters may be added to requests.
  • New event types may be added to webhooks (use * to future-proof).
  • Deprecations are announced at least 6 months before removal via the Sunset header and the changelog.

Support & SLAs

Get help when you need it.

ChannelResponse TimeAvailability
DocumentationSelf-serve24/7
Community (Discord)< 4 hoursBusiness hours
Email Support< 24 hoursAll plans
Priority Support< 2 hoursGrowth & above
Dedicated Slack Channel< 30 minutesEnterprise only

Start building with ProLayer

Create your free account to get API keys and send your first team alert in under 5 minutes.