Webhooks

Webhooks are used to immediately notify your server of events that take place in the Lean ecosystem. These are especially useful for events that take place on your front end through the Lean SDK or events that take place asynchronously. Once you receive an event on your server, you can process and act on it as you need.

Webhook retry policy

We consider a webhook as having been successfully delivered when we receive a success status code (200) from the webhook URL you specified in your application dashboard.

If we receive any other status code or do not receive a response within 10 seconds, we will start retrying. We take an exponential back off approach to resending webhooks until we receive a successful response sending retries 1, 2, 5, 10, 60, and 180 minutes after the initial webhook was sent.

Webhook security

It is important to ensure that webhooks sent to your Webhook URL truly came from Lean. Without such verification a bad actor can send a fake request to your Webhook URL and potentially cause your system to react as if the request came from Lean.

Subsequently Lean provides two methods for you to secure your webhooks and ensure they're coming directly from us.

Request signing and signatures

If you inspect the headers sent with our webhooks, you’ll see a lean-signature with a value that begins with “sha512=“.

This is the signature — a hash-based message authentication code (HMAC) which was constructed by using SHA-512 as the message digest algorithm and your “webhook secret” as the shared secret key to hash the webhook body. Your “webhook secret” can be found in the Integration section of the developer portal.

Upon receiving the webhook you can compute the signature by computing the HMAC yourself using the webhook body and your webhook secret and then comparing it with the value of the lean-signature header. When you perform your hash calculations, ensure that you use the raw payload of the webhook body to avoid altering the values within the payload prior to calculating the hash. With this you can ensure that the webhook you received came from Lean and nobody else.

IP Whitelisting

You can also configure your server or endpoint to exclusively accept POST requests from the IP Addresses we use to send webhooks. For production applications we send all webhooks from 193.123.71.28 and for sandbox applications we send webhooks from either 15.184.15.24, 15.184.92.63, or 15.184.77.41 and so you will need to whitelist all of these.


Webhook Structure

All webhooks sent through Lean will follow a standard structure as laid out below.

Attributes

type string

The event that has taken place. This is composed of the object type followed by the event in the format [object_type].[event].


payload object

The object associated with the event that triggered the webhook.


message string

A description of the event.


timestamp string

The time at which the event took place in the format: yyyy-MM-dd*HH:mm:ss.SSSSSSZ


event_id string

A UUID uniquely identifying the event

json

// This snippet is only use to show the general structure of webhooks sent by Lean, the text in place of values are descriptive and not representative of the objects or values they describe.
{
"type": "[object].[action]",
"payload": {[Object]},
"message": "[Descriptive message explaining the event]]",
"timestamp": "[Timestamp in format yyyy-MM-dd*HH:mm:ss.SSSSSSZ]",
"event_id": "[Unique UUID identifier for the webhook notification]"
}

Webhook Library

Payment source created

"type": "payment_source.created"

Payload object type: Payment Source

This webhook is triggered when your customer successfully creates a payment source using the Link SDKs Connect() or CreatePaymentSource() functions.

json

{
"payload": {
"id": "ab172710-740c-452b-a78f-8a36a9cf48ef",
"customer_id": "0fd6d135-a4be-40ac-9630-1738ced7da69",
"status": "AWAITING_BENEFICIARY_COOL_OFF",
"bank_identifier": "EIB_UAE",
"bank_name": "Emirates Islamic Bank"
},
"type": "payment_source.created",
"message": "A payment source was pre-authorized by your customer.",
"timestamp": "2021-06-23T14:17:34.342847Z",
"event_id": "00405221-21fc-4aa2-a237-3edfe4c38411"
}
}

Payment source updated

"type": "payment_source.updated"

Payload object type: Payment Source

This webhook is triggered after a payment source's status is updated from AWAITING_BENEFICIARY_COOL_OFF to ACTIVE. This change in status means that your customer can now initiate payments using this payment source. Note that some payment sources are active upon creation and this webhook will only be triggered for payment sources with an AWAITING_BENEFICIARY_COOL_OFF status upon creation. Whether a payment source is ACTIVE or AWAITING_BENEFICIARY_COOL_OFF upon creation is dependent on the financial instituition of the payment source.

Deprecation warning: This webhook will soon be deprecated. Since the main use case for this webhook is to let you know that payments can be initiated from a payment source it is important to know which destination the payment can be made to. Therefore it is more useful to trigger an action based on the payment_source.beneficiary.updated webhook.

json

{
"payload": {
"id": "ab172710-740c-452b-a78f-8a36a9cf48ef",
"customer_id": "0fd6d135-a4be-40ac-9630-1738ced7da69",
"status": "ACTIVE",
"bank_identifier": "EIB_UAE",
"bank_name": "Emirates Islamic Bank"
},
"type": "payment_source.updated",
"message": "A payment source has been updated.",
"timestamp": "2021-06-24T14:21:04.169186Z",
"event_id": "5f8f7875-31d9-43ed-9b2a-0bafa9154311"
}

Beneficiary created

"type": "payment_source.beneficiary.created"

Payload object type: Payment Source Beneficiary

This webhook is triggered when your customer successfully creates a payment source using the Link SDKs Connect() or CreatePaymentSource() functions. This webhook is also triggered when a destination is added to a payment source using the Link SDK UpdatePaymentSource() function which creates a new beneficiary object within the payment source.

