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
- Create the notification and pick Webhook as the type.
- Paste your endpoint URL.
- 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.
- 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
| Header | Meaning |
|---|---|
X-EC-Event | Event name, currently global.matched, or global.test for a test |
X-EC-Delivery | Unique id for this delivery attempt |
X-EC-Timestamp | Unix seconds, part of the signed string |
X-EC-Signature | sha256=<hex>, an HMAC of the timestamp and body using your signing secret |
Body
{
"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:
- Take the raw request body, exactly as received, before any JSON parsing.
- Build the string
{X-EC-Timestamp}.{raw body}. - Compute
HMAC-SHA256over that string using your signing secret. - Hex encode it, prefix it with
sha256=, and compare againstX-EC-Signatureusing a constant time comparison. - Reject anything where
X-EC-Timestampis 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.
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.