A webhook notification sends matched globals to a URL you control as JSON, so you can feed them into your own bot, dashboard, spreadsheet or database. This page covers the webhook side. For filters and general setup see custom notifications.

If you only want messages in a Discord channel, use a Discord notification instead. It needs no code.

What you need

An HTTPS endpoint that accepts POST and answers with any 2xx status. That is the whole requirement.

Your endpoint has to be reachable from the internet. Local addresses, private network ranges and plain http are rejected when you save, because we will not send your data to an address we cannot verify.

Setting it up

  1. Create the notification and pick Webhook as the type.
  2. Paste your endpoint URL.
  3. Use Send test event to fire a sample global at it. You get the status code and round trip time back immediately, or the error if it failed.
  4. Save. You are shown a signing secret once. Copy it now, it is never displayed again.

If you lose the secret, use New secret on the notification. The old one stops working the moment you generate a new one.

What we send

One POST per matching global, Content-Type: application/json.

Headers

HeaderMeaning
X-EC-EventEvent name, currently global.matched, or global.test for a test
X-EC-DeliveryUnique id for this delivery attempt
X-EC-TimestampUnix seconds, part of the signed string
X-EC-Signaturesha256=<hex>, an HMAC of the timestamp and body using your signing secret

Body

json
{
  "event": "global.matched",
  "deliveryId": "0f2a1c9e-6b1e-4f2a-9c1d-2b7a6e5d4c3b",
  "sentAt": "2026-07-29T17:00:04Z",
  "trigger": { "id": "9c1d2b7a-...", "name": "Big hunting globals" },
  "message": "Emma Nova Steel killed a Atrox Old Alpha with the value of 1234.56 PED! @Fort Ithaca",
  "data": {
    "id": 48213377,
    "type": "Hunting",
    "value": 1234.56,
    "isHof": true,
    "isAth": false,
    "isTeam": false,
    "occurredAt": "2026-07-29T17:00:01Z",
    "societyName": "Deep Space Miners",
    "avatar": {
      "name": "Emma Nova Steel",
      "slug": "emma-nova-steel",
      "url": "https://www.entropiacentral.com/avatars/emma-nova-steel"
    },
    "creature": {
      "name": "Atrox Old Alpha",
      "slug": "atrox-old-alpha",
      "url": "https://www.entropiacentral.com/wiki/creatures/atrox-old-alpha"
    },
    "landarea": {
      "name": "Fort Ithaca",
      "slug": "fort-ithaca",
      "url": "https://www.entropiacentral.com/landareas/fort-ithaca"
    }
  }
}

message is a plain sentence with no markup, there for when you just want to print something. The structured fields under data are what you should read from in code.

creature, deposit, landarea and item are only present when they apply to that type of global, so treat them as optional. tier appears on tier ups. Fields will be added over time, so ignore anything you do not recognise rather than failing on it.

Verifying a request

Everything needed to verify is in the headers, so you can reject anything that is not from us:

  1. Take the raw request body, exactly as received, before any JSON parsing.
  2. Build the string {X-EC-Timestamp}.{raw body}.
  3. Compute HMAC-SHA256 over that string using your signing secret.
  4. Hex encode it, prefix it with sha256=, and compare against X-EC-Signature using a constant time comparison.
  5. Reject anything where X-EC-Timestamp is more than five minutes old, which stops a captured request being replayed at you later.

Reading the body after your framework has parsed and re-serialised it will produce a different string and the signature will never match. Capture the raw bytes.

X-EC-Delivery is stable per delivery attempt. Store the ones you have processed and skip repeats, so a retry cannot double count.

Build it with AI

Copy this prompt into your AI assistant of choice, along with whatever language and framework you use.

text
Build an HTTP endpoint that receives webhooks from Entropia Central.

Requirements:
- A single POST route, publicly reachable over HTTPS, that returns 200 quickly.
- Read the RAW request body before any JSON parsing. I need the exact bytes for
  signature verification.
- Verify the signature before trusting anything:
    signed_string = X-EC-Timestamp header + "." + raw body
    expected = "sha256=" + hex(HMAC_SHA256(signing_secret, signed_string))
  Compare against the X-EC-Signature header using a constant time comparison.
  Return 401 on mismatch.
- Reject the request if X-EC-Timestamp is more than 300 seconds away from now,
  to prevent replays.
- Deduplicate on the X-EC-Delivery header. If a delivery id has already been
  processed, return 200 without doing the work again.
- Only then parse the JSON body. Its shape is:
    event       string, e.g. "global.matched" or "global.test"
    deliveryId  string
    sentAt      ISO 8601 UTC timestamp
    trigger     { id: string, name: string }
    message     string, a plain sentence with no markup
    data        {
      id          number
      type        string, one of Hunting, Mining, Space Mining, Construction,
                  Discovery, Rare Item, Tiered Item, PvP
      value       number, PED. PvP globals have no meaningful value.
      isHof       boolean
      isAth       boolean
      isTeam      boolean
      occurredAt  ISO 8601 UTC timestamp
      societyName string or absent
      avatar      { name, slug, url }
      creature    { name, slug, url } or absent
      deposit     { name, slug, url } or absent
      landarea    { name, slug, url } or absent
      item        { name, url } or absent
      tier        number or absent
    }
  Treat every optional object as possibly missing, and ignore unknown fields
  rather than failing, because new ones will be added over time.
- Read the signing secret from an environment variable, never hardcode it.
- Log the delivery id, event name and outcome of each request.
- Include a small test that posts a correctly signed sample request and one with
  a bad signature, asserting 200 and 401.

Then store each global in <your database>, or <describe what you want done with it>.

Replace the last line with what you actually want to happen. Everything above it is the contract, and it will not change without notice.

When something goes wrong

The notification card shows how many deliveries succeeded and failed in the last 24 hours, plus the last error we saw. A non 2xx status, a timeout past 10 seconds, or a TLS failure all count as failures.

We do not follow redirects, so a URL that redirects will fail. Point us at the final address.