Subscriptions and Webhooks

Snapdocs Connect facilitates the signing experiences for wet, full eClose, and hybrid signing activities. To build trust that Snapdocs is facilitating the closings as expected, Snapdocs Connect offers a broadcast of status and document events, such that connected systems can bring transparency back to the loan. We also offer document events that support the automation of downstream actions, such as downloading signed and executed packages back to the system of record.

Webhooks allow you to subscribe to events happening with the closings. Rather than making an API call when an event happens, Snapdocs Connect can send an HTTP request to an endpoint in your system. You can configure subscribed events in your using the API endpoints detailed below.

Follow the below steps to set up the notifications:

  1. Designate an existing endpoint from your ecosystem to trigger the appropriate work in order to download the signed documents from Snapdocs.
  2. Add a subscription to the event you would like to be notified using the URL from the previous step.

Manage subscriptions

Reference the Subscriptions documentation for more details about the available subscription APIs.

Subscription Events

Listed below are few examples of subscription events.

EventDescription
borrower.esigning_availableA borrower has been prompted to eSign a closing package.
borrower.esigning_completeA borrower has eSigned a closing package.
borrower.preview_availableClosing documents are available for a borrower to review.
borrower.preview_completeA borrower has reviewed closing documents.
document.createdA closing document has been generated.
status.changedAn event resulting in a closing Status change has been generated.

Please see Create a subscription for the complete list of subscribable events.

Webhooks (Listeners)

The endpoint at the URL you specify in webhooks settings will receive requests from Snapdocs Connect like the following:

Method type: POST
Headers:

  • Content-Type: application/json

The webhook event object

KeyTypeDescription
event_idStringA unique identifier for the event object.
closing_uuidStringThe internal unique identifier for a Snapdocs closing.
event_nameStringThe name of the event being broadcast via the webhook.
created_atIntegerThe Unix timestamp for when the event was generated.
PayloadObjectAn object containing additional information about the event.

Example webhook payload

{
	"event_id": "572f592a-fbec-49d9-a28a-88d8e38175be",
	"closing_uuid": "d679e2ad-278d-e547-9756-84639ba3865b",
	"event_name": "borrower.preview_available",
	"created_at": 1618936005,
	"payload": {
		"external_identifiers": [{
			"external_system": "other_los",
			"external_type": "file_number",
			"value": "1234"
		}]
	}
}

📘

external_identifiers may have 0 or more records .

Handle the Snapdocs requests

  • Event type — Your webhook endpoints should be configured to receive only the types of events required by your integration. Listening for extra events (or all events) will put undue strain on your server and is not recommended.

  • Handle duplicate events — Webhook endpoints might occasionally receive the same event more than once. We advise you to guard against duplicated event receipts by making your event processing idempotent.

  • Verify events are sent from Snapdocs — Use webhook signatures to verify if the events are sent from Snapdocs.

  • Verify event timestamp — We do not guarantee that you get these notifications in the order they occurred. Please use the timestamp in the event object for each notification to determine when the notification occurred.

Webhook responses

If the webhook arrives successfully, please respond with a 200 Http status. We encourage client integrations to use different Http status if webhook does not arrive successfully or if webhook's request processing fails.

📘

Respond with a 200 upon successful arrival of webhook prior to processing an event and regardless of success in event processing.
If Snapdocs does not receive a 200 response, Snapdocs will attempt to redeliver the webhook event. Snapdocs retries run at an exponential backoff over the course of many hours.

Please refer to the response examples below for success and bad requests.

HTTP/1.1 200 OK
Content-Type: application/json

{
	"status": "OK",
	"code": 200,
	"message": "webhook received successfully"
}
HTTP/1.1 400 Bad Request
Content-Type: application/json

{
	"status": "Bad Request",
	"code": 400,
	"message": "[reason for failure]"
}

Retries

If your service has problems handling notifications at any time, we will attempt to re-send failed notifications up to 13 times over the course of 24 hours from the first attempt, with increasing durations between each subsequent attempt.

Example webhook service

Below is a simple AWS Lambda function to demonstrate the webhook at Lenders.

import json
import os
import logging

logger = logging.getLogger()
LOG_LEVEL = os.environ.get('LOG_LEVEL', 'WARNING').upper()
logger.setLevel(level=LOG_LEVEL)

success_code = 200
success_body = {
        "status": "OK",
        "code": success_code,
        "message": "webhook received successfully"
    }


def download_scanback_document(document_uuid):
    logger.info("downloading scanback document %s", document_uuid)
    pass


def process_snapdocs_event(event_body):
    try:
        # check event body
        logger.debug("%s %s %s", event_body.get('event_id'), event_body.get('closing_uuid'),
                    event_body.get('event_name'))
        logger.debug("lender system attributes %s", event_body.get("payload"))
        # typically we put the message into a message broker to be processed downstream
        if event_body.get('event_name') == 'document.created':
            if event_body.get('document_type') == 'scanback_documents':
                download_scanback_document(event_body.get('document_uuid'))
    except Exception as e:
        logger.error(e)
        return 500, {
            "status": "Failed to process event",
            "code": 500,
            "message": f"webhook received but could not process {str(e)}"
        }
    return success_code, success_body


def handle(event, context):
    code = success_code
    response_body = success_body
    try:
        if event['body']:
            body = json.loads(event['body'])
            code, response_body = process_snapdocs_event(body)
        else:
            raise "no data found in the post body"
    except Exception as e:
        code = 400
        response_body = {
            "status": "Bad Request",
            "code": code,
            "message": f"failed to parse the webhook, {str(e)}"
        }
    finally:
        return {"statusCode": code, "body": json.dumps(response_body)}

Note that in production the web listener more likely will return 200 status code upon receiving the message, and push it to a message broker or queue to have it processed by downstream systems.