Best Practices
Verifying Webhooks
In each of the webhook events sent, you will receive an x-webook-signature that you can use to verify that the event came from Original and was not impersonated by someone else. This is best described on the Verifying Webhooks Page
Periodically make get
requests
get
requestsDue to the nature of webhooks, it is generally good practice to periodically call get_asset
(or any other relevantget
resource) every few hours using a cron job in case an error occurs or an out of date event is received. This will ensure you will more than likely have the correct state of the entities.
Managing Errors and Replays
If an error occurs during the dispatch of a webhook, you will see this in the Webhook logs.
When an error occurs, we will attempt to resend the event until we receive a success response code (2xx).
The retry rate varies, however we will typically resend the event up to 10 times per hour, up to a maximum of 72 hours after the initial event was registered.
If after retrying for 72 hours we still do not receive a success response code, we will mark the event as expired and no longer attempt to resend it.
During the 72 hours, you are able to change the webhook configuration, in case the URL or access credentials are incorrect. We will resend the event using the up-to-date credentials.
Receiving an out of date event
Each event will contain the event_time
(the time that the event was initially registered in UTC) and a sent_at
time (the time that this dispatch took place in UTC), as well as the number of attempts x-webhook-attempt
.
{
"data": {
"asset": {
"uid: "ABC1234567"
...
}
},
"event_time": "2023-08-01T11:24:36.531141+00:00",
"sent_at": "2023-07-31T17:27:01.764524+00:00",
"x-webhook-attempt": "100",
}
If there is a large difference between the event_time
and sent_at
time, it may be that the event becomes irrelevant. For example, observe the following edge case:
asset A
is transferred touser U1
webhook W1
fail - asset.transferredasset A
is then transferred touser U2
webhook W2
ok - asset.transferred- client side:
asset A
is owned byuser U2
webhook W1
is replayed and ok - asset.transferred- client side:
asset A
is owned byuser U1
In this case, as a contingency you should check the difference between the event_time
and sent_at
times, and possibly the x-webhook-attempt
.
If the x-webhook-attempt
is greater than a certain threshold, or the difference between the event_time
and sent_at
are greater than a certain threshold, then it is recommended to make a request to get the current state of the asset:
from datetime import timedelta, datetime
from api import get_asset_api # Calls GET /asset/{uid}
attempt_threshold = 10
time_diff_threshold_in_mins = 10
def handle_asset_webhook_event(webhook_event: dict):
asset_uid = webhook_event["data"]["asset"]["uid"]
attempt_number = webhook_event["x-webhook-attempt"]
sent_at = datetime.fromisoformat(webhook_event["sent_at"])
event_time = datetime.fromisoformat(webhook_event["event_time"])
# Check if either the attempt threshold or time difference threshold is exceeded
if attempt_number > attempt_threshold:
asset_data = get_asset_api(asset_uid)
elif sent_at - event_time > timedelta(minutes=time_diff_threshold_in_mins):
asset_data = get_asset_api(asset_uid)
else:
asset_data = webhook_event["data"]["asset"]
# handle asset_data
...
In general, it is good practice to use the webhook as a signal that "something changed" and then make a request to get the latest data if you expect there to be many transactions of a particular resource or to catch edge cases such as the one highlighted above.
However in the majority of cases, the latest webhook event will be the latest one, and this can be verified with the event time (in UTC).
Updated 15 days ago