mirror of
https://github.com/documenso/documenso.git
synced 2026-08-24 23:32:29 +10:00
docs(webhooks): address review findings
- qualify the SSRF guard as best-effort (no DNS-rebinding coverage, fails open on lookup errors) - use envelope IDs from the actual generator alphabet (no digits possible) - fix template-events intro: templateId is null except on TEMPLATE_USED
This commit is contained in:
@@ -114,7 +114,7 @@ Triggered when a new document is created.
|
|||||||
"event": "DOCUMENT_CREATED",
|
"event": "DOCUMENT_CREATED",
|
||||||
"payload": {
|
"payload": {
|
||||||
"id": 10,
|
"id": 10,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"externalId": null,
|
"externalId": null,
|
||||||
"userId": 1,
|
"userId": 1,
|
||||||
"authOptions": null,
|
"authOptions": null,
|
||||||
@@ -148,7 +148,7 @@ Triggered when a new document is created.
|
|||||||
"recipients": [
|
"recipients": [
|
||||||
{
|
{
|
||||||
"id": 52,
|
"id": 52,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"documentId": 10,
|
"documentId": 10,
|
||||||
"templateId": null,
|
"templateId": null,
|
||||||
"email": "signer@example.com",
|
"email": "signer@example.com",
|
||||||
@@ -170,7 +170,7 @@ Triggered when a new document is created.
|
|||||||
"Recipient": [
|
"Recipient": [
|
||||||
{
|
{
|
||||||
"id": 52,
|
"id": 52,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"documentId": 10,
|
"documentId": 10,
|
||||||
"templateId": null,
|
"templateId": null,
|
||||||
"email": "signer@example.com",
|
"email": "signer@example.com",
|
||||||
@@ -208,7 +208,7 @@ The document status changes to `PENDING` and recipients have `sendStatus: "SENT"
|
|||||||
"event": "DOCUMENT_SENT",
|
"event": "DOCUMENT_SENT",
|
||||||
"payload": {
|
"payload": {
|
||||||
"id": 10,
|
"id": 10,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"externalId": null,
|
"externalId": null,
|
||||||
"userId": 1,
|
"userId": 1,
|
||||||
"authOptions": null,
|
"authOptions": null,
|
||||||
@@ -242,7 +242,7 @@ The document status changes to `PENDING` and recipients have `sendStatus: "SENT"
|
|||||||
"recipients": [
|
"recipients": [
|
||||||
{
|
{
|
||||||
"id": 52,
|
"id": 52,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"documentId": 10,
|
"documentId": 10,
|
||||||
"templateId": null,
|
"templateId": null,
|
||||||
"email": "signer@example.com",
|
"email": "signer@example.com",
|
||||||
@@ -264,7 +264,7 @@ The document status changes to `PENDING` and recipients have `sendStatus: "SENT"
|
|||||||
"Recipient": [
|
"Recipient": [
|
||||||
{
|
{
|
||||||
"id": 52,
|
"id": 52,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"documentId": 10,
|
"documentId": 10,
|
||||||
"templateId": null,
|
"templateId": null,
|
||||||
"email": "signer@example.com",
|
"email": "signer@example.com",
|
||||||
@@ -302,14 +302,14 @@ The recipient's `readStatus` changes to `OPENED`.
|
|||||||
"event": "DOCUMENT_OPENED",
|
"event": "DOCUMENT_OPENED",
|
||||||
"payload": {
|
"payload": {
|
||||||
"id": 10,
|
"id": 10,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"status": "PENDING",
|
"status": "PENDING",
|
||||||
"title": "contract.pdf",
|
"title": "contract.pdf",
|
||||||
"source": "DOCUMENT",
|
"source": "DOCUMENT",
|
||||||
"recipients": [
|
"recipients": [
|
||||||
{
|
{
|
||||||
"id": 52,
|
"id": 52,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"email": "signer@example.com",
|
"email": "signer@example.com",
|
||||||
"name": "John Doe",
|
"name": "John Doe",
|
||||||
"role": "SIGNER",
|
"role": "SIGNER",
|
||||||
@@ -337,7 +337,7 @@ The recipient's `signingStatus` changes to `SIGNED` and `signedAt` is populated.
|
|||||||
"event": "DOCUMENT_SIGNED",
|
"event": "DOCUMENT_SIGNED",
|
||||||
"payload": {
|
"payload": {
|
||||||
"id": 10,
|
"id": 10,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"status": "COMPLETED",
|
"status": "COMPLETED",
|
||||||
"title": "contract.pdf",
|
"title": "contract.pdf",
|
||||||
"source": "DOCUMENT",
|
"source": "DOCUMENT",
|
||||||
@@ -345,7 +345,7 @@ The recipient's `signingStatus` changes to `SIGNED` and `signedAt` is populated.
|
|||||||
"recipients": [
|
"recipients": [
|
||||||
{
|
{
|
||||||
"id": 51,
|
"id": 51,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"email": "signer@example.com",
|
"email": "signer@example.com",
|
||||||
"name": "John Doe",
|
"name": "John Doe",
|
||||||
"role": "SIGNER",
|
"role": "SIGNER",
|
||||||
@@ -372,14 +372,14 @@ Triggered when an individual recipient completes their required action (signing,
|
|||||||
"event": "DOCUMENT_RECIPIENT_COMPLETED",
|
"event": "DOCUMENT_RECIPIENT_COMPLETED",
|
||||||
"payload": {
|
"payload": {
|
||||||
"id": 10,
|
"id": 10,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"status": "PENDING",
|
"status": "PENDING",
|
||||||
"title": "contract.pdf",
|
"title": "contract.pdf",
|
||||||
"source": "DOCUMENT",
|
"source": "DOCUMENT",
|
||||||
"recipients": [
|
"recipients": [
|
||||||
{
|
{
|
||||||
"id": 52,
|
"id": 52,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"email": "signer@example.com",
|
"email": "signer@example.com",
|
||||||
"name": "John Doe",
|
"name": "John Doe",
|
||||||
"role": "SIGNER",
|
"role": "SIGNER",
|
||||||
@@ -408,7 +408,7 @@ The document status changes to `COMPLETED` and `completedAt` is set.
|
|||||||
"event": "DOCUMENT_COMPLETED",
|
"event": "DOCUMENT_COMPLETED",
|
||||||
"payload": {
|
"payload": {
|
||||||
"id": 10,
|
"id": 10,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"externalId": null,
|
"externalId": null,
|
||||||
"userId": 1,
|
"userId": 1,
|
||||||
"authOptions": null,
|
"authOptions": null,
|
||||||
@@ -442,7 +442,7 @@ The document status changes to `COMPLETED` and `completedAt` is set.
|
|||||||
"recipients": [
|
"recipients": [
|
||||||
{
|
{
|
||||||
"id": 50,
|
"id": 50,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"documentId": 10,
|
"documentId": 10,
|
||||||
"templateId": null,
|
"templateId": null,
|
||||||
"email": "reviewer@example.com",
|
"email": "reviewer@example.com",
|
||||||
@@ -465,7 +465,7 @@ The document status changes to `COMPLETED` and `completedAt` is set.
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 51,
|
"id": 51,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"documentId": 10,
|
"documentId": 10,
|
||||||
"templateId": null,
|
"templateId": null,
|
||||||
"email": "signer@example.com",
|
"email": "signer@example.com",
|
||||||
@@ -490,7 +490,7 @@ The document status changes to `COMPLETED` and `completedAt` is set.
|
|||||||
"Recipient": [
|
"Recipient": [
|
||||||
{
|
{
|
||||||
"id": 50,
|
"id": 50,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"documentId": 10,
|
"documentId": 10,
|
||||||
"templateId": null,
|
"templateId": null,
|
||||||
"email": "reviewer@example.com",
|
"email": "reviewer@example.com",
|
||||||
@@ -513,7 +513,7 @@ The document status changes to `COMPLETED` and `completedAt` is set.
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 51,
|
"id": 51,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"documentId": 10,
|
"documentId": 10,
|
||||||
"templateId": null,
|
"templateId": null,
|
||||||
"email": "signer@example.com",
|
"email": "signer@example.com",
|
||||||
@@ -554,14 +554,14 @@ The recipient's `signingStatus` changes to `REJECTED` and `rejectionReason` cont
|
|||||||
"event": "DOCUMENT_REJECTED",
|
"event": "DOCUMENT_REJECTED",
|
||||||
"payload": {
|
"payload": {
|
||||||
"id": 10,
|
"id": 10,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"status": "PENDING",
|
"status": "PENDING",
|
||||||
"title": "contract.pdf",
|
"title": "contract.pdf",
|
||||||
"source": "DOCUMENT",
|
"source": "DOCUMENT",
|
||||||
"recipients": [
|
"recipients": [
|
||||||
{
|
{
|
||||||
"id": 52,
|
"id": 52,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"email": "signer@example.com",
|
"email": "signer@example.com",
|
||||||
"name": "John Doe",
|
"name": "John Doe",
|
||||||
"role": "SIGNER",
|
"role": "SIGNER",
|
||||||
@@ -591,7 +591,7 @@ This event is **not** triggered when a recipient hides a document from their inb
|
|||||||
"event": "DOCUMENT_CANCELLED",
|
"event": "DOCUMENT_CANCELLED",
|
||||||
"payload": {
|
"payload": {
|
||||||
"id": 7,
|
"id": 7,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"externalId": null,
|
"externalId": null,
|
||||||
"userId": 3,
|
"userId": 3,
|
||||||
"authOptions": null,
|
"authOptions": null,
|
||||||
@@ -625,7 +625,7 @@ This event is **not** triggered when a recipient hides a document from their inb
|
|||||||
"recipients": [
|
"recipients": [
|
||||||
{
|
{
|
||||||
"id": 7,
|
"id": 7,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"documentId": 7,
|
"documentId": 7,
|
||||||
"templateId": null,
|
"templateId": null,
|
||||||
"email": "signer@example.com",
|
"email": "signer@example.com",
|
||||||
@@ -647,7 +647,7 @@ This event is **not** triggered when a recipient hides a document from their inb
|
|||||||
"Recipient": [
|
"Recipient": [
|
||||||
{
|
{
|
||||||
"id": 7,
|
"id": 7,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"documentId": 7,
|
"documentId": 7,
|
||||||
"templateId": null,
|
"templateId": null,
|
||||||
"email": "signer@example.com",
|
"email": "signer@example.com",
|
||||||
@@ -685,14 +685,14 @@ The recipient's `expiresAt` contains the signing deadline, and `expirationNotifi
|
|||||||
"event": "RECIPIENT_EXPIRED",
|
"event": "RECIPIENT_EXPIRED",
|
||||||
"payload": {
|
"payload": {
|
||||||
"id": 10,
|
"id": 10,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"status": "PENDING",
|
"status": "PENDING",
|
||||||
"title": "contract.pdf",
|
"title": "contract.pdf",
|
||||||
"source": "DOCUMENT",
|
"source": "DOCUMENT",
|
||||||
"recipients": [
|
"recipients": [
|
||||||
{
|
{
|
||||||
"id": 52,
|
"id": 52,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"documentId": 10,
|
"documentId": 10,
|
||||||
"templateId": null,
|
"templateId": null,
|
||||||
"email": "signer@example.com",
|
"email": "signer@example.com",
|
||||||
@@ -722,14 +722,14 @@ Triggered when a reminder email is sent to a recipient who has not yet completed
|
|||||||
"event": "DOCUMENT_REMINDER_SENT",
|
"event": "DOCUMENT_REMINDER_SENT",
|
||||||
"payload": {
|
"payload": {
|
||||||
"id": 10,
|
"id": 10,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"status": "PENDING",
|
"status": "PENDING",
|
||||||
"title": "contract.pdf",
|
"title": "contract.pdf",
|
||||||
"source": "DOCUMENT",
|
"source": "DOCUMENT",
|
||||||
"recipients": [
|
"recipients": [
|
||||||
{
|
{
|
||||||
"id": 52,
|
"id": 52,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"email": "signer@example.com",
|
"email": "signer@example.com",
|
||||||
"name": "John Doe",
|
"name": "John Doe",
|
||||||
"role": "SIGNER",
|
"role": "SIGNER",
|
||||||
@@ -748,7 +748,7 @@ Triggered when a reminder email is sent to a recipient who has not yet completed
|
|||||||
|
|
||||||
## Template Events
|
## Template Events
|
||||||
|
|
||||||
Template events track changes to reusable document templates. Template payloads use the same structure as document payloads, with `source` set to `TEMPLATE` and `templateId` populated.
|
Template events track changes to reusable document templates. Template payloads use the same structure as document payloads. For `TEMPLATE_CREATED`, `TEMPLATE_UPDATED`, and `TEMPLATE_DELETED` the template's own legacy numeric ID is in `id` and `templateId` is `null`. Only `TEMPLATE_USED` — whose payload describes the new document envelope created from the template — carries the originating template's legacy ID in `templateId`, with `source` set to `TEMPLATE`.
|
||||||
|
|
||||||
### `template.created`
|
### `template.created`
|
||||||
|
|
||||||
@@ -761,7 +761,7 @@ Triggered when a new template is created.
|
|||||||
"event": "TEMPLATE_CREATED",
|
"event": "TEMPLATE_CREATED",
|
||||||
"payload": {
|
"payload": {
|
||||||
"id": 10,
|
"id": 10,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"title": "My Template",
|
"title": "My Template",
|
||||||
"status": "DRAFT",
|
"status": "DRAFT",
|
||||||
"templateId": null,
|
"templateId": null,
|
||||||
@@ -784,7 +784,7 @@ Triggered when a template's settings, recipients, or fields are modified.
|
|||||||
"event": "TEMPLATE_UPDATED",
|
"event": "TEMPLATE_UPDATED",
|
||||||
"payload": {
|
"payload": {
|
||||||
"id": 10,
|
"id": 10,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"title": "My Updated Template",
|
"title": "My Updated Template",
|
||||||
"status": "DRAFT",
|
"status": "DRAFT",
|
||||||
"templateId": null,
|
"templateId": null,
|
||||||
@@ -807,7 +807,7 @@ Triggered when a template is deleted.
|
|||||||
"event": "TEMPLATE_DELETED",
|
"event": "TEMPLATE_DELETED",
|
||||||
"payload": {
|
"payload": {
|
||||||
"id": 10,
|
"id": 10,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"title": "Deleted Template",
|
"title": "Deleted Template",
|
||||||
"status": "DRAFT",
|
"status": "DRAFT",
|
||||||
"templateId": null,
|
"templateId": null,
|
||||||
@@ -830,7 +830,7 @@ Triggered when a document is created from a template. This event fires alongside
|
|||||||
"event": "TEMPLATE_USED",
|
"event": "TEMPLATE_USED",
|
||||||
"payload": {
|
"payload": {
|
||||||
"id": 10,
|
"id": 10,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"title": "Document from Template",
|
"title": "Document from Template",
|
||||||
"status": "DRAFT",
|
"status": "DRAFT",
|
||||||
"templateId": 10,
|
"templateId": 10,
|
||||||
|
|||||||
@@ -42,14 +42,14 @@ Documenso supports webhook events for the full document lifecycle (created, sent
|
|||||||
"event": "DOCUMENT_COMPLETED",
|
"event": "DOCUMENT_COMPLETED",
|
||||||
"payload": {
|
"payload": {
|
||||||
"id": 123,
|
"id": 123,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"title": "Contract",
|
"title": "Contract",
|
||||||
"status": "COMPLETED",
|
"status": "COMPLETED",
|
||||||
"completedAt": "2024-01-15T10:30:00.000Z",
|
"completedAt": "2024-01-15T10:30:00.000Z",
|
||||||
"recipients": [
|
"recipients": [
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"envelopeId": "envelope_abc123def456xy78",
|
"envelopeId": "envelope_abcdefhiklmnorst",
|
||||||
"email": "signer@example.com",
|
"email": "signer@example.com",
|
||||||
"signingStatus": "SIGNED"
|
"signingStatus": "SIGNED"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -209,9 +209,12 @@ Your webhook endpoint must meet these requirements:
|
|||||||
| **Availability** | Must be publicly accessible from the internet |
|
| **Availability** | Must be publicly accessible from the internet |
|
||||||
|
|
||||||
<Callout type="warn">
|
<Callout type="warn">
|
||||||
Documenso blocks webhook URLs that use or resolve to private or loopback addresses. Self-hosters
|
Documenso performs a best-effort check that rejects webhook URLs which use or resolve to private
|
||||||
that need to deliver to a hostname resolving to a private address can add that hostname to the
|
or loopback addresses. This is not a complete SSRF mitigation — it does not cover DNS rebinding
|
||||||
comma-separated `NEXT_PRIVATE_WEBHOOK_SSRF_BYPASS_HOSTS` environment variable.
|
and fails open on DNS lookup errors or timeouts — so self-hosted deployments should still enforce
|
||||||
|
network-level egress rules. Self-hosters that need to deliver to a hostname resolving to a
|
||||||
|
private address can add that hostname to the comma-separated
|
||||||
|
`NEXT_PRIVATE_WEBHOOK_SSRF_BYPASS_HOSTS` environment variable.
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
||||||
<Callout type="info">
|
<Callout type="info">
|
||||||
|
|||||||
Reference in New Issue
Block a user