Skip to main content

Metrics Webhook

Push your Marqo index metrics to any HTTPS endpoint on a configurable schedule. Each delivery is an OTLP JSON payload signed with HMAC-SHA256, so you can verify authenticity and feed the data directly into any OpenTelemetry-compatible observability system.

Base URL​

https://ecom.marqo-ep.ai/api/v1/webhooks/metrics

Authentication​

Include your API key in the Authorization header of every request using the Bearer scheme:

Authorization: Bearer {api_key}

Create a Webhook​

Register a new HTTPS endpoint to receive metrics pushes.

POST /api/v1/webhooks/metrics

Request Body​

ParameterTypeRequiredDefaultDescription
webhookUrlstringYes—HTTPS endpoint to receive deliveries
metricSetsstring[]Yes—One or more of: request_latency, index_stats, jobs
cadencestringNo1hHow often the webhook fires: 5m, 15m, 30m, 1h
periodstringNo1hData point granularity: 5m, 15m, 30m, 1h
timeRangestringNo12hHow far back each delivery covers: 1h, 6h, 12h, 1d, 3d, 7d
indexAllowliststring[] or nullNonullIndex names to include. null means all indexes

Example Request​

curl -X POST "https://ecom.marqo-ep.ai/api/v1/webhooks/metrics" \
-H "Authorization: Bearer {api_key}" \
-H "Content-Type: application/json" \
-d '{
"webhookUrl": "https://your-endpoint.example.com/marqo-metrics",
"metricSets": ["request_latency", "index_stats", "jobs"],
"cadence": "1h",
"period": "1h",
"timeRange": "12h"
}'

Example Response (201)​

{
"webhookId": "b69c30b4-198a-4dae-ab7a-6e75304e57b8",
"webhookUrl": "https://your-endpoint.example.com/marqo-metrics",
"metricSets": ["request_latency", "index_stats", "jobs"],
"cadence": "1h",
"period": "1h",
"timeRange": "12h",
"indexAllowlist": null,
"signingSecret": "whsec_Xge2WnZ_ebTmHfAbZ9S5vNRGsOH...",
"enabled": true,
"createdAt": "2026-04-22T04:29:51.435748+00:00"
}
Save Your Signing Secret

The signingSecret is only returned at creation time. Store it securely — you will need it to verify webhook signatures. It cannot be retrieved later.

List Webhooks​

Retrieve all webhook configurations for your account.

GET /api/v1/webhooks/metrics

Example Response (200)​

{
"webhooks": [
{
"webhookId": "b69c30b4-198a-4dae-ab7a-6e75304e57b8",
"webhookUrl": "https://your-endpoint.example.com/marqo-metrics",
"metricSets": ["request_latency", "index_stats", "jobs"],
"cadence": "1h",
"period": "1h",
"timeRange": "12h",
"indexAllowlist": null,
"enabled": true,
"createdAt": "2026-04-22T04:29:51.435748+00:00"
}
]
}
note

The signingSecret is not included in list responses.

Delete a Webhook​

Remove a webhook configuration. Deliveries stop immediately.

DELETE /api/v1/webhooks/metrics/{webhookId}

Response: 204 No Content

Deleting a webhook ID that does not exist returns 404.


Errors​

Errors return {"error": "..."}.

StatusBodyCause
400{"error": "Webhook URL must use HTTPS"}webhookUrl does not use the https scheme.
400{"error": "Webhook URL must have a valid hostname"}webhookUrl has no hostname.
400{"error": "At least one metric set must be specified"}metricSets is an empty array.
400{"error": "time_range '3d' exceeds maximum '1d' for period '5m'"}timeRange and period are both valid on their own but the combination exceeds the maximum in Time Range.
401{"error": "Unauthorized"}Missing or invalid API key.
404{"error": "Webhook 'wh_abc123' not found"}Deleting a webhook ID that does not exist, or one belonging to another account.
422Validation detail under errorA value outside the accepted set for metricSets, cadence, period or timeRange, or a malformed body. The accepted values are listed under Configuration.

Note that the time_range and period field names appear in their internal snake_case form in the message text, while the request body uses timeRange and period.


