Subscribe to Webhooks

PandaDoc notifies your custom application endpoint when a PandaDoc document or template changes state

A Webhook is a callback request from the PandaDoc API to an endpoint specified in your application. Use webhooks as pushed notifications of document events instead of polling various API endpoints for modified data.

Documents are processed asynchronously in PandaDoc. Instead of checking a document status by polling, PandaDoc can push document information to your application when a document changes state. We support multiple webhooks for one application.

Webhook data is in the form of an array and may contain several notification objects at the same time.

👍

Webhook Setup

Set up a webhook for your application in the developer dashboard: https://app.pandadoc.com/a/#/settings/api-dashboard/configuration.

A webhooks always returns basic information and recipient. You can configure fields, tokens, products, and pricing in the webhook settings.

Events

Event NameEvent Description
document_state_changedChanged the state of a document (for example, a document has been sent or completed).
document_updatedAdded a new revision of a document. Please take into account that a new revision is added with a document status change (for example, draft->sent, sent-> draft)
recipient_completedA recipient has completed a document. JSON payload additionally contains action_by and action_on fields with the information on who and when completed the document.
document_deletedA document has been removed.
document_creation_failedDocument creation via API failed.
quote_updatedA user clicked the Save button after updating a quote in the Quote Builder within PandaDoc.
template_createdA user have created a new template.
template_updatedA user have updated an existing template: changed its name, roles, or added new sections to the bundled content.

Webhook Payload Examples

Most webhook payloads follow the example below:

  1. The event that triggered the webhook
  2. Document data in the format similar to the one Document Details returns, depending on the fields you have selected when setting up the webhook:

Here's the webhook example:

[
    {
        "event": "document_state_changed",
        "data":
        {
            "id": "eHCjisfzWydzJnbqnBbvAj",
            "name": "My document for webhooks testing",
            "date_created": "2024-03-18T15:55:03.090372Z",
            "date_modified": "2024-03-18T16:26:46.286951Z",
            "expiration_date": "2024-05-17T16:26:45.583270Z",
            "autonumbering_sequence_name": null,
            "created_by":
            {
                "id": "uHfzrB4Goai39ZkxDRLpMo",
                "email": "[email protected]",
                "first_name": "Test",
                "last_name": "Test",
                "avatar": null,
                "membership_id": "vk5aRcJG4RTd2J83wNGFT5"
            },
            "metadata":
            {},
            "tokens":
            [],
            "fields":
            [],
            "products":
            [],
            "pricing":
            {
                "tables":
                [],
                "quotes":
                [],
                        "merge_rules":
                        []
                    }
                ],
                "total": "130"
            },
            "tags":
            [],
            "status": "document.completed",
            "recipients":
            [],
            "sent_by":
            {
                "id": "uHfzrB4Goai39ZkxDRLpMo",
                "email": "[email protected]",
                "first_name": "Test",
                "last_name": "Test",
                "avatar": null,
                "membership_id": "vk5aRcJG4RTd2J83wNGFT5"
            },
            "grand_total":
            {
                "amount": "130.00",
                "currency": "USD"
            },
            "template":
            {
                "id": "A9VkDBdjqn3HvRx7zyME3n",
                "name": "My template for webhooks testing"
            },
            "version": "2",
            "linked_objects":
            []
        }
    }
]

document_creation_failed payload example

When PandaDoc fails to process your upload and create a document, the webhook payload looks like this:

[
  {
    "event": "document_creation_failed",
    "data": {
      "id": "ptSNky6J4Q8yDh3QKwa7fZ",
      "error": {
        "type": "validation_error",
        "detail": "Role for form field with name='userName' is not provided in payload"
      }
    }
  }
]

It includes the error type and error message.

Debugging and Retries

You can use the Webhooks History tab to see all the request calls PandaDoc is making to your server and response statuses.

2748

Webhook History

2288

Webhook manual retries

PandaDoc does not currently have an automatic retry webhooks mechanism, nor a retry option available via API. If we receive an erroneous response from your web application, we won't retry the webhook post request again.

Timeouts

  • A connection timeout occurs after 5 seconds.
  • A read timeout occurs after 20 seconds.

Subscription deactivation

PandaDoc deactivates your webhook subscription in any of these cases:

  1. Your server responds with the 410 error.
  2. HTTP 4xx client errors and/or HTTP 5xx server errors occur for a period of 7 days, without any successful responses.

Webhooks Verification

When making a PandaDoc API request, your identity is confirmed by the access_token submitted in each request header. To make sure requests are valid and not a request spoofed as PandaDoc in your own application, we support webhook verification.

Using your shared key and a signature, you can verify the contents of a webhook are authentic and un-tampered.

Your shared key can be found in the Developer Dashboard part of your account settings.

🚧

Protect Your Shared Key

Treat your shared key like a password to prevent others from generating a webhook with a payload that could pass a signature verification test.

Webhooks are signed with a signature generated by taking an HMAC-SHA256 hash of the webhook post’s raw HTTP Body (UTF8 encoding). This signature is sent with the Webhook post as a query parameter in your URL by using the signature variable.

https://yourdomain.com/webhook-handler/?signature={signature}
signature = hmac.new(str(shared_key), str(request_body), digestmod=hashlib.sha256).hexdigest()
signature = hash_hmac('sha256', $request_body, $shared_key);
public static bool VerifyHMACSHA256(string sharedKey, string source, string dest) {
    byte[] key = Encoding.UTF8.GetBytes(sharedKey);
    using (HMACSHA256 hmac256 = new HMACSHA256(key))
    {
         var hashedSource = hmac256.ComputeHash(Encoding.UTF8.GetBytes(source));
                   
         if (hashedSource == null || hashedSource.Length == 0)
         {
             return false;
         }

         var hmac = new StringBuilder();
         for (int i = 0; i < hashedSource.Length; i++)
         {
             // the format hex string should always be 2 characters
             hmac.AppendFormat("{0:x2}", hashedSource[i]);
         }

         return hmac.ToString().Equals(dest);
    }
}
const hmac = crypto.createHmac('sha256', sharedKey);

hmac.update(requestBody);

const signature = hmac.digest('hex');

IP safelist

Below is a list of IPs from where you can safely accept PandaDoc webhooks events:

US servers

  • 52.12.31.116/32
  • 52.37.240.175/32
  • 35.167.41.246/32

EU servers

If you only work with http://app.pandadoc.eu/, safelist the IPs below:

  • 3.76.175.239
  • 18.192.96.161
  • 18.158.79.41