These are callbacks are made from Ensuro's system back to partner's systems to notify about events on policies. It's mainly used for asynchronous policy creation/resolution via our API.
To get notifications from our system you will need to provide an HTTPS endpoint that will receive the notifications.
Retry policy
The endpoint must reply with a status code of 200. Any response status 4xx or 5xx will be considered a rejected message and will be retried later. If the endpoint does not reply within 10 seconds the message will be considered rejected and retried again later.
The first retry is done after 30 seconds, then doubled each time the request fails up to a max delay of 10 minutes. After 10 retries the notification will not be retried anymore.
Robustness principle and idempotency
You must implement an idempotent consumer to receive notifications from our system.
This means that we guarantee at-least-once delivery, but provide no guarantees about:
Repeated messages: your system must gracefully handle receiving the same notification twice. For instance, if you receive a policy/resolved notification twice, you can just ignore the second notification by returning a 200 status code without further processing.
Out of order messages: because of the retry policy, you may receive messages in a different order than they were generated. Your system must not expect notification messages in a specific order.
Besides idempotency, you should apply the robustness principle when designing your events receiver:
Be conservative in what you send, be liberal in what you accept
We may add new fields to the notification payload as the protocol evolves, your system should pick what it needs from the payload and simply ignore everything else, instead of failing if a new field is added.
This, of course, only refers to backwards compatible changes like adding new fields. Any backwards-incompatible changes will be rolled out with previous coordination to avoid breaking stuff.
Notification signature
Notifications will be signed by Ensuro. You should verify this signature to make sure the notification really comes from Ensuro. No other authentication method is supported.
To verify the signature you'll need to recalculate the signature on your end and verify that it matches the signature present in the X-Ensuro-Signature header. Here's how to do that:
import hmacfrom hashlib import sha256defcheck_signature(raw_body:bytes,signature:str,sign_secret:str) ->bool:"""Check HMAC signature Arguments: - raw_body: the signed message as bytes - signature: the signature received along with the message - sign_secret: the secret used for signing Returns: True if signature checks out, False otherwise. Examples: >>> check_signature(b"hello world", "500f38dc7f0b1b86b6911e95cb1ad56bb13409937302e1c0f31f5ab1c397d5b6", "T0pS3cret")
True >>> check_signature(b"hello world", "ff73b9fbfcd2454daa91ad3c232c65090713b18651cb5c0c4f39d57ccc87d4bb", "T0pS3cret")
False """ calculated_digest = hmac.new(bytes(sign_secret, "utf-8"), msg=raw_body, digestmod=sha256).hexdigest()return calculated_digest.lower()== signature.lower()
/** * Checks that the signature is correct * @param{String} rawBody The raw body of the request * @param{String} signature The signature to verify * @param{String} signSecret The signing secret * @returns{Boolean} true if the signature is valid, false otherwise */functioncheckSignature(rawBody, signature, signSecret) {constcalculated_signature=createHmac("sha256", signSecret).update(rawBody).digest("hex");return calculated_signature === signature;}
You must take the raw body as received from the network, without any additional decoding or processing. This is a common error in NodeJS:
constexpress=require("express");constapp=express();constport=3000;constSIGN_SECRET="T0pS3cret";app.use(express.json());app.post("/notify", (req, res) => {constsignature=req.get("X-Ensuro-Signature");constbody=JSON.stringify(req.body); // BAD, don't do thisif (!checkSignature(body, signature,SIGN_SECRET)) {res.status(403).send("Invalid signature");return; }// Process the request});app.listen(port, () => {console.log(`App started on port ${port}`);});
By first decoding the JSON payload and then encoding it again as JSON you may be modifying the body and the signature will not match. You should instead do something like this:
constexpress=require("express");constapp=express();constport=3000;constSIGN_SECRET="T0pS3cret";app.use(express.json({verify: (req, res, buf) => {if (!checkSignature(buf,req.get("X-Ensuro-Signature"),SIGN_SECRET)) {thrownewError("Invalid signature"); } }, }));app.post("/notify", (req, res) => {// Process the requestres.send("OK");});app.listen(port, () => {console.log(`App started on port ${port}`);});
If you aren't using ExpressJS you should check your framework's documentation to find out how to get the raw body of the request for signature validation.
Notifications
Error notifications will have JSON content with the following structure:
{"type": ..."data": ..."error_detail": ...}
Success notifications will have the following structure:
{"type": ..."policy": ...}
Policy Creation
When a policy is successfully created a notification like this one will be sent:
Note the events array that now holds the resolution transaction. Another important field to notice is the actual_payout which holds the amount for which the policy was resolved, as opposed to the payout field that holds the policy's original max payout.
Failed policy creation notification
When a policy's creation has an unrecoverable error a notification like the following will be sent: