# Webhooks ## Introduction Webhooks are a form of server-to-server communication which send notifications as soon as specific events occur. By subscribing to webhooks, you can reduce the number of API calls that your solution makes and build your solution to react to important events in real time. This guide explains how to subscribe to webhooks and contains an explanation and sample payload for each webhook event. ## How to subscribe to a webhook note Please note that webhooks are intended for informational purposes only. Your solution's business logic should not directly use the contents of the webhook payload (with the exception of **seizure-related** webhooks). Please use the Solaris API's `GET` endpoints to retrieve information about resources for your business logic. First, your solution must expose a URL (HTTPS) where Solaris can send webhook notifications. Then, for **each** event type you wish to subscribe to, you must call the [POST Create a webhook subscription](/api-reference/onboarding/webhooks/#tag/Webhook-subscription/paths/~1v1~1webhooks/post) method. Supply the following in the request: - `event_type`: The [type of webhook event](#full-list-of-webhook-events) to subscribe to. - `url`: The URL to which Solaris will send the webhook notification. When you register a new webhook subscription, the Solaris API will check the availability of the provided URL by sending a single `POST` notification. The notification does not include a body, but it includes the `SOLARIS-WEBHOOK-EVENT-TYPE` header with a value of `WEBHOOK-SUBSCRIPTION`. If your URL returns a `2XX` HTTP response code, then the Solaris API will create the webhook subscription and return you a `201 Created` response. In the response body, you will find the `secret` property. You must use the value of this property to [verify the authenticity](#content-verification) of Solaris webhook notifications. The API only exposes this secret once, so if you misplace it, then you must register a new webhook subscription. ### How to update a webhook subscription Once a webhook subscription has been created, it cannot be modified. You must delete the webhook subscription and register a new one with the desired updated properties. ## Webhook notification delivery Implement the following HTTP response codes for the URL where your solution will receive webhook notifications: | Response Code | Reaction | | --- | --- | | `2xx` - Success | Solaris' webhook service assumes that the `notification` was successfully delivered. | | `410` - Gone | Solaris' webhook service marks the `notification` delivery as rejected and cancels the subscription on this particular URL. | | `422` - Unprocessable Entity | The delivery is marked as rejected, and Solaris' webhook service will not re-attempt to deliver the `notification`. | Any response code not listed above will place the webhook notification into a **retry loop.** The Solaris webhook service keeps track of the number of retries using a header; with each failed delivery, it will increase the count. After **20 unsuccessful retries** over a period of seven days, the message will automatically expire. ### Notification content A webhook notification consists of two parts: - **Metadata:** Information stored in the HTTP header. This could be information related to the webhook subscription itself (consistent across all webhook types) or it could include IDs of entities (e.g., person IDs) related to the webhook event that caused the notification. - **Payload:** Information stored in the HTTP body. This usually contains all of the properties of the resource related to the webhook event that caused the notification. ### Full list of webhook headers Each webhook notification will contain the following headers: - **Content-Type:** The media type of the webhook notification content. - **Solaris-Webhook-Id:** ID of the webhook itself. - **Solaris-Webhook-Subscription-Id:** ID of the subscription you created for the webhook event. - **Solaris-Webhook-Event-Type:** The event type of the webhook notification. [See the section below](#full-list-of-webhook-events) for a full list. - **Solaris-Entity-Id:** ID of the Solaris entity related to the webhook notification. For example, in a [PERSON_DELETED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/person_deleted/post) webhook notification, this header would contain the ID of a person whose data you must delete. - **Solaris-Webhook-Attempt:** Indicates the number of times Solaris has attempted to deliver the webhook notification. - **Solaris-Webhook-Signature:** SHA256 signature that you must use to verify the authenticity of the webhook. See the [next section](#content-verification) for more information. - **Solaris-Webhook-Callout-Timestamp:** UTC timestamp from when the webhook notification was sent. - **User-Agent:** Indicates the user agent that generated the webhook notification on Solaris' side. ### Content verification Solaris provides a signature hash in each webhook notification as the value of the `Solaris-Webhook-Signature` header. This allows you to verify that the notification really came from Solaris and protect against misuse of your webhook URL. The webhook service generates the signature using the [HMAC algorithm](https://en.wikipedia.org/wiki/HMAC). It uses the `secret` from the webhook subscription as the HMAC key to encrypt the payload, and it encodes the digest used for the signature generation in the header. The `Solaris-Webhook-Subscription-Id` header references the relevant `secret` for calculating the signature. We recommend that you use the raw message payload to generate a signature hash to verify the one received in the header. This ensures your implementation will not break in case additional fields are introduced. Moreover, calculating on the raw message will ensure that parsing errors do not compromise your ability to verify the authenticity of your webhook traffic. Here is a Ruby example of how to validate a message by its signature: ```ruby digest_algorithm, signature = request.env['HTTP_SOLARIS_WEBHOOK_SIGNATURE'].split('=') content = request.body.read if OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new(digest_algorithm), secret, content) != signature halt(422, 'Signature not correct') end # process notification ``` ## Mandatory webhook events You are **required** to subscribe to the following webhook events for compliance purposes: - [ACCOUNT_CLOSURE_REQUEST](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/account_closure_request/post) - [BUSINESS_DELETED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/business_deleted/post) - [BUSINESS_SEIZURE_CREATED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/business_seizure_created/post) - [BUSINESS_SEIZURE_DELETED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/business_seizure_deleted/post) - [BUSINESS_SEIZURE_FULFILLED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/business_seizure_fulfilled/post) - [BUSINESS_SEIZURE_UPDATED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/business_seizure_updated/post) - [PERSON_DELETED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/person_deleted/post) - [PERSON_SEIZURE_CREATED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/person_seizure_created/post) - [PERSON_SEIZURE_DELETED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/person_seizure_deleted/post) - [PERSON_SEIZURE_FULFILLED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/person_seizure_fulfilled/post) - [PERSON_SEIZURE_UPDATED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/person_seizure_updated/post) - [POSTBOX_ITEM_CREATED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/postbox_item_created/post) Beyond compliance requirements, different products require different webhook subscriptions in order to function well. Check the respective product feature guide to learn about which webhooks a particular product requires. ## Full list of webhook events Click the links below to see the full payload information for each webhook event: - [ACCOUNT_BLOCK](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/account_block/post) - [ACCOUNT_CLOSURE](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/account_closure/post) - [ACCOUNT_CLOSURE_REQUEST](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/account_closure_request/post) - [ACCOUNT_LIMIT_CHANGE](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/account_limit_change/post) - [ACCOUNT_OPENING_REQUEST](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/account_opening_request/post) - [ACCOUNT_SNAPSHOT](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/account_snapshot/post) - [ACQUIRER_TOPUP_DECLINED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/acquirer_topup_declined/post) - [ACQUIRER_TOPUP_EXECUTED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/acquirer_topup_executed/post) - [ACQUIRER_TOPUP_PAYMENT_FAILED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/acquirer_topup_payment_failed/post) - [BENEFICIAL_OWNER](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/beneficial_owner/post) - [BOOKING](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/booking/post) - [BUSINESS_CHANGED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/business_changed/post) - [BUSINESS_DELETED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/business_deleted/post) - [BUSINESS_DIRECT_DEBIT_PROFILE_BLOCK_CANCELLED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/business_direct_debit_profile_block_cancelled/post) - [BUSINESS_DIRECT_DEBIT_PROFILE_BLOCK_SCHEDULED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/business_direct_debit_profile_block_scheduled/post) - [BUSINESS_FRONTING_APPLICATION](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/business_fronting_application/post) - [BUSINESS_FRONTING_LOAN_PAYOUT](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/business_fronting_loan_payout/post) - [BUSINESS_FRONTING_PAYOUT_UPDATE](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/business_fronting_payout_update/post) - [BUSINESS_FRONTING_RELATIONSHIP_STATUS_CHANGED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/business_fronting_relationship_status_changed/post) - [BUSINESS_IDENTIFICATION](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/business_identification/post) - [BUSINESS_SEIZURE_CREATED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/business_seizure_created/post) - [BUSINESS_SEIZURE_DELETED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/business_seizure_deleted/post) - [BUSINESS_SEIZURE_FULFILLED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/business_seizure_fulfilled/post) - [BUSINESS_SEIZURE_UPDATED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/business_seizure_updated/post) - [BUSINESS_TAX_IDENTIFICATION_CHANGED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/business_tax_identification_changed/post) - [CARD_AUTHORIZATION](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/card_authorization/post) - [CARD_AUTHORIZATION_DECLINE - LEGACY](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/card_authorization_decline/post) - [CARD_AUTHORIZATION_DECLINE_V2](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/card_authorization_decline_v2/post) - [CARD_AUTHORIZATION_RESOLUTION](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/card_authorization_resolved/post) - [CARD_DELIVERY_TRACKING](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/card_delivery_tracking/post) - [CARD_FRAUD_CASE_PENDING](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/card_fraud_case_pending/post) - [CARD_FRAUD_CASE_TIMEOUT](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/card_fraud_case_timeout/post) - [CARD_LIFECYCLE_EVENT](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/card_lifecycle_event/post) - [CARD_TOKEN_LIFECYCLE](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/card_token_lifecycle/post) - [CASH_OPERATION_STATUS_CHANGED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/cash_operation_status_changed/post) - [CONSUMER_FRONTING_APPLICATION](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/consumer_fronting_application/post) - [CONSUMER_LOAN_APPLICATION](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/consumer_loan_application/post) - [CONSUMER_OVERDRAFT](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/consumer_overdraft/post) - [CONSUMER_OVERDRAFT_APPLICATION](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/consumer_overdraft_application/post) - [CREDIT_CARD_APPLICATION](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/credit_card_application/post) - [CREDIT_LINE](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/credit_line/post) - [DELEGATE_SCA_CANCEL](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/delegate_sca_cancel/post) - [FREELANCER_CREDIT_LINE_APPLICATION](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/freelancer_credit_line/post) - [IDENTIFICATION](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/identification/post) - [IDENTIFICATION_ORIGINATION](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/identification_origination/post) - [IDENTIFICATION_SESSION](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/identification_session/post) - [INCOMING_REJECTED_TRANSACTION](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/incoming_rejected_transaction/post) - [INSTANT_SEPA_CREDIT_TRANSFER_EXECUTED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/instant_sepa_credit_transfer_executed/post) - [INSTANT_SEPA_CREDIT_TRANSFER_FAILED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/instant_sepa_credit_transfer_failed/post) - [LEGAL_REPRESENTATIVE](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/legal_representative/post) - [LOAN](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/loan/post) - [OVERDRAFT](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/overdraft/post) - [OVERDRAFT_APPLICATION](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/overdraft_application/post) - [OVERDRAFT_LIMIT_CHANGE](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/overdraft_limit_change/post) - [PERSON_CHANGED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/person_changed/post) - [PERSON_DELETED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/person_deleted/post) - [PERSON_MOBILE_NUMBER_CREATED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/person_mobile_number_created/post) - [PERSON_MOBILE_NUMBER_DELETED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/person_mobile_number_deleted/post) - [PERSON_SEIZURE_CREATED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/person_seizure_created/post) - [PERSON_SEIZURE_DELETED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/person_seizure_deleted/post) - [PERSON_SEIZURE_FULFILLED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/person_seizure_fulfilled/post) - [PERSON_SEIZURE_UPDATED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/person_seizure_updated/post) - [PERSON_TAX_IDENTIFICATION_CHANGED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/person_tax_identification_changed/post) - [POTENTIAL_ACCOUNT_BLOCKING](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/potential_account_blocking/post) - [POSTBOX_ITEM_CREATED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/postbox_item_created/post) - [QUESTIONS_REQUIRE_RESPONSE](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/questions_require_response/post) - [REFERENCE_ACCOUNT_CREATED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/reference_account_created/post) - [REFERENCE_ACCOUNT_INSTANT_PAYOUT_DECLINED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/reference_account_instant_payout_declined/post) - [REFERENCE_ACCOUNT_INSTANT_PAYOUT_EXECUTED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/reference_account_instant_payout_executed/post) - [RESERVATION_CREATED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/reservation_created/post) - [RESERVATION_RESOLVED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/reservation_resolved/post) - [SCA_CHALLENGE](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/sca_challenge/post) - [SEPA_CREDIT_TRANSACTION_DECLINED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/sepa_credit_transaction_declined/post) - [SEPA_DIRECT_DEBIT_RETURN](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/sepa_direct_debit_return/post) - [SEPA_SCHEDULED_TRANSACTION](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/sepa_scheduled_transaction/post) - [SEPA_STANDING_ORDER](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/sepa_standing_order/post) - [SEPA_TIMED_ORDER](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/sepa_timed_order/post) - [TRUSTED_IBAN_CONFIRMED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/TRUSTED_IBAN_CONFIRMED/post) - [SCHEDULED_TRANSFER_STATUS_CHANGED](/api-reference/onboarding/webhooks/#tag/Webhook-events/paths/scheduled_transfer_status_changed/post)