Configuration​

Metric Sets​

Choose which categories of metrics to receive. You can select one or more per webhook:

Metric SetDescription
request_latencyAPI request duration (p50/p90/p99) and request counts by endpoint and HTTP status
index_statsCurrent document count and vector count per index
jobsIndexing job counts, document processing counts, average job duration and delay

Cadence​

How often the webhook fires. Each cadence interval triggers a delivery to your endpoint.

ValueDescription
5mEvery 5 minutes
15mEvery 15 minutes
30mEvery 30 minutes
1hEvery hour (default)

Period​

The granularity of each data point in the payload. A shorter period produces more data points per delivery.

ValueDescription
5mOne data point every 5 minutes
15mOne data point every 15 minutes
30mOne data point every 30 minutes
1hOne data point every hour (default)

Time Range​

How far back each delivery covers. The number of data points per delivery is timeRange ÷ period.

ValueDescription
1hLast 1 hour
6hLast 6 hours
12hLast 12 hours (default)
1dLast 1 day
3dLast 3 days
7dLast 7 days

To keep payload sizes reasonable, the maximum timeRange depends on the period:

PeriodMax Time RangeMax Data Points
5m1d288
15m3d288
30m7d336
1h7d168

Index Allowlist​

By default (null), metrics for all indexes in your account are delivered. Each index produces a separate delivery. To limit to specific indexes:

{
"indexAllowlist": ["my-products", "my-articles"]
}

Configuration Examples​

High-Frequency Monitoring​

Monitor a single production index every 5 minutes with fine-grained data points:

curl -X POST "https://ecom.marqo-ep.ai/api/v1/webhooks/metrics" \
-H "Authorization: Bearer {api_key}" \
-H "Content-Type: application/json" \
-d '{
"webhookUrl": "https://your-endpoint.example.com/marqo-metrics",
"metricSets": ["request_latency", "index_stats"],
"cadence": "5m",
"period": "5m",
"timeRange": "1h",
"indexAllowlist": ["production-index"]
}'

This delivers 12 data points (1h ÷ 5m) every 5 minutes for one index.

Hourly Summary​

Get a broad view of all indexes with hourly granularity:

curl -X POST "https://ecom.marqo-ep.ai/api/v1/webhooks/metrics" \
-H "Authorization: Bearer {api_key}" \
-H "Content-Type: application/json" \
-d '{
"webhookUrl": "https://your-endpoint.example.com/marqo-metrics",
"metricSets": ["request_latency", "index_stats", "jobs"],
"cadence": "1h",
"period": "1h",
"timeRange": "1d"
}'

This delivers 24 data points (1d ÷ 1h) every hour for all indexes.


Payload Format​

Each delivery is an OTLP JSON payload. One delivery is sent per index, per webhook, per cadence interval.

Structure​

{
"resourceMetrics": [
{
"resource": {
"attributes": [
{ "key": "service.name", "value": { "stringValue": "marqo" } },
{ "key": "marqo.index.name", "value": { "stringValue": "my-index" } }
]
},
"scopeMetrics": [
{
"scope": {
"name": "marqo.metrics.webhook",
"version": "1.0.0"
},
"metrics": [...]
}
]
}
]
}

Delivery Headers​

HeaderValue
Content-Typeapplication/json
Content-Encodinggzip
X-Webhook-Signaturet=1713758400,v1=a1b2c3d4...

The body is gzip-compressed. Decompress it before parsing.


Metrics Reference​

request_latency​

marqo.request.duration — Summary (ms)​

Request latency percentiles grouped by API operation. Each data point includes p50, p90, and p99 values.

{
"name": "marqo.request.duration",
"unit": "ms",
"summary": {
"dataPoints": [
{
"startTimeUnixNano": "1713754800000000000",
"timeUnixNano": "1713758400000000000",
"quantileValues": [
{ "quantile": 0.5, "value": 47.80 },
{ "quantile": 0.9, "value": 125.30 },
{ "quantile": 0.99, "value": 450.00 }
],
"attributes": [
{ "key": "operation", "value": { "stringValue": "POST /api/v1/indexes/:index/search" } }
]
}
]
}
}

