Skip to content
Norsk Sign in

For external systems and agents

Send status, traffic and events to Manager

Manager receives telemetry from websites, apps, APIs, scheduled jobs and AI agents, and turns it into status, freshness monitoring, thresholds and incidents. The whole integration contract is on this page. You need one secret from a human — the rest you can work out yourself.

You send
POST https://monitor.skjldlabs.com/api/v1/ingest/{SOURCE_ID}
Authorization: Bearer {SOURCE_TOKEN}
Content-Type: application/json

{
    "idempotency_key": "checkout-health-2026-08-08T13:00",
    "type": "heartbeat",
    "status": "healthy",
    "severity": "info",
    "title": "Checkout health",
    "message": "p95 over the last five minutes",
    "latency_ms": 184,
    "metric": {
        "name": "http.latency.p95",
        "value": 184,
        "unit": "ms"
    },
    "tags": {
        "environment": "production",
        "region": "eu-north-1"
    },
    "data": {
        "requests": 1294,
        "version": "4.12.0"
    },
    "occurred_at": "2026-08-08T13:00:00Z"
}
Manager replies 202
{
    "accepted": true,
    "event_id": "01KZ9F3M2V7X0T4B8Q6R1Y5C3D",
    "duplicate": false,
    "source_status": "healthy",
    "received_at": "2026-08-08T13:00:01+00:00"
}
Read the state back

GET /api/v1/ingest/{SOURCE_ID}/status

{
    "source": {
        "id": "01KZ8SET41J71MQ261V8N8J2Z1",
        "name": "Checkout health",
        "type": "health",
        "status": "healthy",
        "last_received_at": "2026-08-08T13:00:01+00:00",
        "next_expected_at": "2026-08-08T13:06:01+00:00",
        "expected_interval_seconds": 300
    }
}

Same token. This tells you when the next report is expected — miss that window on several consecutive checks and an incident opens.

The model

You always send to a data source, never to a service.

Tenant

The customer or organisation. Owns everything beneath it, and all data is isolated per tenant.

Service

One website, app, API or other digital product.

Data source

One authenticated feed with its own token, its own declared contract and its own expected frequency. This is the address you send to.

What you need before you start

One registration token, created by an owner or admin in the dashboard under "API & MCP". It is bound to one tenant and one stable service key, and it is the only thing you cannot obtain yourself.

Three steps

  1. 01

    Declare yourself

    POST a versioned manifest with the registration token. The manifest describes the service and every data source: reporting frequency, metrics, event types, tags and data fields.

  2. 02

    Take the keys

    The response carries one single-use token per new data source, along with its REST and MCP address. Store them immediately — they are never shown again.

  3. 03

    Send data

    Use the source token against REST or MCP. Both store the same events and drive the same rules.

The manifest

Idempotent. Send it again whenever your service changes: new sources are created, sources you stop declaring are paused with their history intact, and existing tokens are left alone.

POST /api/v1/register
{
    "manifest_version": "1.0",
    "service": {
        "key": "customer-portal-production",
        "name": "Customer portal",
        "environment": "production",
        "url": "https://portal.example.com",
        "version": "4.12.0",
        "accent": "emerald",
        "tags": [
            "laravel",
            "eu-north-1"
        ]
    },
    "sources": [
        {
            "key": "web-analytics",
            "name": "Web analytics",
            "type": "analytics",
            "description": "Traffic aggregated over five-minute windows.",
            "expected_interval_seconds": 300,
            "grace_seconds": 300,
            "metrics": [
                {
                    "name": "analytics.pageviews",
                    "unit": "count",
                    "aggregation": "sum",
                    "description": "Pageviews in the window."
                },
                {
                    "name": "analytics.users",
                    "unit": "count",
                    "aggregation": "max",
                    "description": "Unique visitors in the window."
                }
            ],
            "events": [
                {
                    "type": "analytics.top_pages",
                    "severity": "info",
                    "description": "Most visited pages in the window."
                }
            ],
            "tags": [
                "site",
                "channel"
            ],
            "data_fields": [
                {
                    "name": "top_path",
                    "type": "string",
                    "required": false,
                    "description": "Most visited path in the window."
                }
            ]
        }
    ]
}

Rules

manifest_version
MAJOR.MINOR
service.key
Lowercase kebab-case. Must match the key the registration token is bound to.
service.environment
production · staging · development
sources[].key
The stable identity of the feed. Unique within the manifest.
sources[].type
health · analytics · contacts · security · laravel-log · performance · custom
sources[].expected_interval_seconds
60 – 2 592 000
sources[].grace_seconds
0 – 86 400
metrics[].aggregation
last · sum · avg · min · max · p50 · p95 · p99
events[].severity
info · warning · error · critical

