Webhooks

Receive every logged request, with its cost and tokens, at an HTTPS endpoint of your own, signed so you can check it came from CoreValue.

A webhook sends each request the gateway logs to an HTTPS endpoint you run: the request and response bodies and, if you ask for them, the model, provider, cost, tokens and latency. Use it to feed your own warehouse, billing or alerting.

Webhooks carry request logs. Budget alerts are sent by email and to Slack; see Budget alerts.

Add a webhook

You need the Owner or Admin role.

  1. Open Notifications, then the Webhooks tab.

  2. Enter the Endpoint URL. It must be https://. Private, internal and cloud-metadata addresses are refused.

  3. Optionally set:

    FieldWhat it does
    Sample RateThe percentage of requests to send, from 0 to 100. Default 100.
    Properties FiltersOnly send requests whose properties match. Each filter is a key and an exact value, and a request must match all of them.
    Include Enhanced DataAdds the model, provider, cost, tokens, latency and a link to the full request and response. Only an Owner can turn this on.
  4. Save. The webhook's HMAC Key is shown in the list, hidden until you reveal it. Copy it: it is the secret you verify deliveries with.

Use Test on the webhook to send a sample delivery with made-up data.

What you receive

Each delivery is a POST with Content-Type: application/json:

{
  "request_id": "550e8400-e29b-41d4-a716-446655440000",
  "request_body": "{\"model\":\"gpt-4o-mini\",\"messages\":[…]}",
  "response_body": "{\"id\":\"chatcmpl-…\",\"choices\":[…]}",
  "user_id": "user_abc123",
  "model": "gpt-4o-mini",
  "provider": "openai",
  "request_response_url": "https://…",
  "metadata": {
    "cost": 0.0015,
    "promptTokens": 120,
    "completionTokens": 48,
    "totalTokens": 168,
    "latencyMs": 812
  }
}
FieldNotes
request_idThe request's Cova-Id.
request_body, response_bodyThe bodies as strings. A body over 10 KB is replaced with a short message; fetch the full one from request_response_url or the console.
user_idPresent when the request carried Cova-User-Id.
model, provider, metadata, request_response_urlOnly with Include Enhanced Data. metadata.cost is in US dollars. request_response_url is a signed link that expires.

Verify the signature

Every delivery carries a Cova-Signature header: the HMAC-SHA256 of the raw request body, keyed with the webhook's HMAC Key, as lowercase hex. Compute it over the body exactly as received, before parsing the JSON, and compare in constant time.

import { createHmac, timingSafeEqual } from "node:crypto";

function isFromCoreValue(rawBody: string, signature: string, secret: string) {
  const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
  return (
    signature.length === expected.length &&
    timingSafeEqual(Buffer.from(signature), Buffer.from(expected))
  );
}
import hashlib
import hmac

def is_from_corevalue(raw_body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(signature, expected)

The signature covers the body only, with no timestamp, so it proves a delivery came from CoreValue but not when. Use request_id to discard a delivery you have already processed.

Delivery

  • Timing. A delivery is sent about 30 seconds after the request is logged.
  • Success. Any 2xx response. Respond within 90 seconds.
  • No retries. A delivery that fails or times out is not sent again. If you must not miss a request, reconcile against the request log, which records a request whether or not its delivery succeeded.
  • Order. Deliveries are not guaranteed to arrive in the order the requests were made.

On this page