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
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
webhookUrl | string | Yes | — | HTTPS endpoint to receive deliveries |
metricSets | string[] | Yes | — | One or more of: request_latency, index_stats, jobs |
cadence | string | No | 1h | How often the webhook fires: 5m, 15m, 30m, 1h |
period | string | No | 1h | Data point granularity: 5m, 15m, 30m, 1h |
timeRange | string | No | 12h | How far back each delivery covers: 1h, 6h, 12h, 1d, 3d, 7d |
indexAllowlist | string[] or null | No | null | Index 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"
}
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"
}
]
}
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
Configuration
Metric Sets
Choose which categories of metrics to receive. You can select one or more per webhook:
| Metric Set | Description |
|---|---|
request_latency | API request duration (p50/p90/p99) and request counts by endpoint and HTTP status |
index_stats | Current document count and vector count per index |
jobs | Indexing 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.
| Value | Description |
|---|---|
5m | Every 5 minutes |
15m | Every 15 minutes |
30m | Every 30 minutes |
1h | Every hour (default) |
Period
The granularity of each data point in the payload. A shorter period produces more data points per delivery.
| Value | Description |
|---|---|
5m | One data point every 5 minutes |
15m | One data point every 15 minutes |
30m | One data point every 30 minutes |
1h | One data point every hour (default) |
Time Range
How far back each delivery covers. The number of data points per delivery is timeRange ÷ period.
| Value | Description |
|---|---|
1h | Last 1 hour |
6h | Last 6 hours |
12h | Last 12 hours (default) |
1d | Last 1 day |
3d | Last 3 days |
7d | Last 7 days |
To keep payload sizes reasonable, the maximum timeRange depends on the period:
| Period | Max Time Range | Max Data Points |
|---|---|---|
5m | 1d | 288 |
15m | 3d | 288 |
30m | 7d | 336 |
1h | 7d | 168 |
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
| Header | Value |
|---|---|
Content-Type | application/json |
Content-Encoding | gzip |
X-Webhook-Signature | t=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
| Metric | Metric Set | Type | Unit | Attributes |
|---|---|---|---|---|
marqo.request.duration | request_latency | summary | ms | operation |
marqo.request.count | request_latency | sum | 1 | endpoint, status_class |
marqo.index.doc_count | index_stats | gauge | 1 | — |
marqo.index.vector_count | index_stats | gauge | 1 | — |
marqo.jobs.total | jobs | sum | 1 | status |
marqo.jobs.documents_total | jobs | sum | 1 | result |
marqo.jobs.avg_duration | jobs | gauge | s | — |
marqo.jobs.avg_delay | jobs | gauge | s | — |
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.