# Observe API

The Observe area publishes 35 REST endpoints. Paths are relative to `https://api.lessly.com`; see [Authentication](/reference/openapi#authentication) for the required header.

### `GET` `/observe/alerts`

List the alert rules of this product, newest first, with what each one measures and whether it is firing right now.

MCP tool: `observe_alerts_list`

**Parameters**

No parameters.

**Response**

Returns `object[]`.

| Item field | Type | Always | Description |
| --- | --- | --- | --- |
| id | string | • | Identifier of the rule |
| kind | string one of: error_rate, log_match | • | What the rule measures |
| name | string | • | Human name the rule was registered under |
| state | string one of: ok, firing | • | firing means the condition is met right now |
| enabled | boolean | • | Whether the evaluator measures it every minute |
| condition | object | • | The kind’s parameters: threshold and window, plus q for log_match |
| createdAt | string | • | ISO 8601 creation timestamp |
| updatedAt | string | • | ISO 8601 timestamp of the last change |
| lastFiredAt | string | null | • | ISO 8601 time it last fired, or null |
| throttleMinutes | number | • | How long after firing the rule stays quiet |

### `POST` `/observe/alerts`

Create an alert rule for this product. error_rate counts error events in the window; log_match counts log lines matching a Lucene expression, the same one observe_logs_search takes. The rule fires when the count reaches the threshold and resolves when it drops back below it; throttleMinutes is how long it stays quiet after firing. An expression that cannot be parsed is rejected here rather than failing silently on every evaluation.

MCP tool: `observe_alerts_create`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| name | body | string | • | What to call the rule, up to 200 characters |
| kind | body | string one of: error_rate, log_match | • | error_rate counts error events in the window; log_match counts log lines matching q |
| condition | body | object | • | The kind’s parameters |
| enabled | body | boolean | — | false registers the rule without evaluating it |
| throttleMinutes | body | integer | — | How long after firing the rule stays quiet. Default 60, up to a week |

**Response**

Returns `object`.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| id | string | • | Identifier of the rule |
| kind | string one of: error_rate, log_match | • | What the rule measures |
| name | string | • | Human name the rule was registered under |
| state | string one of: ok, firing | • | firing means the condition is met right now |
| enabled | boolean | • | Whether the evaluator measures it every minute |
| condition | object | • | The kind’s parameters: threshold and window, plus q for log_match |
| createdAt | string | • | ISO 8601 creation timestamp |
| updatedAt | string | • | ISO 8601 timestamp of the last change |
| lastFiredAt | string | null | • | ISO 8601 time it last fired, or null |
| throttleMinutes | number | • | How long after firing the rule stays quiet |

### `DELETE` `/observe/alerts/:id`

Remove an alert rule and its firing history. To stop being alerted while keeping the history, set the rule disabled instead.

MCP tool: `observe_alerts_delete`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| id | path | string | • | Identifier of the rule |

**Response**

Returns `object`.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| id | string | • | Identifier of the removed rule |
| deleted | boolean one of: true | • | Always true; the call fails otherwise |

### `GET` `/observe/alerts/:id`

Read one alert rule of this product, with its current state and last firing.

MCP tool: `observe_alerts_get`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| id | path | string | • | Identifier of the rule |

**Response**

Returns `object`.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| id | string | • | Identifier of the rule |
| kind | string one of: error_rate, log_match | • | What the rule measures |
| name | string | • | Human name the rule was registered under |
| state | string one of: ok, firing | • | firing means the condition is met right now |
| enabled | boolean | • | Whether the evaluator measures it every minute |
| condition | object | • | The kind’s parameters: threshold and window, plus q for log_match |
| createdAt | string | • | ISO 8601 creation timestamp |
| updatedAt | string | • | ISO 8601 timestamp of the last change |
| lastFiredAt | string | null | • | ISO 8601 time it last fired, or null |
| throttleMinutes | number | • | How long after firing the rule stays quiet |

### `PATCH` `/observe/alerts/:id`

Edit an alert rule: its name, its condition, its throttle, or whether it is enabled. What the rule measures — its kind — is not editable; create a second rule instead. Omit a field to leave it alone.

MCP tool: `observe_alerts_update`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| id | path | string | • | Identifier of the rule to change |
| name | body | string | — | New name for the rule |
| condition | body | object | — | The kind’s parameters |
| enabled | body | boolean | — | false stops evaluation and keeps the rule |
| throttleMinutes | body | integer | — | New quiet period after a firing, in minutes |

**Response**

Returns `object`.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| id | string | • | Identifier of the rule |
| kind | string one of: error_rate, log_match | • | What the rule measures |
| name | string | • | Human name the rule was registered under |
| state | string one of: ok, firing | • | firing means the condition is met right now |
| enabled | boolean | • | Whether the evaluator measures it every minute |
| condition | object | • | The kind’s parameters: threshold and window, plus q for log_match |
| createdAt | string | • | ISO 8601 creation timestamp |
| updatedAt | string | • | ISO 8601 timestamp of the last change |
| lastFiredAt | string | null | • | ISO 8601 time it last fired, or null |
| throttleMinutes | number | • | How long after firing the rule stays quiet |

### `GET` `/observe/alerts/events`

List past and current firings for this product, newest first. A row with a null resolvedAt is an alert that is happening now. Each carries what the measure was and the condition as it stood at that moment.

MCP tool: `observe_alerts_events_list`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| ruleId | query | string | — | Only firings of this rule |
| limit | query | integer | — | Page size, 1..200 |
| offset | query | integer | — | Number of firings to skip |

**Response**

Returns `object[]`.

| Item field | Type | Always | Description |
| --- | --- | --- | --- |
| id | string | • | Identifier of the firing |
| ruleId | string | • | Rule that fired |
| firedAt | string | • | ISO 8601 time the condition was first met |
| measured | number | • | What the measure was when it fired |
| condition | object | • | The condition as it stood then, not as the rule reads today |
| resolvedAt | string | null | • | ISO 8601 time it cleared, or null while it is still firing |

### `GET` `/observe/artifacts`

List the source map artifacts stored for the current product, newest first. Filter by debug ID to see what is available for one build output.

MCP tool: `observe_artifacts_list`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| debugId | query | string | — | Only return artifacts carrying this debug ID; omit to list all of them |
| limit | query | integer | — | Maximum number of artifacts to return, 1..200 (default 50) |

**Response**

Returns `object[]`.

| Item field | Type | Always | Description |
| --- | --- | --- | --- |
| id | string | • | Identifier of the stored artifact |
| kind | string one of: source, sourcemap | • | Whether this is the minified source or its map |
| debugId | string | • | Debug ID the build stamped into the file and its source map |
| byteSize | number | • | Size of the stored artifact in bytes |
| fileName | string | • | Name the file was uploaded under, e.g. "app.min.js.map" |
| uploadedAt | string | • | ISO 8601 timestamp of the upload |

### `DELETE` `/observe/artifacts/:id`

Delete one stored artifact and the bytes behind it. An artifact belonging to another product is reported as not found.

MCP tool: `observe_artifacts_delete`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| id | path | string | • | Identifier of the artifact to delete. An artifact of another product is reported as not found |

**Response**

Returns `object`.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| id | string | • | Identifier of the stored artifact |
| kind | string one of: source, sourcemap | • | Whether this is the minified source or its map |
| debugId | string | • | Debug ID the build stamped into the file and its source map |
| byteSize | number | • | Size of the stored artifact in bytes |
| fileName | string | • | Name the file was uploaded under, e.g. "app.min.js.map" |
| uploadedAt | string | • | ISO 8601 timestamp of the upload |

### `GET` `/observe/artifacts/:issueId/resolve`

Resolve the stack trace of an issue's latest event against the stored source maps, turning minified frames into original file, function, line and column. Frames with no matching debug ID are returned unchanged: a partially resolved trace is the normal result.

MCP tool: `observe_artifacts_resolve`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| issueId | path | string | • | Issue whose latest event should have its stack resolved. An issue of another product is reported as not found |

**Response**

Returns `object`.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| frames | object[] | • | The stack, resolved where a source map matched |
| eventId | string | • | Identifier of the event the stack was taken from |
| issueId | string | • | The issue whose stack this is |

### `GET` `/observe/issues`

List error tracking issues of the current product, most recently seen first. Filter by status or assignee.

MCP tool: `observe_issues_list`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| status | query | string one of: unresolved, resolved, archived | — | Only return issues in this state; omit to list every state |
| assignee | query | string | — | Only return issues owned by this assignee |
| limit | query | integer | — | How many issues to return, most recently seen first. 1-200, default 50 |

**Response**

Returns `object[]`.

| Item field | Type | Always | Description |
| --- | --- | --- | --- |
| id | string | • | Identifier of the issue |
| title | string | • | Display title, set when the issue opened and never rewritten |
| status | string | • | unresolved, resolved or archived |
| culprit | string | null | • | Where the error appears to come from, or null |
| service | string | null | • | Service the most recent event came from, or null if none was reported |
| assignee | string | null | • | Who owns the issue, or null if unassigned |
| lastSeen | string | • | ISO 8601 timestamp of the most recent event |
| firstSeen | string | • | ISO 8601 timestamp of the first event |
| archivedAt | string | null | • | ISO 8601 archive timestamp, or null |
| eventCount | number | • | How many events have landed on this issue |
| resolvedAt | string | null | • | ISO 8601 resolution timestamp, or null |
| environment | string | null | • | Environment the issue was first seen in, or null |
| fingerprint | string | • | Stable grouping hash the issue is keyed on |
| lastEventId | string | null | • | Identifier of the most recent event that landed on the issue, or null for issues rolled up before this was recorded |
| lastRelease | string | null | • | Release the most recent event was reported from, or null if none was reported |
| regressedAt | string | null | • | ISO 8601 timestamp of the last regression, or null if it never regressed |
| groupingRule | string | • | Which rule of the ladder matched: explicit, stack, exception or message |
| exceptionType | string | null | • | Exception class of the first event, or null |
| groupingVersion | string | • | Grouping config version that produced the fingerprint; part of the issue identity |

### `GET` `/observe/issues/:id`

Read one error tracking issue by id, together with the resolved stack of its most recent event. Frames with no matching source map are returned unchanged, and an issue whose stack cannot be read still comes back — with a null stack.

MCP tool: `observe_issues_get`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| id | path | string | • | Identifier of the issue |

**Response**

Returns `object`.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| id | string | • | Identifier of the issue |
| stack | object | null | • | Stack of the most recent event, or null when the issue has no events yet or the stack could not be read |
| title | string | • | Display title, set when the issue opened and never rewritten |
| status | string | • | unresolved, resolved or archived |
| culprit | string | null | • | Where the error appears to come from, or null |
| service | string | null | • | Service the most recent event came from, or null if none was reported |
| assignee | string | null | • | Who owns the issue, or null if unassigned |
| lastSeen | string | • | ISO 8601 timestamp of the most recent event |
| firstSeen | string | • | ISO 8601 timestamp of the first event |
| archivedAt | string | null | • | ISO 8601 archive timestamp, or null |
| eventCount | number | • | How many events have landed on this issue |
| resolvedAt | string | null | • | ISO 8601 resolution timestamp, or null |
| environment | string | null | • | Environment the issue was first seen in, or null |
| fingerprint | string | • | Stable grouping hash the issue is keyed on |
| lastEventId | string | null | • | Identifier of the most recent event that landed on the issue, or null for issues rolled up before this was recorded |
| lastRelease | string | null | • | Release the most recent event was reported from, or null if none was reported |
| regressedAt | string | null | • | ISO 8601 timestamp of the last regression, or null if it never regressed |
| groupingRule | string | • | Which rule of the ladder matched: explicit, stack, exception or message |
| exceptionType | string | null | • | Exception class of the first event, or null |
| groupingVersion | string | • | Grouping config version that produced the fingerprint; part of the issue identity |

### `POST` `/observe/issues/:id/archive`

Archive an issue: it stops being reported and stays archived even when new events keep landing on it.

MCP tool: `observe_issues_archive`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| id | path | string | • | Identifier of the issue |

**Response**

Returns `object`.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| id | string | • | Identifier of the issue |
| title | string | • | Display title, set when the issue opened and never rewritten |
| status | string | • | unresolved, resolved or archived |
| culprit | string | null | • | Where the error appears to come from, or null |
| service | string | null | • | Service the most recent event came from, or null if none was reported |
| assignee | string | null | • | Who owns the issue, or null if unassigned |
| lastSeen | string | • | ISO 8601 timestamp of the most recent event |
| firstSeen | string | • | ISO 8601 timestamp of the first event |
| archivedAt | string | null | • | ISO 8601 archive timestamp, or null |
| eventCount | number | • | How many events have landed on this issue |
| resolvedAt | string | null | • | ISO 8601 resolution timestamp, or null |
| environment | string | null | • | Environment the issue was first seen in, or null |
| fingerprint | string | • | Stable grouping hash the issue is keyed on |
| lastEventId | string | null | • | Identifier of the most recent event that landed on the issue, or null for issues rolled up before this was recorded |
| lastRelease | string | null | • | Release the most recent event was reported from, or null if none was reported |
| regressedAt | string | null | • | ISO 8601 timestamp of the last regression, or null if it never regressed |
| groupingRule | string | • | Which rule of the ladder matched: explicit, stack, exception or message |
| exceptionType | string | null | • | Exception class of the first event, or null |
| groupingVersion | string | • | Grouping config version that produced the fingerprint; part of the issue identity |

### `POST` `/observe/issues/:id/assign`

Assign an issue to someone, or pass a null assignee to clear the assignment.

MCP tool: `observe_issues_assign`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| id | path | string | • | Identifier of the issue |
| assignee | body | string | null | • | Who should own the issue; null clears the assignment |

**Response**

Returns `object`.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| id | string | • | Identifier of the issue |
| title | string | • | Display title, set when the issue opened and never rewritten |
| status | string | • | unresolved, resolved or archived |
| culprit | string | null | • | Where the error appears to come from, or null |
| service | string | null | • | Service the most recent event came from, or null if none was reported |
| assignee | string | null | • | Who owns the issue, or null if unassigned |
| lastSeen | string | • | ISO 8601 timestamp of the most recent event |
| firstSeen | string | • | ISO 8601 timestamp of the first event |
| archivedAt | string | null | • | ISO 8601 archive timestamp, or null |
| eventCount | number | • | How many events have landed on this issue |
| resolvedAt | string | null | • | ISO 8601 resolution timestamp, or null |
| environment | string | null | • | Environment the issue was first seen in, or null |
| fingerprint | string | • | Stable grouping hash the issue is keyed on |
| lastEventId | string | null | • | Identifier of the most recent event that landed on the issue, or null for issues rolled up before this was recorded |
| lastRelease | string | null | • | Release the most recent event was reported from, or null if none was reported |
| regressedAt | string | null | • | ISO 8601 timestamp of the last regression, or null if it never regressed |
| groupingRule | string | • | Which rule of the ladder matched: explicit, stack, exception or message |
| exceptionType | string | null | • | Exception class of the first event, or null |
| groupingVersion | string | • | Grouping config version that produced the fingerprint; part of the issue identity |

### `GET` `/observe/issues/:id/event`

Read one event of an error tracking issue in full — the most recent one by default, or a specific one by id. Returns where and in what it happened: service, environment, release, trace, tags, and for OTel events the span with its HTTP method, route, status and target address. An issue whose events are no longer stored still comes back, with a null event.

MCP tool: `observe_issues_event`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| id | path | string | • | Identifier of the issue |
| eventId | query | string | — | Which event to read; omit to read the most recent one. Must be an event of this issue |

**Response**

Returns `object`.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| event | object | null | • | The event, or null when the issue has no event id or the event is no longer stored — events live 30 days and an issue outlives them. Neither case is an error |
| source | string | null one of: sentry, otel | • | Which store the event id addresses, derived from its shape. Null when there is no event id |
| eventId | string | null | • | Identifier of the event that was read, or null when the issue has recorded no event id yet |
| issueId | string | • | Identifier of the issue the event belongs to |

### `GET` `/observe/issues/:id/events`

List the events of an error tracking issue, newest first. Each row identifies one event; pass its eventId to observe_issues_event to open it. Page with nextCursor.

MCP tool: `observe_issues_events`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| id | path | string | • | Identifier of the issue |
| limit | query | integer | — | How many events to return, newest first. 1-100, default 50 |
| cursor | query | string | — | The nextCursor of the previous page; omit for the first page |

**Response**

Returns `object`.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| rows | object[] | • | The events, newest first |
| nextCursor | string | null | • | Pass back as cursor for the next page; null when the last page was reached |

### `GET` `/observe/issues/:id/histogram`

Count the events of an error tracking issue per bucket over a window, so a reader can see when it started and whether it is still going. Bucket width is derived from the window; quiet buckets come back as zeros, never as gaps.

MCP tool: `observe_issues_histogram`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| id | path | string | • | Identifier of the issue |
| window | query | string | — | How far back to count, e.g. "1h", "24h", "7d". Up to 30d, default "24h" |

**Response**

Returns `object`.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| to | string | • | ISO 8601 end of the last bucket, exclusive |
| from | string | • | ISO 8601 start of the first bucket |
| window | string | • | The window that was counted, e.g. "24h" |
| buckets | object[] | • | Every bucket of the window, oldest first. Quiet buckets are zeros, never gaps |
| bucketSeconds | number | • | Width of one bucket in seconds, derived from the window |

### `POST` `/observe/issues/:id/resolve`

Mark an issue resolved. A later event flips it back to unresolved and emits observe/issue.regressed.

MCP tool: `observe_issues_resolve`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| id | path | string | • | Identifier of the issue |

**Response**

Returns `object`.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| id | string | • | Identifier of the issue |
| title | string | • | Display title, set when the issue opened and never rewritten |
| status | string | • | unresolved, resolved or archived |
| culprit | string | null | • | Where the error appears to come from, or null |
| service | string | null | • | Service the most recent event came from, or null if none was reported |
| assignee | string | null | • | Who owns the issue, or null if unassigned |
| lastSeen | string | • | ISO 8601 timestamp of the most recent event |
| firstSeen | string | • | ISO 8601 timestamp of the first event |
| archivedAt | string | null | • | ISO 8601 archive timestamp, or null |
| eventCount | number | • | How many events have landed on this issue |
| resolvedAt | string | null | • | ISO 8601 resolution timestamp, or null |
| environment | string | null | • | Environment the issue was first seen in, or null |
| fingerprint | string | • | Stable grouping hash the issue is keyed on |
| lastEventId | string | null | • | Identifier of the most recent event that landed on the issue, or null for issues rolled up before this was recorded |
| lastRelease | string | null | • | Release the most recent event was reported from, or null if none was reported |
| regressedAt | string | null | • | ISO 8601 timestamp of the last regression, or null if it never regressed |
| groupingRule | string | • | Which rule of the ladder matched: explicit, stack, exception or message |
| exceptionType | string | null | • | Exception class of the first event, or null |
| groupingVersion | string | • | Grouping config version that produced the fingerprint; part of the issue identity |

### `GET` `/observe/keys`

List the OTLP ingest keys of the current product. Tokens are never returned.

MCP tool: `observe_keys_list`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| environment | query | string | — | Only return keys of this environment; omit to list all of them |

**Response**

Returns `object[]`.

| Item field | Type | Always | Description |
| --- | --- | --- | --- |
| id | string | • | Identifier of the ingest key |
| kind | string one of: ingest, dsn | • | ingest = Bearer OTLP key; dsn = public Sentry key that ships in client code |
| name | string | null | • | Human label given at creation, or null |
| prefix | string | • | First characters of the token, shown for recognition |
| createdAt | string | • | ISO 8601 creation timestamp |
| revokedAt | string | null | • | ISO 8601 revocation timestamp, or null if active |
| environment | string | • | Environment the key writes telemetry for |

### `POST` `/observe/keys`

Create an ingest credential for one environment: a Bearer OTLP token (kind "ingest") or a public Sentry DSN (kind "dsn"). The credential is returned once and cannot be retrieved again.

MCP tool: `observe_keys_create`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| environment | body | string | • | Environment this key sends telemetry for, e.g. "staging" or "production" |
| name | body | string | — | Optional human label for the key, e.g. "ci" or "backend pods" |
| kind | body | string one of: ingest, dsn | — | ingest (default) issues a Bearer OTLP token; dsn issues a public Sentry key returned as a full DSN |

**Response**

Returns `object`.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| id | string | • | Identifier of the ingest key |
| dsn | string | — | The full DSN, for kind "dsn" — shown only here, store it now |
| kind | string one of: ingest, dsn | • | ingest = Bearer OTLP key; dsn = public Sentry key that ships in client code |
| name | string | null | • | Human label given at creation, or null |
| token | string | — | The full ingest token, for kind "ingest" — shown only here, store it now |
| prefix | string | • | First characters of the token, shown for recognition |
| createdAt | string | • | ISO 8601 creation timestamp |
| revokedAt | string | null | • | ISO 8601 revocation timestamp, or null if active |
| environment | string | • | Environment the key writes telemetry for |

### `POST` `/observe/keys/:id/revoke`

Revoke an OTLP ingest key. Telemetry sent with it is rejected immediately.

MCP tool: `observe_keys_revoke`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| id | path | string | • | Identifier of the ingest key to revoke |

**Response**

Returns `object`.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| id | string | • | Identifier of the revoked key |
| revokedAt | string | • | ISO 8601 timestamp of the revocation |

### `GET` `/observe/logs/facets`

Count the environments, services and severities present in the logs of the current product over a window, so a filter can offer the values that actually exist. Counts are cached for up to a minute.

MCP tool: `observe_logs_facets`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| window | query | string | — | How far back to count, e.g. "1h", "24h", "7d". Up to 30d, default "24h" |

**Response**

Returns `object`.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| to | string | • | Newest timestamp counted, ISO 8601 |
| from | string | • | Oldest timestamp counted, ISO 8601 |
| window | string | • | The window that was counted, e.g. "24h" |
| services | object[] | • | Services, most frequent first |
| severities | object[] | • | Severities, most frequent first |
| environments | object[] | • | Environments, most frequent first |

### `GET` `/observe/logs/search`

Search the log lines of the current product, newest first. Filters are structured; there is no SQL surface. Page with nextCursor.

MCP tool: `observe_logs_search`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| environment | query | string | — | Only lines of this environment, e.g. "production" |
| service | query | string | — | Only lines of this service, e.g. "api" |
| severityIn | query | string[] | — | Only lines whose severity is one of these, e.g. ["ERROR", "FATAL"] |
| search | query | string | — | Match the message body. A single word matches whole words and is indexed; anything longer is a case-insensitive substring match |
| traceId | query | string | — | Only lines of this trace |
| q | query | string | — | Lucene-style expression, ANDed with the structured filters. `key:value` (service, environment, severity, trace_id, span_id, body; any other key matches an attribute), "quoted phrases", AND / OR / NOT in uppercase, parentheses. A bare word matches a whole word in the message body. Example: service:api AND NOT severity:INFO AND "connection refused" |
| from | query | string | — | Oldest timestamp to include, ISO 8601 |
| to | query | string | — | Newest timestamp to include, ISO 8601 |
| limit | query | integer | — | How many lines to return, 1-1000, default 100 |
| cursor | query | string | — | The nextCursor of the previous page; omit for the first page |

**Response**

Returns `object`.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| rows | object[] | • | The matching log lines, newest first |
| nextCursor | string | null | • | Pass back as cursor for the next page; null when the last page was reached |

### `GET` `/observe/logs/tail`

Follow the log lines of the current product. The first call returns the last 50 lines oldest-first; pass the returned cursor back to get only what arrived since.

MCP tool: `observe_logs_tail`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| environment | query | string | — | Only lines of this environment |
| service | query | string | — | Only lines of this service |
| severityIn | query | string[] | — | Only lines whose severity is one of these |
| cursor | query | string | — | The nextCursor of the previous poll; omit to start at the newest lines |

**Response**

Returns `object`.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| rows | object[] | • | The new log lines, oldest first |
| nextCursor | string | null | • | Pass back as cursor on the next poll; null only while the product has no logs |

### `GET` `/observe/metrics/catalog`

Catalogue the metrics of the current product: what exists, whether it is a counter or a histogram, its unit, which services report it and which attribute keys its series carry. Read this before observe_metrics_query — it is where a name, a type and a groupBy key come from.

MCP tool: `observe_metrics_list`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| environment | query | string | — | Only metrics reported in this environment, e.g. "production" |
| service | query | string | — | Only metrics reported by this service, e.g. "api" |
| search | query | string | — | Only metrics whose name contains this text, case-insensitively |
| window | query | string one of: 15m, 1h, 24h, 7d, 30d | — | How far back to look for metrics; default "24h" |

**Response**

Returns `object`.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| window | string | • | The window that was catalogued |
| metrics | object[] | • | The metrics seen in the window, by name |

### `GET` `/observe/metrics/query`

Read one metric of the current product as a time series. Cumulative counters are differenced at read time, so a process restart reads as its own increase rather than as a negative spike; histogram percentiles are interpolated across the emitter's buckets. Up to 100 series and 500 points each, over at most 31 days; truncated says when a ceiling was reached.

MCP tool: `observe_metrics_query`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| name | query | string | • | The metric to read, exactly as observe_metrics_list names it |
| aggregation | query | string one of: rate, increase, value, p50, p90, p99, avg, count | • | How to aggregate each step. A counter takes "rate" (per second), "increase" (over the step) or "value" (its last reading); a histogram takes "p50", "p90", "p99", "avg" or "count". A pair that does not match the metric is rejected. |
| from | query | string | — | Start of the window, ISO 8601; default one hour ago |
| to | query | string | — | End of the window, ISO 8601; default now |
| step | query | string one of: 1m, 5m, 15m, 1h, 6h, 1d | — | Bucket width; default the finest that renders at most 500 points |
| environment | query | string | — | Only points from this environment |
| service | query | string | — | Only points from this service |
| attributes | query | object | — | Only points whose attributes match every one of these key/value pairs |
| groupBy | query | string[] | — | Attribute keys to keep as the series labels, aggregating every other attribute into them; default one series per distinct attribute set |

**Response**

Returns `object`.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| to | string | • | End of the window that was read, ISO 8601 |
| from | string | • | Start of the window that was read, ISO 8601 |
| name | string | • | The metric that was read |
| step | string | • | The bucket width the series is rendered at |
| type | string one of: sum, histogram | • | What the metric holds: a counter ("sum") or an explicit-bucket histogram |
| unit | string | • | Unit as the emitter declared it; empty when none |
| series | object[] | • | One series per label set, ordered stably |
| truncated | boolean | • | True when a ceiling cut the answer — more series exist than were returned |
| aggregation | string | • | The aggregation that was applied |
| stepSeconds | number | • | That width in seconds — what a rate was divided by |

### `GET` `/observe/traces/:traceId`

Read every span of one trace of the current product, oldest first, as a flat list carrying parentSpanId and depth — enough to draw a waterfall without a second call. Span events are returned exactly as the emitter sent them. Up to 5000 spans.

MCP tool: `observe_traces_get`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| traceId | path | string | • | The trace to render, by its 32-character hex id |

**Response**

Returns `object`.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| spans | object[] | • | Every span of the trace, oldest first; empty when the product has no such trace |
| traceId | string | • | The trace that was read |

### `GET` `/observe/traces/search`

Search the traces of the current product, newest first. One row per trace: its root span, how long the whole trace took, how many spans it has and whether any of them failed. Filters are structured; there is no SQL surface. Page with nextCursor.

MCP tool: `observe_traces_search`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| environment | query | string | — | Only traces of this environment, e.g. "production" |
| service | query | string | — | Only traces one of whose spans belongs to this service, e.g. "api" |
| name | query | string | — | Only traces one of whose spans carries this exact span name, e.g. "GET /users" |
| status | query | string one of: ok, error, unset | — | Only traces in this state: "error" when any span failed, "ok" when none did and at least one reported OK, "unset" when no span reported a status |
| minDurationMs | query | number | — | Only traces that took at least this many milliseconds end to end |
| maxDurationMs | query | number | — | Only traces that took at most this many milliseconds end to end |
| traceId | query | string | — | Only this trace, by its 32-character hex id |
| from | query | string | — | Oldest span start to include, ISO 8601 |
| to | query | string | — | Newest span start to include, ISO 8601 |
| limit | query | integer | — | How many traces to return, 1-1000, default 100 |
| cursor | query | string | — | The nextCursor of the previous page; omit for the first page |

**Response**

Returns `object`.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| traces | object[] | • | The matching traces, newest first |
| nextCursor | string | null | • | Pass back as cursor for the next page; null when the last page was reached |

### `GET` `/observe/webhooks`

List the webhook endpoints registered for this product. Signing secrets are never returned.

MCP tool: `observe_webhooks_list`

**Parameters**

No parameters.

**Response**

Returns `object[]`.

| Item field | Type | Always | Description |
| --- | --- | --- | --- |
| id | string | • | Identifier of the endpoint |
| url | string | • | HTTPS endpoint events are POSTed to |
| active | boolean | • | Whether deliveries are being attempted for it |
| createdAt | string | • | ISO 8601 registration timestamp |
| updatedAt | string | • | ISO 8601 timestamp of the last change |
| description | string | null | • | Free-form note given at registration, or null |

### `POST` `/observe/webhooks`

Register an HTTPS endpoint to receive this product’s observe issue events, and mint its signing secret. The secret is returned here and NOWHERE ELSE — store it now: it cannot be read back and can only be replaced by rotating it. Plain http:// endpoints are rejected.

MCP tool: `observe_webhooks_create`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| url | body | string (uri) | • | HTTPS endpoint the event is POSTed to. Plain http:// is rejected |
| description | body | string | — | Free-form note about what this endpoint is for, up to 1000 characters |

**Response**

Returns `object`.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| id | string | • | Identifier of the endpoint |
| url | string | • | HTTPS endpoint events are POSTed to |
| active | boolean | • | Whether deliveries are being attempted for it |
| secret | string | • | The signing secret — shown only here, store it now; it cannot be read back |
| createdAt | string | • | ISO 8601 registration timestamp |
| updatedAt | string | • | ISO 8601 timestamp of the last change |
| description | string | null | • | Free-form note given at registration, or null |

### `DELETE` `/observe/webhooks/:id`

Remove a webhook endpoint and its delivery log. Delivery stops immediately. To stop delivery while keeping the history, set the endpoint inactive instead.

MCP tool: `observe_webhooks_delete`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| id | path | string | • | Identifier of the endpoint |

**Response**

Returns `object`.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| id | string | • | Identifier of the removed endpoint |
| deleted | boolean one of: true | • | Always true; the call fails otherwise |

### `GET` `/observe/webhooks/:id`

Read one registered webhook endpoint of this product. Its signing secret is never returned.

MCP tool: `observe_webhooks_get`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| id | path | string | • | Identifier of the endpoint |

**Response**

Returns `object`.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| id | string | • | Identifier of the endpoint |
| url | string | • | HTTPS endpoint events are POSTed to |
| active | boolean | • | Whether deliveries are being attempted for it |
| createdAt | string | • | ISO 8601 registration timestamp |
| updatedAt | string | • | ISO 8601 timestamp of the last change |
| description | string | null | • | Free-form note given at registration, or null |

### `PATCH` `/observe/webhooks/:id`

Edit a registered endpoint: its url, its description, or whether it is active. Setting active to false keeps the registration and stops delivery. Omit a field to leave it alone; pass null to clear the description. The signing secret is not editable here — rotate it instead.

MCP tool: `observe_webhooks_update`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| id | path | string | • | Identifier of the endpoint to change |
| url | body | string (uri) | — | New HTTPS endpoint. Plain http:// is rejected |
| active | body | boolean | — | false keeps the registration and stops delivery; true resumes it |
| description | body | string | null | — | New note, or null to clear it |

**Response**

Returns `object`.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| id | string | • | Identifier of the endpoint |
| url | string | • | HTTPS endpoint events are POSTed to |
| active | boolean | • | Whether deliveries are being attempted for it |
| createdAt | string | • | ISO 8601 registration timestamp |
| updatedAt | string | • | ISO 8601 timestamp of the last change |
| description | string | null | • | Free-form note given at registration, or null |

### `POST` `/observe/webhooks/:id/rotate-secret`

Replace the signing secret of an endpoint and return the new one, exactly once. The old secret stops verifying immediately — there is no grace window, so update the receiver before rotating.

MCP tool: `observe_webhooks_rotate_secret`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| id | path | string | • | Identifier of the endpoint |

**Response**

Returns `object`.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| id | string | • | Identifier of the endpoint |
| url | string | • | HTTPS endpoint events are POSTed to |
| active | boolean | • | Whether deliveries are being attempted for it |
| secret | string | • | The signing secret — shown only here, store it now; it cannot be read back |
| createdAt | string | • | ISO 8601 registration timestamp |
| updatedAt | string | • | ISO 8601 timestamp of the last change |
| description | string | null | • | Free-form note given at registration, or null |

### `GET` `/observe/webhooks/deliveries`

List webhook delivery attempts for this product, newest first. Filter by endpoint or by state (pending, success, failed). Each row carries the event data that was sent, the last HTTP status and why the last attempt failed.

MCP tool: `observe_webhooks_deliveries_list`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| webhookId | query | string | — | Only deliveries for this endpoint |
| status | query | string one of: pending, success, failed | — | Only deliveries in this state |
| limit | query | integer | — | Page size, 1..200 |
| offset | query | integer | — | Number of deliveries to skip |

**Response**

Returns `object[]`.

| Item field | Type | Always | Description |
| --- | --- | --- | --- |
| id | string | • | Identifier of the delivery, also sent as the envelope id |
| status | string one of: pending, success, failed | • | pending means attempts remain; failed means the retry ladder is spent |
| payload | string | — | The event data that was sent, before snake_case conversion |
| createdAt | string | • | ISO 8601 time the delivery was opened |
| eventName | string | • | Event that was delivered, e.g. "observe/issue.created" |
| lastError | string | null | • | Why the last attempt failed, or null |
| webhookId | string | • | Endpoint the delivery is for |
| deliveredAt | string | null | • | ISO 8601 time it succeeded, or null |
| attemptCount | number | • | How many attempts have been made so far |
| idempotencyKey | string | • | Identity of the source event, repeated on every attempt and on a redelivery |
| lastStatusCode | number | null | • | HTTP status of the last attempt, or null |

### `POST` `/observe/webhooks/deliveries/:id/redeliver`

Send a past delivery to its endpoint again, with the original payload and the original idempotency key — a receiver that already processed it can recognise and ignore the repeat. The original delivery is kept as history and a new one is opened for this attempt. If the endpoint has since been disabled, nothing is delivered.

MCP tool: `observe_webhooks_redeliver`

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| id | path | string | • | Identifier of the delivery to send again |

**Response**

Returns `object`.

| Field | Type | Always | Description |
| --- | --- | --- | --- |
| id | string | • | Identifier of the delivery, also sent as the envelope id |
| status | string one of: pending, success, failed | • | pending means attempts remain; failed means the retry ladder is spent |
| payload | string | — | The event data that was sent, before snake_case conversion |
| createdAt | string | • | ISO 8601 time the delivery was opened |
| eventName | string | • | Event that was delivered, e.g. "observe/issue.created" |
| lastError | string | null | • | Why the last attempt failed, or null |
| webhookId | string | • | Endpoint the delivery is for |
| deliveredAt | string | null | • | ISO 8601 time it succeeded, or null |
| attemptCount | number | • | How many attempts have been made so far |
| idempotencyKey | string | • | Identity of the source event, repeated on every attempt and on a redelivery |
| lastStatusCode | number | null | • | HTTP status of the last attempt, or null |