Be honest about expected_interval_seconds. Manager opens a "no new data" incident when a feed misses its own window on several consecutive checks. Declaring a cadence you do not keep produces noise, not safety.

The response — tokens are shown once
{
    "registered": true,
    "manifest_version": "1.0",
    "service": {
        "id": "01K…",
        "key": "customer-portal-production"
    },
    "sources": [
        {
            "key": "web-analytics",
            "id": "01KZ8SET41J71MQ261V8N8J2Z1",
            "token": "mgr_live_…shown_once",
            "ingest_url": "https://monitor.skjldlabs.com/api/v1/ingest/01KZ8SET41J71MQ261V8N8J2Z1",
            "mcp_url": "https://monitor.skjldlabs.com/mcp/01KZ8SET41J71MQ261V8N8J2Z1"
        }
    ]
}

REST

HTTP

Every field is optional. Send what you have.

Field Type Notes
type string Free-form kind of report, for example heartbeat, metric, event or log. Max 40 characters.
status string Service state. down, failed, critical and error open an incident; up, ok, healthy and operational resolve one. Max 40 characters.
severity enum One of info, warning, error, critical. Defaults to info.
title string Short headline shown in the event list. Max 255 characters.
message string Longer context. Max 10 000 characters.
latency_ms integer Measured latency, 0 to 3 600 000.
metric.name string Required when metric is present. Stable dot-separated name, max 120 characters.
metric.value number Required when metric is present.
metric.unit string For example ms, percent, count or bytes. Max 32 characters.
tags object Up to 30 string values. Use for dimensions you want to filter on, such as environment or region.
data object Up to 100 structured values kept with the event.
occurred_at string ISO 8601. Defaults to now and may not be more than five minutes in the future.
idempotency_key string Repeating a key for the same source returns the original event instead of storing a duplicate. Max 190 characters.

Append /batch to the URL and send an events array for up to 100 events in one request.

MCP

Streamable HTTP

Every data source is also a Streamable HTTP MCP server, using the same token. Clients discover the tools automatically.

Tool Arguments
report_heartbeat Report that the service is alive, with its current status and optional latency. status, message, latency_ms, idempotency_key
report_event Report an operational event such as a deploy, a failure or a signup. type, title, message, severity, status, tags, data, idempotency_key, occurred_at
report_metric Report a numeric metric such as latency, users, errors, conversions or queue depth. name, value, unit, severity, message, idempotency_key, occurred_at
get_source_status Read back the current status of the authenticated source. none
Resource manager://source/recent-events — The most recent events already stored for the authenticated source.
Client configuration
{
    "mcpServers": {
        "manager-production-health": {
            "type": "http",
            "url": "https://monitor.skjldlabs.com/mcp/{SOURCE_ID}",
            "headers": {
                "Authorization": "Bearer {SOURCE_TOKEN}"
            }
        }
    }
}

How often to report

Source type Suggested interval Note
health 60s A heartbeat every minute gives the fastest possible outage detection.
performance 300s Aggregate latency and error rates over the window before sending.
analytics 300s Aggregate first. Never send one event per pageview.
security on event Send when something happens, and a heartbeat in between so silence is distinguishable from calm.
contacts on event Send each submission as it arrives.
laravel-log on event Send warnings and above. Batch to stay inside the rate limit.

Status and severity

status drives incidents: down, failed, critical and error open one, while up, ok, healthy and operational resolve the matching one. severity only controls how loudly a stored event is presented, and opens nothing on its own.

healthy up ok degraded warning down critical

Limits

Requests per minute, per source
120
Registration requests per minute
30
Events per batch
100
Request body size
1.0 MB
Sources per manifest
20
Metrics per source
100
Declared event types per source
50
Raw event retention
90 days

Response codes

202
Accepted and stored.
200
Accepted, but the idempotency key matched an event already stored. Nothing was duplicated.
401
Missing or wrong bearer token, or an invalid or expired signature.
404
Unknown data source id.
413
Request body larger than 1.0 MB.
422
The payload failed validation. The body names the offending fields.
423
The data source is paused in the dashboard.
429
Rate limit exceeded for this source.

Retries and duplicates

Set idempotency_key to something stable and unique per logical report. Repeat the key for the same source and you get the original event back with HTTP 200 and nothing new is stored — so a retry after a timeout is safe.

Optional signing

A source can be set to require signatures. Then every request also needs:

X-Manager-Timestamp: {unix seconds}
X-Manager-Signature: hex(HMAC-SHA256(
    timestamp + "." + raw_body, source_token))

The timestamp must be within 300 seconds of server time.

What not to send

Keep secrets, credentials, session tokens and personal data out of title, message, tags and data. Telemetry is retained for 90 days and is visible to everyone with access to the tenant. Aggregate before you send: counts and percentiles, not rows.