marqo.request.count — Sum (monotonic)​

Total request count grouped by endpoint and status_class (2XX, 4XX, 5XX).

index_stats​

marqo.index.doc_count — Gauge​

Current number of documents in the index. Single data point per delivery.

marqo.index.vector_count — Gauge​

Current number of vectors in the index. Single data point per delivery.

jobs​

marqo.jobs.total — Sum (monotonic)​

Indexing job count grouped by status (COMPLETED, FAILED).

marqo.jobs.documents_total — Sum (monotonic)​

Processed document count grouped by result (processed, failed, skipped, conflict).

marqo.jobs.avg_duration — Gauge (seconds)​

Average job processing time per period window.

marqo.jobs.avg_delay — Gauge (seconds)​

Average time between job creation and processing start per period window.

Summary Table​

MetricMetric SetTypeUnitAttributes
marqo.request.durationrequest_latencysummarymsoperation
marqo.request.countrequest_latencysum1endpoint, status_class
marqo.index.doc_countindex_statsgauge1—
marqo.index.vector_countindex_statsgauge1—
marqo.jobs.totaljobssum1status
marqo.jobs.documents_totaljobssum1result
marqo.jobs.avg_durationjobsgauges—
marqo.jobs.avg_delayjobsgauges—

Signature Verification​

Every delivery includes an X-Webhook-Signature header for authenticity verification. The signature is computed as HMAC-SHA256(signing_secret, "{timestamp}.{uncompressed_body}").

Python​

import gzip
import hashlib
import hmac
import json


def verify_webhook(
raw_body: bytes, signature_header: str, signing_secret: str
) -> dict:
"""Verify and parse a Marqo metrics webhook delivery."""
# Decompress
body = gzip.decompress(raw_body)

# Parse header: "t=TIMESTAMP,v1=SIGNATURE"
parts = dict(p.split("=", 1) for p in signature_header.split(","))
timestamp = parts["t"]
signature = parts["v1"]

# Compute expected signature
message = f"{timestamp}.".encode("utf-8") + body
expected = hmac.new(
signing_secret.encode("utf-8"), message, hashlib.sha256
).hexdigest()

if not hmac.compare_digest(signature, expected):
raise ValueError("Invalid webhook signature")

return json.loads(body)

Node.js​

const crypto = require("crypto");
const zlib = require("zlib");

function verifyWebhook(rawBody, signatureHeader, signingSecret) {
const body = zlib.gunzipSync(rawBody);

const parts = Object.fromEntries(
signatureHeader.split(",").map((p) => p.split("=", 2))
);

const expected = crypto
.createHmac("sha256", signingSecret)
.update(`${parts.t}.`)
.update(body)
.digest("hex");

if (parts.v1 !== expected) {
throw new Error("Invalid webhook signature");
}

return JSON.parse(body);
}

Endpoint Requirements​

Your webhook endpoint must:

  • Accept HTTPS POST requests
  • Return a 2xx status code within 10 seconds
  • Handle gzip-compressed request bodies

If your endpoint is unreachable or returns a non-2xx status, the delivery is retried automatically. After repeated failures, the delivery is dropped.


FAQ​

How many webhooks can I create?

You can create multiple webhooks with different configurations — different metric sets, cadences, or index filters.

Can I use different cadences for different metric sets?

Yes. Create separate webhooks, each with its own cadence and metric set selection.

What happens if my endpoint is down?

Deliveries are retried automatically. If your endpoint remains unreachable after all retries, the delivery is dropped.

What happens when I create a new index?

Metrics appear in webhook deliveries after the index has been active long enough for data to be collected (typically a few minutes).

Are deliveries per-index or aggregated?

Per-index. If you have 3 indexes and no indexAllowlist, you receive 3 separate deliveries per cadence interval.

Can I recover my signing secret?

No. The signing secret is shown only at creation time. If you lose it, delete the webhook and create a new one.

What do startTimeUnixNano and timeUnixNano mean?

These define the time window for each data point. The difference between them equals your configured period. For example, with period: "1h", each data point covers a 1-hour window.