whatsapp-cloud 0.0.5 → 0.0.6

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # whatzapp
2
2
 
3
+ ## 0.0.6
4
+
5
+ ### Patch Changes
6
+
7
+ - a46c084: export types
8
+ - 175d774: add webhooks namespace
9
+
3
10
  ## 0.0.5
4
11
 
5
12
  ### Patch Changes
@@ -0,0 +1,154 @@
1
+ # Development Guide
2
+
3
+ ## Local Development with npm/pnpm link
4
+
5
+ To test the SDK in your own project without publishing to npm:
6
+
7
+ ### Step 1: Link the package (in whatsapp-cloud directory)
8
+
9
+ ```bash
10
+ cd /Users/lukas/Developer/whatsapp-cloud
11
+ pnpm build # Build the package first
12
+ pnpm link --global # Creates a global symlink
13
+ ```
14
+
15
+ Or with npm:
16
+
17
+ ```bash
18
+ npm link
19
+ ```
20
+
21
+ ### Step 2: Link in your project
22
+
23
+ ```bash
24
+ cd /path/to/your/project
25
+ pnpm link whatsapp-cloud
26
+ ```
27
+
28
+ Or with npm:
29
+
30
+ ```bash
31
+ npm link whatsapp-cloud
32
+ ```
33
+
34
+ **What this does:** Creates a symlink in your project's `node_modules` pointing to your local package. You don't need to:
35
+
36
+ - Add it to `package.json` (link handles it)
37
+ - Run `pnpm install` (link is enough)
38
+ - Publish to npm
39
+
40
+ ### Step 3: Use it in your project
41
+
42
+ ```typescript
43
+ import { WhatsAppClient, type IncomingTextMessage } from "whatsapp-cloud";
44
+
45
+ const client = new WhatsAppClient({
46
+ accessToken: "...",
47
+ });
48
+ ```
49
+
50
+ ### Important Notes
51
+
52
+ - **Rebuild after changes**: After making changes to whatsapp-cloud, run `pnpm build` in the whatsapp-cloud directory
53
+ - **Hot reload**: Some bundlers (like Next.js) may need a restart to pick up changes
54
+ - **Unlink**: When done, unlink with `pnpm unlink whatsapp-cloud` in your project
55
+
56
+ ## Production / CI/CD
57
+
58
+ For production builds and CI/CD, you have a few options:
59
+
60
+ ### Option 1: Publish to npm (Recommended)
61
+
62
+ Once ready, publish the package:
63
+
64
+ ```bash
65
+ cd /Users/lukas/Developer/whatsapp-cloud
66
+ pnpm publish
67
+ ```
68
+
69
+ Then in your project's `package.json`:
70
+
71
+ ```json
72
+ {
73
+ "dependencies": {
74
+ "whatsapp-cloud": "^0.0.5"
75
+ }
76
+ }
77
+ ```
78
+
79
+ ### Option 2: Git dependency (For private repos)
80
+
81
+ If your repo is private, use git dependency:
82
+
83
+ ```json
84
+ {
85
+ "dependencies": {
86
+ "whatsapp-cloud": "git+https://github.com/your-username/whatsapp-cloud.git"
87
+ }
88
+ }
89
+ ```
90
+
91
+ ### Option 3: Local file path (For monorepos)
92
+
93
+ If both projects are in the same repo/monorepo:
94
+
95
+ ```json
96
+ {
97
+ "dependencies": {
98
+ "whatsapp-cloud": "file:../whatsapp-cloud"
99
+ }
100
+ }
101
+ ```
102
+
103
+ ### Option 4: Conditional linking (Dev vs Prod)
104
+
105
+ Use environment detection:
106
+
107
+ ```json
108
+ {
109
+ "dependencies": {
110
+ "whatsapp-cloud": process.env.NODE_ENV === "development"
111
+ ? "link:../whatsapp-cloud"
112
+ : "^0.0.5"
113
+ }
114
+ }
115
+ ```
116
+
117
+ **Note:** For CI/CD, you'll need Option 1 (npm publish) or Option 2 (git dependency). `pnpm link` only works locally.
118
+
119
+ ## Type Exports
120
+
121
+ All types are properly exported and can be imported in React/Next.js projects:
122
+
123
+ ```typescript
124
+ // Import client
125
+ import { WhatsAppClient } from "whatsapp-cloud";
126
+
127
+ // Import types
128
+ import type {
129
+ IncomingTextMessage,
130
+ IncomingMessage,
131
+ WebhookPayload,
132
+ MessageContext,
133
+ CreateTemplateRequest,
134
+ // ... all other types
135
+ } from "whatsapp-cloud";
136
+
137
+ // Import schemas (for validation)
138
+ import {
139
+ incomingTextMessageSchema,
140
+ webhookPayloadSchema,
141
+ // ... all other schemas
142
+ } from "whatsapp-cloud";
143
+ ```
144
+
145
+ ## Verifying Exports
146
+
147
+ To verify all types are exported correctly:
148
+
149
+ ```bash
150
+ # In your project
151
+ pnpm exec tsc --noEmit --skipLibCheck
152
+ ```
153
+
154
+ This will check that all imported types are available.
@@ -0,0 +1,76 @@
1
+ # Webhooks
2
+
3
+ Handle incoming WhatsApp messages and status updates via webhooks.
4
+
5
+ ## Quick Start
6
+
7
+ ```typescript
8
+ import { WhatsAppClient } from "@whatsapp-cloud/sdk";
9
+
10
+ const client = new WhatsAppClient({
11
+ accessToken: process.env.WHATSAPP_ACCESS_TOKEN!,
12
+ });
13
+
14
+ // In your webhook endpoint
15
+ app.post("/webhook", async (req, res) => {
16
+ // handle() returns IMMEDIATELY - handlers run in background
17
+ client.webhooks.handle(req.body, {
18
+ text: async (message, context) => {
19
+ // This can take as long as needed (20s, 1min, etc.)
20
+ // Webhook already returned 200, so Meta is happy
21
+
22
+ // Process text message
23
+ console.log(`Received: ${message.text.body} from ${message.from}`);
24
+
25
+ // Store in database
26
+ await db.messages.create({
27
+ id: message.id,
28
+ from: message.from,
29
+ body: message.text.body,
30
+ phoneNumberId: context.metadata.phoneNumberId,
31
+ });
32
+
33
+ // Send response
34
+ await client.messages.sendText({
35
+ to: `+${message.from}`,
36
+ text: { body: "Got it!" },
37
+ });
38
+ },
39
+ });
40
+
41
+ // Returns 200 IMMEDIATELY (handlers continue in background)
42
+ res.json({ success: true });
43
+ });
44
+ ```
45
+
46
+ ## Webhook Verification
47
+
48
+ Meta sends GET requests to verify your webhook endpoint:
49
+
50
+ ```typescript
51
+ app.get("/webhook", (req, res) => {
52
+ const challenge = client.webhooks.verify(req.query, process.env.VERIFY_TOKEN);
53
+ if (challenge) {
54
+ return res.send(challenge);
55
+ }
56
+ return res.status(403).send("Forbidden");
57
+ });
58
+ ```
59
+
60
+ ## Low-Level API
61
+
62
+ For more control, extract messages manually:
63
+
64
+ ```typescript
65
+ const messages = client.webhooks.extractMessages(payload);
66
+ for (const message of messages) {
67
+ // Custom processing
68
+ }
69
+ ```
70
+
71
+ ## API Reference
72
+
73
+ - `client.webhooks.verify(query, token)` - Verify GET request, returns challenge or null
74
+ - `client.webhooks.extractMessages(payload)` - Extract messages from payload
75
+ - `client.webhooks.extractStatuses(payload)` - Extract status updates
76
+ - `client.webhooks.handle(payload, handlers, options?)` - Handle with type-safe callbacks
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whatsapp-cloud",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "description": "Work in progress. A WhatsApp client tailored for LLMs—built to actually work.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -5,6 +5,7 @@ import { MessagesService } from "../services/messages/index";
5
5
  import { AccountsService } from "../services/accounts/index";
