@dereekb/zoho 13.3.1 → 13.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,202 @@
1
+ # Zoho Sign Webhooks
2
+
3
+ This guide covers configuring Zoho Sign webhooks in the Zoho Sign dashboard and wiring up the `ZohoSignWebhookModule` in your NestJS application.
4
+
5
+ ---
6
+
7
+ ## 1. Create a Webhook in Zoho Sign
8
+
9
+ Navigate to **Settings > Developer Settings > Webhooks** in Zoho Sign, or go directly to `https://sign.zoho.com/zs/<YOUR_ORG_ID>#/devspace/webhooks/new`.
10
+
11
+ ### Callback URL
12
+
13
+ Enter the public URL of your webhook endpoint. The NestJS controller registers at `/webhook/zoho/sign`, so the full URL will be something like:
14
+
15
+ ```
16
+ https://your-api-domain.com/api/webhook/zoho/sign
17
+ ```
18
+
19
+ Use the **"Test Url"** button to verify Zoho Sign can reach your endpoint.
20
+
21
+ ### Name
22
+
23
+ Give the webhook a descriptive name (e.g. "Production API Webhook").
24
+
25
+ ### Security Settings (HMAC)
26
+
27
+ Check **"Enable HMAC signature"** and either enter your own secret key or click **"Generate"** to create one.
28
+
29
+ > **Important:** Once saved, the secret key cannot be retrieved from Zoho Sign. Copy it immediately and store it in your environment variables as `ZOHO_SIGN_WEBHOOK_SECRET_TOKEN`.
30
+
31
+ When HMAC is enabled, Zoho Sign includes an `X-ZS-WEBHOOK-SIGNATURE` header on every webhook request. The signature is computed as `base64(HMAC-SHA256(payload, secret_key))`. The `ZohoSignWebhookModule` verifies this automatically.
32
+
33
+ ### Callback Trigger Criteria (Optional)
34
+
35
+ You can optionally filter which documents trigger callbacks. For example, you can set criteria like "Document name Contains NDA" so that only matching documents fire the webhook. If no criteria is set, callbacks are triggered for all events selected below.
36
+
37
+ ### Callback Events
38
+
39
+ Select which events should trigger the webhook. Events are split into two groups:
40
+
41
+ **Document events:**
42
+
43
+ | Event | Description |
44
+ |---|---|
45
+ | Sent | Documents are sent for signatures or approval |
46
+ | Completed by all | All recipients have signed and approved |
47
+ | Expires | Documents sent for signatures or approval expire |
48
+ | Recalled | Sender recalls the document |
49
+
50
+ **Recipient events:**
51
+
52
+ | Event | Description |
53
+ |---|---|
54
+ | Viewed | A recipient views the document |
55
+ | Signed by a recipient | A recipient signs the document |
56
+ | Approved by a recipient | A recipient approves the document |
57
+ | Declined | A recipient declines the document |
58
+ | Reassigned | A recipient reassigns to another person |
59
+
60
+ Click **"Save"** to create the webhook.
61
+
62
+ ---
63
+
64
+ ## 2. Webhook Payload
65
+
66
+ Every webhook POST contains a JSON body with two top-level keys:
67
+
68
+ ```json
69
+ {
70
+ "notifications": {
71
+ "performed_by_email": "signer@example.com",
72
+ "performed_by_name": "Jane Doe",
73
+ "performed_at": 1555062604837,
74
+ "reason": "",
75
+ "activity": "Document has been signed",
76
+ "operation_type": "RequestSigningSuccess",
77
+ "action_id": "1000000000090",
78
+ "ip_address": "192.168.100.100"
79
+ },
80
+ "requests": {
81
+ "request_name": "NDA Document",
82
+ "request_id": "1000000000000",
83
+ "request_status": "inprogress",
84
+ "org_id": "9876543210",
85
+ "request_type_id": "10000000011",
86
+ "document_ids": [
87
+ {
88
+ "document_name": "CommonNDA.pdf",
89
+ "document_id": "100000000000050"
90
+ }
91
+ ]
92
+ }
93
+ }
94
+ ```
95
+
96
+ ### Operation Types
97
+
98
+ The `operation_type` field identifies the event:
99
+
100
+ | Operation Type | Trigger |
101
+ |---|---|
102
+ | `RequestSubmitted` | Document submitted for signature |
103
+ | `RequestViewed` | Document viewed by a recipient |
104
+ | `RequestSigningSuccess` | A signer completes signing |
105
+ | `RequestCompleted` | All signers/approvers complete |
106
+ | `RequestRejected` | A recipient declines |
107
+ | `RequestRecalled` | Sender recalls the document |
108
+ | `RequestForwarded` | A recipient forwards to another person |
109
+ | `RequestExpired` | Document expires |
110
+
111
+ > For signer-related actions (`RequestViewed`, `RequestSigningSuccess`, `RequestRejected`, `RequestForwarded`), the `action_id` of the signer is present in the `notifications` object.
112
+
113
+ ---
114
+
115
+ ## 3. Best Practices
116
+
117
+ - **Return HTTP 200 within 5 seconds.** Do not perform heavy processing before responding.
118
+ - After **10-15 consecutive failures**, Zoho Sign sends a warning email to the admin.
119
+ - After **20 consecutive failures**, the webhook is **automatically disabled**. The admin must re-enable it from the Zoho Sign web interface.
120
+
121
+ ---
122
+
123
+ ## 4. NestJS Module Setup
124
+
125
+ ### Environment Variable
126
+
127
+ ```
128
+ ZOHO_SIGN_WEBHOOK_SECRET_TOKEN=<your-hmac-secret-key>
129
+ ```
130
+
131
+ ### Import the Module
132
+
133
+ `ZohoSignWebhookModule` is a standalone module that reads the webhook secret from the environment. It does not depend on the Zoho Sign API module (`appZohoSignModuleMetadata`), since webhook verification only needs the HMAC secret.
134
+
135
+ ```typescript
136
+ import { Module } from '@nestjs/common';
137
+ import { ZohoSignWebhookModule } from '@dereekb/zoho/nestjs';
138
+
139
+ @Module({
140
+ imports: [ZohoSignWebhookModule]
141
+ })
142
+ export class AppZohoSignWebhookModule {}
143
+ ```
144
+
145
+ Import `AppZohoSignWebhookModule` in your API module alongside your other webhook modules.
146
+
147
+ ### Handling Events
148
+
149
+ Inject `ZohoSignWebhookService` and use the `configure` accessor to register typed event handlers:
150
+
151
+ ```typescript
152
+ import { Injectable, OnModuleInit } from '@nestjs/common';
153
+ import { ZohoSignWebhookService } from '@dereekb/zoho/nestjs';
154
+ import { catchAllHandlerKey } from '@dereekb/util';
155
+
156
+ @Injectable()
157
+ export class MyZohoSignHandler implements OnModuleInit {
158
+ constructor(private readonly zohoSignWebhookService: ZohoSignWebhookService) {}
159
+
160
+ onModuleInit() {
161
+ this.zohoSignWebhookService.configure(this, (x) => {
162
+ x.handleRequestCompleted(this.handleCompleted);
163
+ x.handleRequestSigningSuccess(this.handleSigned);
164
+ x.handleRequestRejected(this.handleRejected);
165
+ x.set(catchAllHandlerKey(), this.handleAny);
166
+ });
167
+ }
168
+
169
+ async handleCompleted(event) {
170
+ const { request_id, request_name } = event.requests;
171
+ // All recipients have signed — process the completed document
172
+ }
173
+
174
+ async handleSigned(event) {
175
+ const { action_id, performed_by_name } = event.notifications;
176
+ // A single recipient signed — update progress
177
+ }
178
+
179
+ async handleRejected(event) {
180
+ const { reason, performed_by_name } = event.notifications;
181
+ // A recipient declined — notify the sender
182
+ }
183
+
184
+ async handleAny(event) {
185
+ // Catch-all for any unhandled event types
186
+ }
187
+ }
188
+ ```
189
+
190
+ Register your handler service in the same module that imports `ZohoSignWebhookModule`:
191
+
192
+ ```typescript
193
+ @Module({
194
+ imports: [ZohoSignWebhookModule],
195
+ providers: [MyZohoSignHandler]
196
+ })
197
+ export class AppZohoSignWebhookModule {}
198
+ ```
199
+
200
+ ### Webhook Middleware
201
+
202
+ The webhook controller is mounted at `/webhook/zoho/sign`. Your application must have webhook middleware enabled so the raw request body is preserved for HMAC verification. If using `@dereekb/firebase-server`, this is handled by setting `configureWebhooks: true` in your `nestServerInstance()` call. For plain NestJS apps, use `AppModuleWithWebhooksEnabled` or `consumeWebhooksWithRawBodyMiddleware()` from `@dereekb/nestjs`.