# Webhooks

Webhooks allow your application to receive real-time notifications when specific events occur in the Solaris platform.

## Overview

* **Mechanism:** Solaris sends a `POST` request to your exposed HTTPS URL.
* **Security:** Payloads are signed with HMAC SHA-256 for verification.
* **Reliability:** Failed deliveries are retried with exponential backoff for up to 5 days.


## Managing subscriptions

To receive notifications, you must create a subscription for **each** event type you want to monitor.

### 1. Create a subscription

Call the `POST /v1/webhooks` endpoint with the following payload:

* `event_type`: The [event name](#available-webhook-events) (e.g., `BOOKING`).
* `url`: Your HTTPS endpoint.


**Verification Request:**
Upon creation, Solaris immediately sends a test `POST` request to your URL:

* **Header:** `Solaris-Webhook-Event-Type: WEBHOOK-SUBSCRIPTION`
* **Body:** (Empty)


Your server must respond with `200 OK`. If successful, the API returns the subscription details, including a **`secret`**.

Important
Store the `secret` immediately. It is displayed **only once** upon creation and is required to verify the authenticity of incoming messages. If lost, you must delete and recreate the subscription.

### 2. Update a subscription

Webhook subscriptions cannot be modified. To change a URL or event type:

1. **Delete** the existing subscription.
2. **Create** a new subscription with the updated details.


## Handling notifications

Your webhook endpoint must process notifications efficiently and securely.

### Response codes

Solaris interprets your HTTP response code to determine the status of the delivery:

| Code | Status | Result |
|  --- | --- | --- |
| `2xx` | **Success** | The notification is marked as delivered. |
| `410` | **Gone** | The subscription is **cancelled** automatically. Use this if you no longer want to receive events at this URL. |
| `422` | **Unprocessable** | The notification is marked as rejected and will **not** be retried. |
| Other | **Failure** | The notification enters the [retry loop](#retries-and-backoff). |


### Timeouts and async processing

Solaris expects a response within **30 seconds**.

Best Practice
**Acknowledge immediately, process later.**
To avoid timeouts, your server should return `200 OK` as soon as it receives and validates the request. Perform complex business logic (e.g., database updates) asynchronously using a background job.

### Retries and backoff

If delivery fails (non-200 response or timeout), Solaris retries using an exponential backoff strategy:

* **Max Attempts:** 25
* **Backoff Rate:** 1.561 (Interval increases by ~56% each time)
* **Total Duration:** Approximately **5 days** before the message is discarded.


## Webhook headers

Every notification contains the following HTTP headers. You should use these for routing, identifying resources, and security verification.

| Header | Description |
|  --- | --- |
| `Content-Type` | The media type of the content (usually `application/json`). |
| `Solaris-Webhook-Id` | Unique ID of this specific webhook notification. Useful for logging and idempotency checks. |
| `Solaris-Webhook-Subscription-Id` | The ID of the subscription that triggered this notification. |
| `Solaris-Webhook-Event-Type` | The type of event (e.g., `PERSON_CHANGED`). |
| `Solaris-Entity-Id` | The ID of the primary resource (e.g., Person ID, Account ID) related to the event. |
| `Solaris-Webhook-Attempt` | The current retry attempt number (starts at `1`). |
| `Solaris-Webhook-Signature` | The HMAC SHA-256 signature of the payload. **Prefix:** `sha256=`. |
| `Solaris-Webhook-Callout-Timestamp` | UTC timestamp of when the notification was sent. Useful to prevent replay attacks. |
| `User-Agent` | Identifies the Solaris service agent sending the request. |


## Security and verification

You must verify the `Solaris-Webhook-Signature` header to ensure the request originated from Solaris and has not been tampered with.

The signature is generated using **HMAC SHA-256**. The key is the `secret` you received when creating the subscription.

### Verification steps

1. Extract the signature from the `Solaris-Webhook-Signature` header. Note that the value is prefixed with the algorithm (e.g., `sha256=...`).
2. Read the **raw request body** (do not parse it as JSON yet).
3. Calculate the HMAC SHA-256 hash of the raw body using your stored `secret`.
4. Compare your calculated hash with the signature from the header.


### Ruby example


```ruby verify_signature.rb
signature_header = request.env['HTTP_SOLARIS_WEBHOOK_SIGNATURE']
algorithm, received_signature = signature_header.split('=') # splits "sha256=..."
content = request.body.read

# Calculate expected signature
calculated_signature = OpenSSL::HMAC.hexdigest(
  OpenSSL::Digest.new(algorithm), 
  your_stored_secret, 
  content
)

# Compare signatures (secure comparison is recommended)
if calculated_signature != received_signature
  halt 422, 'Invalid Signature'
end

# Proceed to process the webhook
```

## Mandatory compliance webhooks

You are **required** to subscribe to these events to remain compliant with banking regulations (e.g., account closures, seizures).

details
summary
strong
View mandatory webhooks list
* [`ACCOUNT_CLOSURE_REQUEST`](/api-reference/onboarding/webhooks/webhook-events/paths/account_closure_request/post)
* [`BUSINESS_DELETED`](/api-reference/onboarding/webhooks/webhook-events/paths/business_deleted/post)
* [`BUSINESS_SEIZURE_CREATED`](/api-reference/onboarding/webhooks/webhook-events/paths/business_seizure_created/post)
* [`BUSINESS_SEIZURE_DELETED`](/api-reference/onboarding/webhooks/webhook-events/paths/business_seizure_deleted/post)
* [`BUSINESS_SEIZURE_FULFILLED`](/api-reference/onboarding/webhooks/webhook-events/paths/business_seizure_fulfilled/post)
* [`BUSINESS_SEIZURE_UPDATED`](/api-reference/onboarding/webhooks/webhook-events/paths/business_seizure_updated/post)
* [`PERSON_DELETED`](/api-reference/onboarding/webhooks/webhook-events/paths/person_deleted/post)
* [`PERSON_SEIZURE_CREATED`](/api-reference/onboarding/webhooks/webhook-events/paths/person_seizure_created/post)
* [`PERSON_SEIZURE_DELETED`](/api-reference/onboarding/webhooks/webhook-events/paths/person_seizure_deleted/post)
* [`PERSON_SEIZURE_FULFILLED`](/api-reference/onboarding/webhooks/webhook-events/paths/person_seizure_fulfilled/post)
* [`PERSON_SEIZURE_UPDATED`](/api-reference/onboarding/webhooks/webhook-events/paths/person_seizure_updated/post)
* [`POSTBOX_ITEM_CREATED`](/api-reference/onboarding/webhooks/webhook-events/paths/postbox_item_created/post)


## Available webhook events

Click the links below to view the full payload information for each webhook event.

details
summary
strong
View all webhook events
* [`ACCOUNT_BLOCK`](/api-reference/onboarding/webhooks/webhook-events/paths/account_block/post)
* [`ACCOUNT_CLOSURE`](/api-reference/onboarding/webhooks/webhook-events/paths/account_closure/post)
* [`ACCOUNT_LIMIT_CHANGE`](/api-reference/onboarding/webhooks/webhook-events/paths/account_limit_change/post)
* [`ACCOUNT_OPENING_REQUEST`](/api-reference/onboarding/webhooks/webhook-events/paths/account_opening_request/post)
* [`ACCOUNT_SNAPSHOT`](/api-reference/onboarding/webhooks/webhook-events/paths/account_snapshot/post)
* [`ACQUIRER_TOPUP_DECLINED`](/api-reference/onboarding/webhooks/webhook-events/paths/acquirer_topup_declined/post)
* [`ACQUIRER_TOPUP_EXECUTED`](/api-reference/onboarding/webhooks/webhook-events/paths/acquirer_topup_executed/post)
* [`ACQUIRER_TOPUP_PAYMENT_FAILED`](/api-reference/onboarding/webhooks/webhook-events/paths/acquirer_topup_payment_failed/post)
* [`ACTIVATION_CHALLENGE_CREATION`](/api-reference/onboarding/webhooks/webhook-events/paths/activation_challenge_created/post)
* [`BENEFICIAL_OWNER`](/api-reference/onboarding/webhooks/webhook-events/paths/beneficial_owner/post)
* [`BOOKING`](/api-reference/onboarding/webhooks/webhook-events/paths/booking/post)
* [`BUSINESS_CHANGED`](/api-reference/onboarding/webhooks/webhook-events/paths/business_changed/post)
* [`BUSINESS_FRONTING_APPLICATION`](/api-reference/onboarding/webhooks/webhook-events/paths/business_fronting_application/post)
* [`BUSINESS_FRONTING_LOAN_PAYOUT`](/api-reference/onboarding/webhooks/webhook-events/paths/business_fronting_loan_payout/post)
* [`BUSINESS_FRONTING_PAYOUT_UPDATE`](/api-reference/onboarding/webhooks/webhook-events/paths/business_fronting_payout_update/post)
* [`BUSINESS_FRONTING_RELATIONSHIP_STATUS_CHANGED`](/api-reference/onboarding/webhooks/webhook-events/paths/business_fronting_relationship_status_changed/post)
* [`BUSINESS_IDENTIFICATION`](/api-reference/onboarding/webhooks/webhook-events/paths/business_identification/post)
* [`BUSINESS_TAX_IDENTIFICATION_CHANGED`](/api-reference/onboarding/webhooks/webhook-events/paths/business_tax_identification_changed/post)
* [`CARD_AUTHORIZATION`](/api-reference/onboarding/webhooks/webhook-events/paths/card_authorization/post)
* [`CARD_AUTHORIZATION_DECLINE_V2`](/api-reference/onboarding/webhooks/webhook-events/paths/card_authorization_decline_v2/post)
* [`CARD_AUTHORIZATION_RESOLUTION`](/api-reference/onboarding/webhooks/webhook-events/paths/card_authorization_resolved/post)
* [`CARD_DELIVERY_TRACKING`](/api-reference/onboarding/webhooks/webhook-events/paths/card_delivery_tracking/post)
* [`CARD_FRAUD_CASE_PENDING`](/api-reference/onboarding/webhooks/webhook-events/paths/card_fraud_case_pending/post)
* [`CARD_FRAUD_CASE_TIMEOUT`](/api-reference/onboarding/webhooks/webhook-events/paths/card_fraud_case_timeout/post)
* [`CARD_LIFECYCLE_EVENT`](/api-reference/onboarding/webhooks/webhook-events/paths/card_lifecycle_event/post)
* [`CARD_TOKEN_LIFECYCLE`](/api-reference/onboarding/webhooks/webhook-events/paths/card_token_lifecycle/post)
* [`CASH_OPERATION_STATUS_CHANGED`](/api-reference/onboarding/webhooks/webhook-events/paths/cash_operation_status_changed/post)
* [`CONSUMER_FRONTING_APPLICATION`](/api-reference/onboarding/webhooks/webhook-events/paths/consumer_fronting_application/post)
* [`CONSUMER_LOAN_APPLICATION`](/api-reference/onboarding/webhooks/webhook-events/paths/consumer_loan_application/post)
* [`CONSUMER_OVERDRAFT`](/api-reference/onboarding/webhooks/webhook-events/paths/consumer_overdraft/post)
* [`CONSUMER_OVERDRAFT_APPLICATION`](/api-reference/onboarding/webhooks/webhook-events/paths/consumer_overdraft_application/post)
* [`CONSUMER_OVERDRAFT_INTEREST_RATE_CHANGE`](/api-reference/onboarding/webhooks/webhook-events/paths/consumer_overdraft_interest_rate_change/post)
* [`CREDIT_CARD_APPLICATION`](/api-reference/onboarding/webhooks/webhook-events/paths/credit_card_application/post)
* [`CREDIT_LINE`](/api-reference/onboarding/webhooks/webhook-events/paths/credit_line/post)
* [`DISPUTE_CHANGED`](/api-reference/onboarding/webhooks/webhook-events/paths/dispute_changed/post)
* [`DELEGATE_SCA_CANCEL`](/api-reference/onboarding/webhooks/webhook-events/paths/delegate_sca_cancel/post)
* [`FREELANCER_CREDIT_LINE_APPLICATION`](/api-reference/onboarding/webhooks/webhook-events/paths/freelancer_credit_line/post)
* [`FREELANCER_OVERDRAFT_INTEREST_RATE_CHANGE`](/api-reference/onboarding/webhooks/webhook-events/paths/freelancer_overdraft_interest_rate_change/post)
* [`IDENTIFICATION`](/api-reference/onboarding/webhooks/webhook-events/paths/identification/post)
* [`IDENTIFICATION_ORIGINATION`](/api-reference/onboarding/webhooks/webhook-events/paths/identification_origination/post)
* [`IDENTIFICATION_SESSION`](/api-reference/onboarding/webhooks/webhook-events/paths/identification_session/post)
* [`INCOMING_REJECTED_TRANSACTION`](/api-reference/onboarding/webhooks/webhook-events/paths/incoming_rejected_transaction/post)
* [`INSTANT_SEPA_CREDIT_TRANSFER_EXECUTED`](/api-reference/onboarding/webhooks/webhook-events/paths/instant_sepa_credit_transfer_executed/post)
* [`INSTANT_SEPA_CREDIT_TRANSFER_FAILED`](/api-reference/onboarding/webhooks/webhook-events/paths/instant_sepa_credit_transfer_failed/post)
* [`LEGAL_REPRESENTATIVE`](/api-reference/onboarding/webhooks/webhook-events/paths/legal_representative/post)
* [`LOAN`](/api-reference/onboarding/webhooks/webhook-events/paths/loan/post)
* [`OVERDRAFT`](/api-reference/onboarding/webhooks/webhook-events/paths/overdraft/post)
* [`OVERDRAFT_APPLICATION`](/api-reference/onboarding/webhooks/webhook-events/paths/overdraft_application/post)
* [`OVERDRAFT_LIMIT_CHANGE`](/api-reference/onboarding/webhooks/webhook-events/paths/overdraft_limit_change/post)
* [`PERSON_CHANGED`](/api-reference/onboarding/webhooks/webhook-events/paths/person_changed/post)
* [`PERSON_MOBILE_NUMBER_CREATED`](/api-reference/onboarding/webhooks/webhook-events/paths/person_mobile_number_created/post)
* [`PERSON_MOBILE_NUMBER_DELETED`](/api-reference/onboarding/webhooks/webhook-events/paths/person_mobile_number_deleted/post)
* [`PERSON_TAX_IDENTIFICATION_CHANGED`](/api-reference/onboarding/webhooks/webhook-events/paths/person_tax_identification_changed/post)
* [`POTENTIAL_ACCOUNT_BLOCKING`](/api-reference/onboarding/webhooks/webhook-events/paths/potential_account_blocking/post)
* [`QUESTIONS_REQUIRE_RESPONSE`](/api-reference/onboarding/webhooks/webhook-events/paths/questions_require_response/post)
* [`REFERENCE_ACCOUNT_CREATED`](/api-reference/onboarding/webhooks/webhook-events/paths/reference_account_created/post)
* [`REFERENCE_ACCOUNT_INSTANT_PAYOUT_DECLINED`](/api-reference/onboarding/webhooks/webhook-events/paths/reference_account_instant_payout_declined/post)
* [`REFERENCE_ACCOUNT_INSTANT_PAYOUT_EXECUTED`](/api-reference/onboarding/webhooks/webhook-events/paths/reference_account_instant_payout_executed/post)
* [`RESERVATION_CREATED`](/api-reference/onboarding/webhooks/webhook-events/paths/reservation_created/post)
* [`RESERVATION_RESOLVED`](/api-reference/onboarding/webhooks/webhook-events/paths/reservation_resolved/post)
* [`SCA_CHALLENGE`](/api-reference/onboarding/webhooks/webhook-events/paths/sca_challenge/post)
* [`SEPA_CREDIT_TRANSACTION_DECLINED`](/api-reference/onboarding/webhooks/webhook-events/paths/sepa_credit_transaction_declined/post)
* [`SEPA_DIRECT_DEBIT_RETURN`](/api-reference/onboarding/webhooks/webhook-events/paths/sepa_direct_debit_return/post)
* [`SEPA_SCHEDULED_TRANSACTION`](/api-reference/onboarding/webhooks/webhook-events/paths/sepa_scheduled_transaction/post)
* [`SEPA_STANDING_ORDER`](/api-reference/onboarding/webhooks/webhook-events/paths/sepa_standing_order/post)
* [`SEPA_TIMED_ORDER`](/api-reference/onboarding/webhooks/webhook-events/paths/sepa_timed_order/post)
* [`TRUSTED_IBAN_CONFIRMED`](/api-reference/onboarding/webhooks/webhook-events/paths/trusted_iban_confirmed/post)
* [`SCHEDULED_TRANSFER_STATUS_CHANGED`](/api-reference/onboarding/webhooks/webhook-events/paths/scheduled_transfer_status_changed/post)