6
6
  import { BusinessService } from "../services/business/index";
7
7
  import { TemplatesService } from "../services/templates/index";
8
+ import { WebhooksService } from "../services/webhooks/index";
8
9
  import { ZodError } from "zod";
9
10
  import { transformZodError } from "../utils/zod-error";
10
11
  import type { DebugTokenResponse } from "../types/debug";
@@ -17,6 +18,7 @@ export class WhatsAppClient {
17
18
  public readonly accounts: AccountsService;
18
19
  public readonly business: BusinessService;
19
20
  public readonly templates: TemplatesService;
21
+ public readonly webhooks: WebhooksService;
20
22
 
21
23
  private readonly httpClient: HttpClient;
22
24
 
@@ -40,6 +42,7 @@ export class WhatsAppClient {
40
42
  this.accounts = new AccountsService(this.httpClient);
41
43
  this.business = new BusinessService(this.httpClient);
42
44
  this.templates = new TemplatesService(this.httpClient);
45
+ this.webhooks = new WebhooksService(this.httpClient);
43
46
  }
44
47
 
45
48
  /**
package/src/index.ts CHANGED
@@ -7,6 +7,13 @@ export * from "./schemas/index";
7
7
  // Export types (primary export point)
8
8
  export type * from "./types/index";
9
9
 
10
+ // Export webhook handler types (convenience exports)
11
+ export type {
12
+ MessageContext,
13
+ MessageHandlers,
14
+ HandleOptions,
15
+ } from "./services/webhooks/index";
16
+
10
17
  // Export errors for error handling
11
18
  export {
12
19
  WhatsAppError,
@@ -3,4 +3,5 @@ export * from "./messages/index";
3
3
  export * from "./accounts/index";
4
4
  export * from "./business/index";
5
5
  export * from "./templates/index";
6
+ export * from "./webhooks/index";
6
7
  export * from "./debug";
@@ -0,0 +1,38 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * Base fields present in ALL incoming messages
5
+ */
6
+ const baseIncomingMessageSchema = z.object({
7
+ from: z.string(), // WhatsApp ID (phone number without +)
8
+ id: z.string(), // Message ID (wamid.*)
9
+ timestamp: z.string(), // Unix timestamp as string
10
+ type: z.string(), // Message type discriminator
11
+ });
12
+
13
+ /**
14
+ * Text content in incoming text messages
15
+ * Note: Incoming messages don't have preview_url like outgoing
16
+ */
17
+ const incomingTextContentSchema = z.object({
18
+ body: z.string(),
19
+ });
20
+
21
+ /**
22
+ * Incoming text message schema
23
+ * Uses discriminated union pattern (type: "text")
24
+ */
25
+ export const incomingTextMessageSchema = baseIncomingMessageSchema.extend({
26
+ type: z.literal("text"),
27
+ text: incomingTextContentSchema,
28
+ });
29
+
30
+ /**
31
+ * Union of all incoming message types
32
+ * For now: just text. Others (image, audio, etc.) will be added later
33
+ */
34
+ export const incomingMessageSchema = z.discriminatedUnion("type", [
35
+ incomingTextMessageSchema,
36
+ // Future: incomingImageMessageSchema, incomingAudioMessageSchema, etc.
37
+ ]);
38
+
@@ -0,0 +1,3 @@
1
+ export * from "./incoming-message";
2
+ export * from "./payload";
3
+
@@ -0,0 +1,56 @@
1
+ import { z } from "zod";
2
+ import { incomingMessageSchema } from "./incoming-message";
3
+
4
+ /**
5
+ * Contact information in webhook
6
+ */
7
+ const contactSchema = z.object({
8
+ profile: z.object({
9
+ name: z.string(),
10
+ }),
11
+ wa_id: z.string(),
12
+ });
13
+
14
+ /**
15
+ * Metadata in webhook value
16
+ */
17
+ const webhookMetadataSchema = z.object({
18
+ display_phone_number: z.string(),
19
+ phone_number_id: z.string(),
20
+ });
21
+
22
+ /**
23
+ * Webhook value (the actual data)
24
+ */
25
+ const webhookValueSchema = z.object({
26
+ messaging_product: z.literal("whatsapp"),
27
+ metadata: webhookMetadataSchema,
28
+ contacts: z.array(contactSchema).optional(),
29
+ messages: z.array(incomingMessageSchema).optional(), // Incoming messages
30
+ statuses: z.array(z.any()).optional(), // Status updates (for later)
31
+ });
32
+
33
+ /**
34
+ * Webhook change entry
35
+ */
36
+ const webhookChangeSchema = z.object({
37
+ value: webhookValueSchema,
38
+ field: z.literal("messages"), // For now: only messages field
39
+ });
40
+
41
+ /**
42
+ * Webhook entry
43
+ */
44
+ const webhookEntrySchema = z.object({
45
+ id: z.string(), // WABA ID
46
+ changes: z.array(webhookChangeSchema),
47
+ });
48
+
49
+ /**
50
+ * Full webhook payload schema
51
+ */
52
+ export const webhookPayloadSchema = z.object({
53
+ object: z.literal("whatsapp_business_account"),
54
+ entry: z.array(webhookEntrySchema),
55
+ });
56
+
@@ -0,0 +1,214 @@
1
+ import type { HttpClient } from "../../client/HttpClient";
2
+ import { extractMessages } from "./utils/extract-messages";
3
+ import { extractStatuses } from "./utils/extract-statuses";
4
+ import { verifyWebhook } from "./utils/verify";
5
+ import { webhookPayloadSchema } from "../../schemas/webhooks/payload";
6
+ import type {
7
+ WebhookPayload,
8
+ IncomingTextMessage,
9
+ IncomingMessage,
10
+ } from "../../types/webhooks";
11
+
12
+ /**
13
+ * Context provided to message handlers
14
+ * Contains metadata and contact info (message is passed separately)
15
+ */
16
+ export type MessageContext = {
17
+ metadata: {
18
+ phoneNumberId: string;
19
+ displayPhoneNumber: string;
20
+ wabaId: string;
21
+ };
22
+ contact?: {
23
+ name: string;
24
+ waId: string;
25
+ };
26
+ };
27
+
28
+ /**
29
+ * Handler functions for different message types
30
+ * Receives message and context separately - message is the focus, context is optional metadata
31
+ */
32
+ export type MessageHandlers = {
33
+ text?: (
34
+ message: IncomingTextMessage,
35
+ context: MessageContext
36
+ ) => Promise<void> | void;
37
+ // Future: image, audio, video, etc.
38
+ };
39
+
40
+ /**
41
+ * Options for handle() method
42
+ */
43
+ export type HandleOptions = {
44
+ /**
45
+ * Error handler called when a message handler throws an error
46
+ * If not provided, errors are logged and processing continues
47
+ */
48
+ onError?: (error: Error, message: IncomingMessage) => void;
49
+ };
50
+
51
+ /**
52
+ * Webhooks service for handling incoming webhook payloads
53
+ *
54
+ * Provides utilities for extracting messages and a convenience handler
55
+ * for type-safe message processing.
56
+ */
57
+ export class WebhooksService {
58
+ constructor(private readonly httpClient: HttpClient) {}
59
+
60
+ /**
61
+ * Verify webhook GET request from Meta
62
+ *
63
+ * Meta sends GET requests to verify webhook endpoints during setup.
64
+ * Returns the challenge string if valid, null if invalid.
65
+ *
66
+ * @param query - Query parameters from GET request
67
+ * @param verifyToken - Your verification token (stored on your server)
68
+ * @returns Challenge string if valid, null if invalid
69
+ */
70
+ verify(
71
+ query: {
72
+ "hub.mode"?: string;
73
+ "hub.verify_token"?: string;
74
+ "hub.challenge"?: string;
75
+ },
76
+ verifyToken: string
77
+ ): string | null {
78
+ return verifyWebhook(query, verifyToken);
79
+ }
80
+
81
+ /**
82
+ * Extract all incoming messages from webhook payload
83
+ *
84
+ * Low-level utility that flattens the nested webhook structure
85
+ * and returns messages directly.
86
+ *
87
+ * @param payload - Webhook payload from Meta
88
+ * @returns Flat array of incoming messages
89
+ */
90
+ extractMessages(payload: WebhookPayload): IncomingMessage[] {
91
+ return extractMessages(payload);
92
+ }
93
+
94
+ /**
95
+ * Extract status updates from webhook payload
96
+ *
97
+ * Low-level utility for extracting status updates for outgoing messages.
98
+ *
99
+ * @param payload - Webhook payload from Meta
100
+ * @returns Flat array of status updates
101
+ */
102
+ extractStatuses(payload: WebhookPayload): unknown[] {
103
+ return extractStatuses(payload);
104
+ }
105
+
106
+ /**
107
+ * Validate webhook payload structure
108
+ *
109
+ * Validates the payload against the schema. Logs errors if malformed
110
+ * but doesn't throw, allowing processing to continue.
111
+ *
112
+ * @param payload - Raw payload to validate
113
+ * @returns Validated payload if valid, original payload if invalid (with logged error)
114
+ */
115
+ private validatePayload(payload: unknown): WebhookPayload {
116
+ const result = webhookPayloadSchema.safeParse(payload);
117
+ if (!result.success) {
118
+ console.error(
119
+ "Webhook payload validation failed:",
120
+ result.error.format()
121
+ );
122
+ // Return as-is (TypeScript will treat it as WebhookPayload, but it's actually invalid)
123
+ // This allows processing to continue, but handlers should be defensive
124
+ return payload as WebhookPayload;
125
+ }
126
+ return result.data;
127
+ }
128
+
129
+ /**
130
+ * Handle webhook payload with type-safe callbacks
131
+ *
132
+ * High-level convenience method that extracts messages and dispatches
133
+ * them to appropriate handlers based on message type.
134
+ *
135
+ * **Important**: This method returns quickly to allow fast webhook responses.
136
+ * Handlers are processed asynchronously. If you need to await handler completion,
137
+ * use the low-level `extractMessages()` method instead.
138
+ *
139
+ * @param payload - Webhook payload from Meta (will be validated)
140
+ * @param handlers - Object with handler functions for each message type
141
+ * @param options - Optional error handling configuration
142
+ */
143
+ handle(
144
+ payload: unknown,
145
+ handlers: MessageHandlers,
146
+ options?: HandleOptions
147
+ ): void {
148
+ // Validate payload (logs error if malformed, but continues)
149
+ const validatedPayload = this.validatePayload(payload);
150
+
151
+ // Extract metadata and contacts from payload for context
152
+ for (const entry of validatedPayload.entry) {
153
+ for (const change of entry.changes) {
154
+ if (change.field === "messages" && change.value.messages) {
155
+ const metadata = {
156
+ phoneNumberId: change.value.metadata.phone_number_id,
157
+ displayPhoneNumber: change.value.metadata.display_phone_number,
158
+ wabaId: entry.id,
159
+ };
160
+
161
+ const contacts = change.value.contacts || [];
162
+
163
+ // Process each message with its context
164
+ for (const message of change.value.messages) {
165
+ // Find contact for this message (match by wa_id)
166
+ const contact = contacts.find((c) => c.wa_id === message.from);
167
+
168
+ // Build context (metadata + contact, no message duplication)
169
+ const context: MessageContext = {
170
+ metadata,
171
+ ...(contact && {
172
+ contact: {
173
+ name: contact.profile.name,
174
+ waId: contact.wa_id,
175
+ },
176
+ }),
177
+ };
178
+
179
+ // Process handler asynchronously (don't await)
180
+ // This allows long-running handlers without blocking webhook response
181
+ Promise.resolve()
182
+ .then(async () => {
183
+ // Type-safe dispatch based on message type
184
+ switch (message.type) {
185
+ case "text":
186
+ if (handlers.text) {
187
+ await handlers.text(message, context);
188
+ }
189
+ break;
190
+
191
+ // Future: image, audio, video, etc.
192
+ default:
193
+ // Unhandled message type - silently continue
194
+ break;
195
+ }
196
+ })
197
+ .catch((error) => {
198
+ // Handle errors in handler execution
199
+ if (options?.onError) {
200
+ options.onError(error as Error, message);
201
+ } else {
202
+ // Default: log and continue (don't break webhook response)
203
+ console.error(
204
+ `Error handling ${message.type} message ${message.id}:`,
205
+ error
206
+ );
207
+ }
208
+ });
209
+ }
210
+ }
211
+ }
212
+ }
213
+ }
214
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./WebhooksService";
2
+ export type { MessageContext, MessageHandlers, HandleOptions } from "./WebhooksService";
3
+
@@ -0,0 +1,25 @@
1
+ import type { WebhookPayload } from "../../../types/webhooks";
2
+ import type { IncomingMessage } from "../../../types/webhooks/incoming-message";
3
+
4
+ /**
5
+ * Extract all incoming messages from webhook payload
6
+ *
7
+ * Flattens the nested structure: entry[].changes[].value.messages[]
8
+ * Returns a flat array of messages directly
9
+ *
10
+ * @param payload - Webhook payload from Meta
11
+ * @returns Flat array of incoming messages
12
+ */
13
+ export function extractMessages(payload: WebhookPayload): IncomingMessage[] {
14
+ const messages: IncomingMessage[] = [];
15
+
16
+ for (const entry of payload.entry) {
17
+ for (const change of entry.changes) {
18
+ if (change.field === "messages" && change.value.messages) {
19
+ messages.push(...change.value.messages);
20
+ }
21
+ }
22
+ }
23
+
24
+ return messages;
25
+ }
@@ -0,0 +1,25 @@
1
+ import type { WebhookPayload } from "../../../types/webhooks";
2
+
3
+ /**
4
+ * Extract status updates from webhook payload
5
+ *
6
+ * Flattens the nested structure: entry[].changes[].value.statuses[]
7
+ * Returns a flat array of status updates
8
+ *
9
+ * @param payload - Webhook payload from Meta
10
+ * @returns Flat array of status updates
11
+ */
12
+ export function extractStatuses(payload: WebhookPayload): unknown[] {
13
+ const statuses: unknown[] = [];
14
+
15
+ for (const entry of payload.entry) {
16
+ for (const change of entry.changes) {
17
+ if (change.field === "messages" && change.value.statuses) {
18
+ statuses.push(...change.value.statuses);
19
+ }
20
+ }
21
+ }
22
+
23
+ return statuses;
24
+ }
25
+
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Verify webhook GET request from Meta
3
+ *
4
+ * Meta sends GET requests to verify webhook endpoints:
5
+ * GET /webhook?hub.mode=subscribe&hub.challenge=<CHALLENGE>&hub.verify_token=<TOKEN>
6
+ *
7
+ * @param query - Query parameters from GET request
8
+ * @param verifyToken - Your verification token (stored on your server)
9
+ * @returns Challenge string if valid, null if invalid
10
+ */
11
+ export function verifyWebhook(
12
+ query: {
13
+ "hub.mode"?: string;
14
+ "hub.verify_token"?: string;
15
+ "hub.challenge"?: string;
16
+ },
17
+ verifyToken: string
18
+ ): string | null {
19
+ const mode = query["hub.mode"];
20
+ const token = query["hub.verify_token"];
21
+ const challenge = query["hub.challenge"];
22
+
23
+ // Verify mode is "subscribe" and token matches
24
+ if (mode === "subscribe" && token === verifyToken && challenge) {
25
+ return challenge;
26
+ }
27
+
28
+ return null;
29
+ }
@@ -3,4 +3,5 @@ export type * from "./messages/index";
3
3
  export type * from "./accounts/index";
4
4
  export type * from "./business/index";
5
5
  export type * from "./templates/index";
6
+ export type * from "./webhooks/index";
6
7
  export type * from "./debug";
@@ -0,0 +1,16 @@
1
+ import { z } from "zod";
2
+ import {
3
+ incomingTextMessageSchema,
4
+ incomingMessageSchema,
5
+ } from "../../schemas/webhooks/incoming-message";
6
+
7
+ /**
8
+ * Type for incoming text message
9
+ */
10
+ export type IncomingTextMessage = z.infer<typeof incomingTextMessageSchema>;
11
+
12
+ /**
13
+ * Union type for all incoming message types
14
+ */
15
+ export type IncomingMessage = z.infer<typeof incomingMessageSchema>;
16
+
@@ -0,0 +1,3 @@
1
+ export * from "./incoming-message";
2
+ export * from "./payload";
3
+
@@ -0,0 +1,8 @@
1
+ import { z } from "zod";
2
+ import { webhookPayloadSchema } from "../../schemas/webhooks/payload";
3
+
4
+ /**
5
+ * Type for webhook payload
6
+ */
7
+ export type WebhookPayload = z.infer<typeof webhookPayloadSchema>;
8
+