Webhooks V2

Webhooks allow your application to be notified of changes in a transaction's state rather than requesting the state from the API directly.

Proof webhooks allow your application to be notified of changes in a transaction's status. The POST request sent from Proof contains a JSON body that describes the event and data relevant to the event.

Webhook Setup

To set up and modify your Proof webhooks, you can call our Webhook API endpoints from the command line, or tools like Postman.

What's New with Webhooks V2?

Webhooks V2 supports a number of enhancements from V1:

Migrating to Webhooks V2

It is simple to move to Webhooks V2:

  • Determine the specific events you would like to subscribe to
  • Make a DELETE call to /webhooks to remove the V1 subscription
  • Make a POST call to /v2/webhooks to subscribe to V2 webhooks (see format here)
  • Edit any firewall restrictions on your network that may restrict our ability to send you webhook calls

Please note: a small number of events that were available in Webhooks V1 will no longer be available in Webhooks V2:

  • transaction.title_added

Webhook Subscriptions

SubscriptionDescriptionWebhook Body
notary.*This specific subscription string will subscribe the organization to all notary sub-events.See individual notary event bodies below.
notary.compliantThis event is triggered when a notary's profile is reviewed and marked compliant by our compliance team.{ event: 'notary.compliant' data: { notary_id: ID of notary status: status of event } }
notary.createdThis event is triggered when a new notary is created in your organization.{ event: 'notary.created' data: { notary_id: ID of notary status: status of event } }
notary.needs_reviewThis event is triggered when a Notary has finished onboarding and needs to be reviewed by our compliance team.{ event: 'notary.needs_review' data: { notary_id: ID of notary status: status of event } }
notary.non_compliantThis event is triggered when a notary is marked non-compliant after review by our compliance team.{ event: 'notary.non_compliant' data: { notary_id: ID of notary status: status of event } }
notary.signer_readyThis event is triggered when a notary has been assigned to a transaction and signer has requested a meeting{ event: 'notary.signer_ready' data: { notary_id: ID of notary transaction_access_link: link to transaction } }
transaction.*This specific subscription string will subscribe the organization to all transaction sub-events.See individual transaction event bodies below.
transaction.createdThis event triggers whenever a transaction is created.{ "event":"transaction.created","data":{"transaction_id":transaction ID (string)}}
transaction.deletedThis event triggers whenever a transaction is deleted.{"event":"transaction.deleted","data":{"transaction_id":transaction ID (string)}}
transaction.expiredThis event triggers whenever a transaction has passed a set expiration date.{"event": 'transaction.expired',"data":{transaction_id:transaction ID (string)}}
transaction.updatedThis event triggers whenever a transaction is updated.{"event":"transaction.updated","data":{"transaction_id":transaction ID (string)}}
transaction.recalledThis event triggers whenever a transaction is recalled.{"event":"transaction.recalled","data":{"transaction_id":transaction ID (string)}}
transaction.document.uploadThis event triggers whenever a document bundle is uploaded to a transaction.{"event":"transaction.document.upload","data":{"transaction_id":transaction ID (string),"organization_name":name of org (string),"document": {"id":document ID (string),"name":document name (string)},"date_occurred":ISO8601 timestamp with timezone (string)}}
transaction.document.processedThis event triggers when a document has finished processing. Any white text tags or template matching has finished and the transaction is safe to activate at this point.{"event":"transaction.document.processed","data":{"transaction_id":transaction ID (string),"organization_name":name of org (string),"document": {"id":document ID (string),"name":document name (string)},"date_occurred":ISO8601 timestamp with timezone (string)}}
transaction.reviewed (Lender only)This event triggers whenever a title agency has finished reviewing a document in a collaborative transaction{"event":"transaction.document.reviewed","data":{"transaction_id:transaction ID (string),"organization_name": name of org (string),"document":{"id":document ID (string),"name":document name (string)},"date_occurred": ISO8601 timestamp with timezone}}
transaction.meeting.createdThis event triggers anytime signer(s) join a meeting. This event can occur when a notary or trusted referee picks up a meeting, or when concurrent signers join an ongoing meeting.{ event: 'transaction.meeting.created', data: { transaction_id: "transaction ID", date_occurred: "ISO8601 timestamp with timezone", signers: [ { first_name: "first name", middle_name: "middle name", last_name: "last name", external_id: "signer external ID", signer_id: "signer ID" } ], meeting: { meeting_id: "meeting ID" } } }
transaction.meeting.failedThis event triggers when a notary terminates a meeting with failure, or when a meeting is terminated after inactivity from participants (e.g. participants lost connection).{ event: 'transaction.meeting.failed', data: { transaction_id: "transaction ID", date_occurred: "ISO8601 timestamp with timezone", meeting: { meeting_id: "meeting ID" } } }
transaction.meeting.requestedThis event triggers anytime signer(s) request a meeting. It can include multiple signers if they are collocated.{ event: 'transaction.meeting.requested', data: { transaction_id: "transaction ID", date_occurred: "ISO8601 timestamp with timezone", signers: [ { first_name: "first name", middle_name: "middle name", last_name: "last name", external_id: "signer external ID", signer_id: "signer ID" } ] } }
transaction.meeting.video.processedThis event triggers once the notary or trusted referee meeting has finished and the recording has finished processing. You can use this event to trigger a call to Retrieve Notarization Record to fetch the meeting video.{ event: 'transaction.meeting.video.processed' data: { date_occurred: ISO8601 timestamp with timezone, meeting: { meeting_id: meeting id api_url: "https://api.proof.com/v1/notarization_records/meeting_id" }, transaction_id: ID of transaction, }, event: "transaction.meeting.video.processing.complete" }
transaction.notary.assignedThis event triggers anytime a notary is assigned to a transaction.{ event: 'transaction.notary.assigned' data: { transaction_id: ID of transaction organization_name: name of calling org date_occurred: ISO8601 timestamp with timezone } }
transaction.sent_to_closing_opsThis event triggers whenever a transaction is sent to the Proof closing ops team.{ event: 'transaction.sent_to_closing_ops' data: { transaction_id: ID of transaction organization_name: name of calling org date_occurred: ISO8601 timestamp with timezone } }
transaction.sent_to_signerThis event triggers whenever a transaction is sent to a signer.{ event: 'transaction.sent_to_signer' data: { transaction_id: ID of transaction organization_name: name of calling org date_occurred: ISO8601 timestamp with timezone } }
transaction.sent_to_title_agencyThis event triggers whenever a transaction is sent from a lender to a title agency in the real estate collab workflow.{ event: 'transaction.sent_to_title_agency' data: { transaction_id: ID of transaction organization_name: name of calling org date_occurred: ISO8601 timestamp with timezone } }
transaction.signer.kba_failedThis event triggers when a signer failed KBA and is locked out due to too many failed attempts.{ event: 'transaction.signer.kba_failed', data: { transaction_id: "transaction ID", date_occurred: "ISO8601 timestamp with timezone", signers: [{ first_name: "first name", middle_name: "middle name", last_name: "last name", external_id: "signer external ID", signer_id: "signer ID" } ] } }
transaction.signer.kba_passedThis event triggers when a signer passed KBA for the transaction.{ event: 'transaction.signer.kba_passed', data: { transaction_id: "transaction ID", date_occurred: "ISO8601 timestamp with timezone", signers: [{ first_name: "first name", middle_name: "middle name", last_name: "last name", external_id: "signer external ID", signer_id: "signer ID" } ] } }
transaction.underwriter.not_availableThis event triggers whenever an underwriter is not available for a transaction.{ event: 'transaction.underwriter.not_available' data: { id: ID of transaction } }
transaction.partially_completedThis event triggers whenever a transaction is partially completed by a signer. The event re-triggers anytime another signer completes their portion of the document. Only applies to signers in separate signing sessions.{ event: 'transaction.partially_completed', data: { transaction_id: "transaction ID", date_occurred: "ISO8601 timestamp with timezone", signers: [{ first_name: "first name", middle_name: "middle name", last_name: "last name", external_id: "signer external ID", signer_id: "signer ID" } ] } }
transaction.completedThis event triggers whenever a transaction moves into the completed state, following successful termination of the notary meeting or completion of the document signing.{"event": "transaction.completed","data":{ transaction_id: ID of transaction,status:null}}
transaction.releasedThis event triggers when the documents are released (available for download from the API). Please listen for this event when using a signer-paid workflow as documents may not be available when transaction.completed fires.{"event":"transaction.released","data":{"transaction_id":"ot_XXXXXX"}}
transaction.completed_with_rejectionsThis event triggers when a notarization transaction is completed but a subset of documents have been rejected for notarization by the notary.{ event: "transaction.completed_with_rejections", data: { transaction_id: ID of transaction } }

Errors and Retries

The webhook expects the receiver to respond with a 200 response code. If for some reason Proof cannot reach the webhook URL or your application responds with any response code other than 200, Proof will retry the request

Retry Back Off

We recommend an idempotent webhook implementation. Our webhooks have a 30 second timeout - should we not receive a response in 30 seconds, we will retry the response. Requests that are retried for any reason use an back-off algorithm that will make up to 3 attempts across roughly two minutes.

Note that webhooks can be sent more than once and delivery is not guaranteed to be in order. The expected behavior is for your application to manage the state and skip to the latest status.

Security

Webhook messages are signed so your application can verify that the sender is Proof. Webhook requests contain an X-Notarize-Signature header with a hexadecimal HMAC signature of the request body, using your API key as the key and SHA256 as the hash function.

You can verify the authenticity of the webhook by computing the signature with your API key and request body, and comparing it to the value in the X-Notarize-Signature header.