Unlike the payment_source.created webhook, the focal object for this webhook is the beneficiary, which represents the pathway between a payment_source and payment_destination. Many beneficiaries can be created for the same payment source which in the real world allows the payment source to make payments to many destinations.

json

{
"payload": {
"id": "2cd827ab-d0c4-462f-b2a2-12e3ef1b7d8e",
"customer_id": "0fd6d135-a4be-40ac-9630-1738ced7da69",
"payment_source_id": "ab172710-740c-452b-a78f-8a36a9cf48ef",
"payment_destination_id": "99da636d-d022-4e0a-a450-a293ecdcb125",
"status": "ACTIVE"
},
"type": "payment_source.beneficiary.created",
"message": "A beneficiary was added for a payment source.",
"timestamp": "2021-06-23T15:59:11.02946Z",
"event_id": "5cf79ddb-ac0e-43fb-894d-5a54eb6c4856"
}

Beneficiary updated

"type": "payment_source.beneficiary.updated"

Payload object type: Payment Source Beneficiary

This webhook is triggered when a beneficiary object changes status from AWAITING_BENEFICIARY_COOL_OFF to ACTIVE. When this webhook is received, the payment source which the beneficiary belongs to can start to make payments to the payment destination that the beneficiary in question refers to.

When received, we suggest notifying the customer who this payment_source belongs to that payments can now be made through this payment source through a push notification.

json

{
"payload": {
"id": "2cd827ab-d0c4-462f-b2a2-12e3ef1b7d8e",
"customer_id": "0fd6d135-a4be-40ac-9630-1738ced7da69",
"payment_source_id": "ab172710-740c-452b-a78f-8a36a9cf48ef",
"payment_destination_id": "99da636d-d022-4e0a-a450-a293ecdcb125",
"status": "ACTIVE"
},
"type": "payment_source.beneficiary.updated",
"message": "A beneficiary was updated for a payment source.",
"timestamp": "2021-06-24T12:27:28.663129Z",
"event_id": "9a826354-d2ea-499e-9f05-a5ad316ef3f1"
}

Payment Created

"type": "payment.created"

Payload object type: Payment

This webhook is triggered when your customer has successfully initiated a payment or when the payment was unsuccessful while going through the Pay() flow within the Link SDK. The status of the payment is reflected in the status field within the payload.

json

{
"payload": {
"id": "66214bdb-5f1a-4127-9ddc-cc44c0446c82",
"customer_id": "0fd6d135-a4be-40ac-9630-1738ced7da69",
"intent_id": "b896af3a-1781-4a48-b173-ce4b2ca6a522",
"status": "ACCEPTED_BY_BANK",
"amount": 10.17,
"currency": "AED",
"bank_transaction_reference": "0H129D0RT04YY"
},
"type": "payment.created",
"message": "A payment object has been created.",
"timestamp": "2020-06-22T13:15:28.565512Z",
"event_id": "f4096636-85f3-42f1-8148-3cf9b5377db2"
}

Entity created

"type": "entity.created"

Payload object type: Entity

This webhook is triggered when your customer succesfullys connects their account through the Link SDK's Link() or Connect(). The entity object is used by you to retrieve data from your customer's bank account. Where the entity_id serves as a token representing the holding bank account.

json

{
"payload": {
"id": "09934cdd-882c-4638-aa2f-bad17bf1a107",
"customer_id": "0fd6d135-a4be-40ac-9630-1738ced7da69",
"app_user_id": "20210908115512",
"permissions": ["transactions", "balance", "identity", "accounts"],
"bank_details": {
"name": "Citi Bank",
"identifier": "CITI_UAE",
"logo": "https://cdn.leantech.me/img/bank-assets/uae/glyphs/citi/g-white-citi_uae.png",
"main_color": "#ffffff",
"background_color": "#003B70"
}
},
"type": "entity.created",
"message": "An entity object has been created.",
"timestamp": "2021-09-08T10:57:21.613015Z",
"event_id": "6573f646-a793-4e5e-897d-61b80e0e835c"
}

Results ready

"type": "results.ready"

Payload object type: Results

The results ready webhook is triggered after Lean's system has retrieved data requested to be delivered asynchronously.

json

{
"payload": {
"id": "dbd310a2-ebd9-4a0f-ae10-0f82f25f1bff"
},
"type": "results.ready",
"message": "Your results are ready.",
"timestamp": "2021-09-02T15:38:54.573741Z",
"event_id": "f8752072-cef2-4056-9ff0-d5d2aa1e4558",
"results_id": "4e326535-9af6-4f77-a24a-e785f88352e1", // deprecated
"status": "ping_results_ready" // deprecated
}

Bank Availability

"type": "bank.availability.updated"

Payload object type: Bank

The Bank Availability webhook is triggered when a bank is taken offline by either Lean or by you in the Application Dashboard. When a bank is unavailable usage of the LinkSDK is restricted for end-users.

json

{
"payload": {
"identifier": "ENBD_UAE",
"availability": {
"active": { "payments": true, "data": true },
"enabled": { "payments": true, "data": true }
}
},
"type": "bank.availability.updated",
"message": "The bank status has been updated.",
"timestamp": "2021-12-14T15:06:46.083515Z",
"event_id": "103586d6-e88c-40ba-aa0e-c6ee255aaf2e"
}