openapi: 3.0.2 info: title: Automated Spreading and Analysis Api-Key Webhooks API version: 1.3.1 description: "This [REST API](https://en.wikipedia.org/wiki/Representational_state_transfer) allows you to interact with the Automated Spreading processing and insights engine. \n\n# Authentication\n\nThis API uses API keys generated from a Automated Spreading User account. To get access to your User account, speak with your Automated Spreading account manager.\n\n# Accepted Media Types\n\n| File Type      | Extension(s) | Content-Type(s) |\n| -------------------------------- |-------------- | ------------ |\n| PDF File | .pdf | `application/pdf` , `application/x-pdf` |\n| Excel File | .xls | `application/vnd.ms-excel` |\n| Excel File | .xlsx | `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` |\n| Excel File | .xlsm | `application/vnd.ms-excel.sheet.macroEnabled.12` |\n| PNG Image | .png | `image/png` |\n| GIF Image | .gif | `image/gif` |\n| JPG Image | .jpg, .jpeg | `image/jpeg` |\n| GIF Image | .gif | `image/gif` |\n| JSON File | .json | `application/json` |\n\n\n# Getting Started\n\n1. [Create a Borrower](#operation/createBorrower) \n2. [Add a file](#operation/createDocumentFile) for that Borrower.\n3. Analyze in the Automated Spreading App\n\n****" x-logo: url: https://manual-public-web-static-resources.s3.amazonaws.com/public-api/Moodys-icon.svg contact: name: Moody's Automated Spreading Customer Support email: MA_NMR_Support@moodys.com security: - API_Key: [] tags: - name: Webhooks description: "## Supported Events\n\nThe following are events you can listen to via webhooks.\n\n| Event Type      | Description | \n| --------------------------------------------- | -------------------------- |\n| GlobalCashflow.Saved | A global cashflow analysis has been saved by the user | \n| Analysis.Saved | An analysis has been saved by the user | \n| DocumentFile.Received | File saved/received to the Fincura Platform | \n| DocumentFile.Processing | File is being processed by the Fincura Pipeline | \n| DocumentFile.HumanRequired | Human Intervention is required for file | \n| DocumentFile.SpreadComplete | File data spread to a template (data now normalized/standardized) | \n| DocumentFile.Error | Error processing DocumentFile | \n| BulkFile.Received | Bulk File saved/received to the Fincura Platform | \n| BulkFile.Processing | Bulk File is being processed by the Fincura Pipeline | \n| BulkFile.Processed | Bulk File has completed processing | \n| BulkFile.Error | Error processing BulkFile | \n| * | Wildcard for all events | \n| Template.Changed | A change was detected in a template for spreading, global cashflow, or DSCR analysis | \n| Borrower.CreatedFromImport | A Borrower was created from processing a file import | \n| CalculatedStatement.Update | A calculated statement has been updated by the user | \n| ForecastedStatement.Update | A forecasted statement has been updated by the user | \n| AnnualizedStatement.Update | An annualized statement has been updated by the user | \n\n\n## Webhook Request Body\n\nThe webhook POST request body will contain the following json parameters:\n\n- `event` the event that triggered the callback (e.g. `DocumentFile.Received`)\n- `transaction_id` a unique UUID representing the event\n- `payload` additional event specific parameters\n\n## Webhook Signatures\n\nAll 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.\n\nThe header contains a list of comma seperated key value pairs:\n\n- `t` - UTC timestamp of the request\n- `uuid` - the UUID of the webhook record\n- `v1` - signature of the message [(HMAC_SHA256)](https://en.wikipedia.org/wiki/HMAC)\n\n### Obtaining the Tenant Signing Key\n\nSee: [Get Tenant Settings](#operation/readTenantSettings) which returns a `webhook_signing_key`\n\n### Calculating the Message Signature\n\nThe 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.\n\nThe signature is generated by creating a HMAC SHA256 signature of that message using the tenant signing key.\n\nThe 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.\n\nNOTE: 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.\n\nPython example of validating a signature:\n```\n#!/usr/bin/env python\nimport hmac\nimport hashlib \n\nSIGNING_KEY = \"tenant signing key\" # Obtained via API call to `operation/readTenantSettings`\n\ndef create_message(timestamp, uuid, body):\n return f\"{timestamp}.{uuid}.{body}\"\n\ndef create_sha256_signature(key, message):\n message = message.encode()\n return hmac.new(key.encode('utf-8'), message, hashlib.sha256).hexdigest().upper()\n\ndef verify_fincura_header(signature_header, request_body):\n header_params = {kv[0]:kv[1] for kv in param.split('=') for param in signature_header.split(',')} \n message = create_message(header_params['t'], header_params['uuid'], request_body)\n signature = create_sha256_signature(SIGNING_KEY, message)\n\n if signature != header_params['v1']:\n raise Exception('Webhook Signature Mismatch')\n\n// for example (signature shortened for brevity)\nheader_value = 't=1602104227,uuid=5262fc90-eb99-4bd8-8712-ae50fc71ea92,v1=E9397...9C3332'\nrequest_body = '{\"event:\"value\"}'\n\nverify_fincura_header(header_value, request_body)\n```" paths: /v1/webhook: get: operationId: listWebhooks description: List registered webhooks parameters: - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer responses: '200': content: application/json: schema: type: object required: - count - results properties: count: type: integer example: 123 next: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=400&limit=100 previous: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=200&limit=100 results: type: array items: $ref: '#/components/schemas/Webhook' description: '' tags: - Webhooks summary: List webhooks post: operationId: createWebhook description: Register a webhook. This call in idempotent. parameters: [] requestBody: content: application/json: schema: $ref: '#/components/schemas/Webhook' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/Webhook' multipart/form-data: schema: $ref: '#/components/schemas/Webhook' responses: '201': content: application/json: schema: $ref: '#/components/schemas/Webhook' description: '' tags: - Webhooks summary: Create a webhook /v1/webhook/{uuid}: get: operationId: retrieveWebhook description: Retrieve a registerted webhook parameters: - name: uuid in: path required: true description: '' schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/Webhook' description: '' tags: - Webhooks summary: Get webhook info delete: operationId: destroyWebhook description: Delete a webhook from Fincura. parameters: - name: uuid in: path required: true description: '' schema: type: string responses: '204': description: '' tags: - Webhooks summary: Delete a webhook components: schemas: Webhook: type: object properties: uuid: type: string format: uuid readOnly: true event_type: enum: - GlobalCashflow.Saved - Analysis.Saved - DocumentFile.Received - DocumentFile.Processing - DocumentFile.HumanRequired - DocumentFile.SpreadComplete - DocumentFile.Error - BulkFile.Received - BulkFile.Processing - BulkFile.Processed - BulkFile.Error - '*' - Template.Changed - Borrower.CreatedFromImport - CalculatedStatement.Update - ForecastedStatement.Update - AnnualizedStatement.Update type: string webhook_url: type: string format: uri maxLength: 2500 pattern: "^(?:[a-z0-9.+-]*)://(?:[^\\s:@/]+(?::[^\\s:@/]*)?@)?(?:(?:0|25[0-5]|2[0-4][0-9]|1[0-9]?[0-9]?|[1-9][0-9]?)(?:\\.(?:0|25[0-5]|2[0-4][0-9]|1[0-9]?[0-9]?|[1-9][0-9]?)){3}|\\[[0-9a-f:.]+\\]|([a-z¡-\uFFFF0-9](?:[a-z¡-\uFFFF0-9-]{0,61}[a-z¡-\uFFFF0-9])?(?:\\.(?!-)[a-z¡-\uFFFF0-9-]{1,63}(?