Subscribe to Webhooks

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

A Webhook is a HTTP-based callback from the PandaDoc API to your server. Create a webhook subscription to get instant updates about the changes to your documents status or other events.

Documents are processed asynchronously in PandaDoc. You may select to poll PandaDoc API to get the updates, or PandaDoc can push document information to your application when a document changes state.
We support multiple webhooks for each workspace.

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 or using the API.

Document webhooks always return basic information plus recipients. You can configure your webhook to also send fields, tokens, products, and pricing in the subscription settings.

Events

Event NameEvent Description
document_state_changedDocument status was changed. Use it to track when document moved to draft (this means, its async creation is completed), document was viewed, completed, paid etc.
document_completed_pdf_readyDocument was completed, a PDF for it was generated and saved to our e-vault. Use this event to download PDF of completed document via Download Protected Document endpoint.
document_updatedDocument was returned to draft.
recipient_completedA recipient has completed the document. JSON payload additionally contains action_by and action_on fields with the information on who and when completed the document. Doesn't indicate the the document itself is completed.
document_deletedDocument was removed.
document_creation_failedDocument creation via API has failed. Opposite to moving the document to "draft" status.
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_updatedExisting template was update. Includes changes to name, roles or addition of the sections.
template_deletedTemplate was deleted.

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 requests PandaDoc made to your server, and response statuses.

2748

Webhook History

2288

Webhook manual retries

PandaDoc does not currently have an on-demand retry mechanism for webhooks via API. You can manually retry failed webhooks as shown on a picture.

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 whitelist 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/, whitelist the IPs below:

  • 3.76.175.239
  • 18.192.96.161
  • 18.158.79.41