Fincura Webhooks API
## Supported Events The following are events you can listen to via webhooks. | Event Type | Description | | --------------------------------------------- | -------------------------- | | GlobalCashflow.Saved | A global cashflow analysis has been saved by the user | | Analysis.Saved | An analysis has been saved by the user | | DocumentFile.Received | File saved/received to the Fincura Platform | | DocumentFile.Processing | File is being processed by the Fincura Pipeline | | DocumentFile.HumanRequired | Human Intervention is required for file | | DocumentFile.SpreadComplete | File data spread to a template (data now normalized/standardized) | | DocumentFile.Error | Error processing DocumentFile | | BulkFile.Received | Bulk File saved/received to the Fincura Platform | | BulkFile.Processing | Bulk File is being processed by the Fincura Pipeline | | BulkFile.Processed | Bulk File has completed processing | | BulkFile.Error | Error processing BulkFile | | * | Wildcard for all events | | Template.Changed | A change was detected in a template for spreading, global cashflow, or DSCR analysis | | Borrower.CreatedFromImport | A Borrower was created from processing a file import | | CalculatedStatement.Update | A calculated statement has been updated by the user | | ForecastedStatement.Update | A forecasted statement has been updated by the user | | AnnualizedStatement.Update | An annualized statement has been updated by the user | ## Webhook Request Body The webhook POST request body will contain the following json parameters: - `event` the event that triggered the callback (e.g. `DocumentFile.Received`) - `transaction_id` a unique UUID representing the event - `payload` additional event specific parameters ## Webhook Signatures All of our outgoing webhook requests contain a `X-Fincura-Signature` header which has been signed by a tenant specific key. This allows a 3rd party to validate requests are from Fincura's servers. The header contains a list of comma seperated key value pairs: - `t` - UTC timestamp of the request - `uuid` - the UUID of the webhook record - `v1` - signature of the message [(HMAC_SHA256)](https://en.wikipedia.org/wiki/HMAC) ### Obtaining the Tenant Signing Key See: [Get Tenant Settings](#operation/readTenantSettings) which returns a `webhook_signing_key` ### Calculating the Message Signature The webhook message signature is calculated by joining the `timestamp` of the request, the `uuid` of the webhook and the `body` of the request with periods (`.`) into a single string. The signature is generated by creating a HMAC SHA256 signature of that message using the tenant signing key. The calculated signature can then be matched to the `v1` signature in the `X-Fincura-Signature` header to verify the request originated from Fincura and the body has not been tampered with. NOTE: Fincura periodically rotates signing keys for security purposes, if a signature mismatch if found, it is recommended to make sure you are using the latest signing key. Python example of validating a signature: ``` #!/usr/bin/env python import hmac import hashlib SIGNING_KEY = "tenant signing key" # Obtained via API call to `operation/readTenantSettings` def create_message(timestamp, uuid, body): return f"{timestamp}.{uuid}.{body}" def create_sha256_signature(key, message): message = message.encode() return hmac.new(key.encode('utf-8'), message, hashlib.sha256).hexdigest().upper() def verify_fincura_header(signature_header, request_body): header_params = {kv[0]:kv[1] for kv in param.split('=') for param in signature_header.split(',')} message = create_message(header_params['t'], header_params['uuid'], request_body) signature = create_sha256_signature(SIGNING_KEY, message) if signature != header_params['v1']: raise Exception('Webhook Signature Mismatch') // for example (signature shortened for brevity) header_value = 't=1602104227,uuid=5262fc90-eb99-4bd8-8712-ae50fc71ea92,v1=E9397...9C3332' request_body = '{"event:"value"}' verify_fincura_header(header_value, request_body) ```