n8n-nodes-posthawk 0.1.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.
- package/README.md +45 -0
- package/dist/credentials/PosthawkApi.credentials.d.ts +9 -0
- package/dist/credentials/PosthawkApi.credentials.js +44 -0
- package/dist/nodes/Posthawk/Posthawk.node.d.ts +5 -0
- package/dist/nodes/Posthawk/Posthawk.node.js +664 -0
- package/dist/nodes/Posthawk/PosthawkTrigger.node.d.ts +12 -0
- package/dist/nodes/Posthawk/PosthawkTrigger.node.js +129 -0
- package/dist/nodes/Posthawk/posthawk.svg +4 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# n8n-nodes-posthawk
|
|
2
|
+
|
|
3
|
+
This is an [n8n](https://n8n.io/) community node for [Posthawk](https://posthawk.dev) — email infrastructure for developers.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
### Posthawk Node (Actions)
|
|
8
|
+
|
|
9
|
+
| Resource | Operations |
|
|
10
|
+
|----------|-----------|
|
|
11
|
+
| **Email** | Send, Send Batch, Get Status |
|
|
12
|
+
| **Scheduled Email** | List, Get, Cancel, Reschedule |
|
|
13
|
+
| **Contact** | List, Get, Create, Update, Delete |
|
|
14
|
+
| **Suppression** | List, Add, Remove |
|
|
15
|
+
| **Webhook** | List, Create, Update, Delete, Test |
|
|
16
|
+
|
|
17
|
+
### Posthawk Trigger (Webhook)
|
|
18
|
+
|
|
19
|
+
Automatically triggers your n8n workflow when email events occur:
|
|
20
|
+
|
|
21
|
+
- **Send** — Email accepted by SES
|
|
22
|
+
- **Delivery** — Email delivered to recipient
|
|
23
|
+
- **Bounce** — Email bounced
|
|
24
|
+
- **Complaint** — Recipient marked as spam
|
|
25
|
+
- **Open** — Email opened
|
|
26
|
+
- **Click** — Link clicked
|
|
27
|
+
- **Reject** — Email rejected by SES
|
|
28
|
+
- **Delivery Delay** — Delivery delayed
|
|
29
|
+
|
|
30
|
+
The trigger node automatically creates and manages webhook endpoints in your Posthawk account.
|
|
31
|
+
|
|
32
|
+
## Setup
|
|
33
|
+
|
|
34
|
+
1. Install this node in your n8n instance
|
|
35
|
+
2. Add your Posthawk API key in the credentials
|
|
36
|
+
3. If self-hosting Posthawk, update the Base URL in credentials
|
|
37
|
+
|
|
38
|
+
## Credentials
|
|
39
|
+
|
|
40
|
+
- **API Key**: Your Posthawk API key (find it in Dashboard → API Keys)
|
|
41
|
+
- **Base URL**: `https://api.posthawk.dev` (or your self-hosted URL)
|
|
42
|
+
|
|
43
|
+
## License
|
|
44
|
+
|
|
45
|
+
MIT
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
|
|
2
|
+
export declare class PosthawkApi implements ICredentialType {
|
|
3
|
+
name: string;
|
|
4
|
+
displayName: string;
|
|
5
|
+
documentationUrl: string;
|
|
6
|
+
properties: INodeProperties[];
|
|
7
|
+
authenticate: IAuthenticateGeneric;
|
|
8
|
+
test: ICredentialTestRequest;
|
|
9
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PosthawkApi = void 0;
|
|
4
|
+
class PosthawkApi {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.name = 'posthawkApi';
|
|
7
|
+
this.displayName = 'Posthawk API';
|
|
8
|
+
this.documentationUrl = 'https://docs.posthawk.dev/api';
|
|
9
|
+
this.properties = [
|
|
10
|
+
{
|
|
11
|
+
displayName: 'API Key',
|
|
12
|
+
name: 'apiKey',
|
|
13
|
+
type: 'string',
|
|
14
|
+
typeOptions: { password: true },
|
|
15
|
+
default: '',
|
|
16
|
+
required: true,
|
|
17
|
+
description: 'Your Posthawk API key. Find it in Dashboard → API Keys.',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
displayName: 'Base URL',
|
|
21
|
+
name: 'baseUrl',
|
|
22
|
+
type: 'string',
|
|
23
|
+
default: 'https://api.posthawk.dev',
|
|
24
|
+
description: 'API base URL. Change this if you are self-hosting Posthawk.',
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
this.authenticate = {
|
|
28
|
+
type: 'generic',
|
|
29
|
+
properties: {
|
|
30
|
+
headers: {
|
|
31
|
+
Authorization: '=Bearer {{$credentials.apiKey}}',
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
this.test = {
|
|
36
|
+
request: {
|
|
37
|
+
baseURL: '={{$credentials.baseUrl}}',
|
|
38
|
+
url: '/v1/emails/queue/stats',
|
|
39
|
+
method: 'GET',
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
exports.PosthawkApi = PosthawkApi;
|
|
@@ -0,0 +1,664 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Posthawk = void 0;
|
|
4
|
+
class Posthawk {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.description = {
|
|
7
|
+
displayName: 'Posthawk',
|
|
8
|
+
name: 'posthawk',
|
|
9
|
+
icon: 'file:posthawk.svg',
|
|
10
|
+
group: ['transform'],
|
|
11
|
+
version: 1,
|
|
12
|
+
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
|
13
|
+
description: 'Send, schedule, and manage emails with Posthawk',
|
|
14
|
+
defaults: {
|
|
15
|
+
name: 'Posthawk',
|
|
16
|
+
},
|
|
17
|
+
inputs: ['main'],
|
|
18
|
+
outputs: ['main'],
|
|
19
|
+
credentials: [
|
|
20
|
+
{
|
|
21
|
+
name: 'posthawkApi',
|
|
22
|
+
required: true,
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
properties: [
|
|
26
|
+
// ─── Resource ───
|
|
27
|
+
{
|
|
28
|
+
displayName: 'Resource',
|
|
29
|
+
name: 'resource',
|
|
30
|
+
type: 'options',
|
|
31
|
+
noDataExpression: true,
|
|
32
|
+
options: [
|
|
33
|
+
{ name: 'Email', value: 'email' },
|
|
34
|
+
{ name: 'Scheduled Email', value: 'scheduled' },
|
|
35
|
+
{ name: 'Contact', value: 'contact' },
|
|
36
|
+
{ name: 'Suppression', value: 'suppression' },
|
|
37
|
+
{ name: 'Webhook', value: 'webhook' },
|
|
38
|
+
],
|
|
39
|
+
default: 'email',
|
|
40
|
+
},
|
|
41
|
+
// ─── Email Operations ───
|
|
42
|
+
{
|
|
43
|
+
displayName: 'Operation',
|
|
44
|
+
name: 'operation',
|
|
45
|
+
type: 'options',
|
|
46
|
+
noDataExpression: true,
|
|
47
|
+
displayOptions: { show: { resource: ['email'] } },
|
|
48
|
+
options: [
|
|
49
|
+
{ name: 'Send', value: 'send', action: 'Send an email', description: 'Send a transactional email' },
|
|
50
|
+
{ name: 'Send Batch', value: 'batch', action: 'Send batch emails', description: 'Send multiple emails in one request' },
|
|
51
|
+
{ name: 'Get', value: 'get', action: 'Get email status', description: 'Get the status of a sent email by job ID' },
|
|
52
|
+
],
|
|
53
|
+
default: 'send',
|
|
54
|
+
},
|
|
55
|
+
// ─── Scheduled Operations ───
|
|
56
|
+
{
|
|
57
|
+
displayName: 'Operation',
|
|
58
|
+
name: 'operation',
|
|
59
|
+
type: 'options',
|
|
60
|
+
noDataExpression: true,
|
|
61
|
+
displayOptions: { show: { resource: ['scheduled'] } },
|
|
62
|
+
options: [
|
|
63
|
+
{ name: 'List', value: 'list', action: 'List scheduled emails' },
|
|
64
|
+
{ name: 'Get', value: 'get', action: 'Get a scheduled email' },
|
|
65
|
+
{ name: 'Cancel', value: 'cancel', action: 'Cancel a scheduled email' },
|
|
66
|
+
{ name: 'Reschedule', value: 'reschedule', action: 'Reschedule an email' },
|
|
67
|
+
],
|
|
68
|
+
default: 'list',
|
|
69
|
+
},
|
|
70
|
+
// ─── Contact Operations ───
|
|
71
|
+
{
|
|
72
|
+
displayName: 'Operation',
|
|
73
|
+
name: 'operation',
|
|
74
|
+
type: 'options',
|
|
75
|
+
noDataExpression: true,
|
|
76
|
+
displayOptions: { show: { resource: ['contact'] } },
|
|
77
|
+
options: [
|
|
78
|
+
{ name: 'List', value: 'list', action: 'List contacts' },
|
|
79
|
+
{ name: 'Get', value: 'get', action: 'Get a contact' },
|
|
80
|
+
{ name: 'Create', value: 'create', action: 'Create a contact' },
|
|
81
|
+
{ name: 'Update', value: 'update', action: 'Update a contact' },
|
|
82
|
+
{ name: 'Delete', value: 'delete', action: 'Delete a contact' },
|
|
83
|
+
],
|
|
84
|
+
default: 'list',
|
|
85
|
+
},
|
|
86
|
+
// ─── Suppression Operations ───
|
|
87
|
+
{
|
|
88
|
+
displayName: 'Operation',
|
|
89
|
+
name: 'operation',
|
|
90
|
+
type: 'options',
|
|
91
|
+
noDataExpression: true,
|
|
92
|
+
displayOptions: { show: { resource: ['suppression'] } },
|
|
93
|
+
options: [
|
|
94
|
+
{ name: 'List', value: 'list', action: 'List suppressions' },
|
|
95
|
+
{ name: 'Add', value: 'add', action: 'Add a suppression' },
|
|
96
|
+
{ name: 'Remove', value: 'remove', action: 'Remove a suppression' },
|
|
97
|
+
],
|
|
98
|
+
default: 'list',
|
|
99
|
+
},
|
|
100
|
+
// ─── Webhook Operations ───
|
|
101
|
+
{
|
|
102
|
+
displayName: 'Operation',
|
|
103
|
+
name: 'operation',
|
|
104
|
+
type: 'options',
|
|
105
|
+
noDataExpression: true,
|
|
106
|
+
displayOptions: { show: { resource: ['webhook'] } },
|
|
107
|
+
options: [
|
|
108
|
+
{ name: 'List', value: 'list', action: 'List webhooks' },
|
|
109
|
+
{ name: 'Create', value: 'create', action: 'Create a webhook' },
|
|
110
|
+
{ name: 'Update', value: 'update', action: 'Update a webhook' },
|
|
111
|
+
{ name: 'Delete', value: 'delete', action: 'Delete a webhook' },
|
|
112
|
+
{ name: 'Test', value: 'test', action: 'Send a test webhook' },
|
|
113
|
+
],
|
|
114
|
+
default: 'list',
|
|
115
|
+
},
|
|
116
|
+
// ════════════════════════════════════════════════
|
|
117
|
+
// EMAIL FIELDS
|
|
118
|
+
// ════════════════════════════════════════════════
|
|
119
|
+
{
|
|
120
|
+
displayName: 'From',
|
|
121
|
+
name: 'from',
|
|
122
|
+
type: 'string',
|
|
123
|
+
default: '',
|
|
124
|
+
required: true,
|
|
125
|
+
placeholder: 'hello@yourdomain.com',
|
|
126
|
+
description: 'Sender email address (must be from a verified domain)',
|
|
127
|
+
displayOptions: { show: { resource: ['email'], operation: ['send'] } },
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
displayName: 'To',
|
|
131
|
+
name: 'to',
|
|
132
|
+
type: 'string',
|
|
133
|
+
default: '',
|
|
134
|
+
required: true,
|
|
135
|
+
placeholder: 'user@example.com',
|
|
136
|
+
description: 'Recipient email address. For multiple recipients, separate with commas.',
|
|
137
|
+
displayOptions: { show: { resource: ['email'], operation: ['send'] } },
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
displayName: 'Subject',
|
|
141
|
+
name: 'subject',
|
|
142
|
+
type: 'string',
|
|
143
|
+
default: '',
|
|
144
|
+
required: true,
|
|
145
|
+
description: 'Email subject line',
|
|
146
|
+
displayOptions: { show: { resource: ['email'], operation: ['send'] } },
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
displayName: 'HTML Body',
|
|
150
|
+
name: 'html',
|
|
151
|
+
type: 'string',
|
|
152
|
+
typeOptions: { rows: 6 },
|
|
153
|
+
default: '',
|
|
154
|
+
description: 'HTML content of the email',
|
|
155
|
+
displayOptions: { show: { resource: ['email'], operation: ['send'] } },
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
displayName: 'Text Body',
|
|
159
|
+
name: 'text',
|
|
160
|
+
type: 'string',
|
|
161
|
+
typeOptions: { rows: 4 },
|
|
162
|
+
default: '',
|
|
163
|
+
description: 'Plain text fallback',
|
|
164
|
+
displayOptions: { show: { resource: ['email'], operation: ['send'] } },
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
displayName: 'Additional Fields',
|
|
168
|
+
name: 'additionalFields',
|
|
169
|
+
type: 'collection',
|
|
170
|
+
placeholder: 'Add Field',
|
|
171
|
+
default: {},
|
|
172
|
+
displayOptions: { show: { resource: ['email'], operation: ['send'] } },
|
|
173
|
+
options: [
|
|
174
|
+
{
|
|
175
|
+
displayName: 'CC',
|
|
176
|
+
name: 'cc',
|
|
177
|
+
type: 'string',
|
|
178
|
+
default: '',
|
|
179
|
+
description: 'CC recipients (comma-separated)',
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
displayName: 'BCC',
|
|
183
|
+
name: 'bcc',
|
|
184
|
+
type: 'string',
|
|
185
|
+
default: '',
|
|
186
|
+
description: 'BCC recipients (comma-separated)',
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
displayName: 'Reply To',
|
|
190
|
+
name: 'replyTo',
|
|
191
|
+
type: 'string',
|
|
192
|
+
default: '',
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
displayName: 'Template ID',
|
|
196
|
+
name: 'templateId',
|
|
197
|
+
type: 'string',
|
|
198
|
+
default: '',
|
|
199
|
+
description: 'Use an email template by ID instead of HTML/text body',
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
displayName: 'Template Variables (JSON)',
|
|
203
|
+
name: 'variables',
|
|
204
|
+
type: 'json',
|
|
205
|
+
default: '{}',
|
|
206
|
+
description: 'Variables to substitute in the template',
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
displayName: 'Schedule For',
|
|
210
|
+
name: 'scheduledFor',
|
|
211
|
+
type: 'dateTime',
|
|
212
|
+
default: '',
|
|
213
|
+
description: 'Schedule the email for future delivery (ISO 8601)',
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
displayName: 'Timezone',
|
|
217
|
+
name: 'timezone',
|
|
218
|
+
type: 'string',
|
|
219
|
+
default: 'UTC',
|
|
220
|
+
description: 'Timezone for scheduled delivery (e.g. America/New_York)',
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
displayName: 'Tags (JSON)',
|
|
224
|
+
name: 'tags',
|
|
225
|
+
type: 'json',
|
|
226
|
+
default: '{}',
|
|
227
|
+
description: 'Key-value tags for categorization',
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
displayName: 'Metadata (JSON)',
|
|
231
|
+
name: 'metadata',
|
|
232
|
+
type: 'json',
|
|
233
|
+
default: '{}',
|
|
234
|
+
description: 'Custom metadata attached to the email',
|
|
235
|
+
},
|
|
236
|
+
],
|
|
237
|
+
},
|
|
238
|
+
// Email Get
|
|
239
|
+
{
|
|
240
|
+
displayName: 'Job ID',
|
|
241
|
+
name: 'jobId',
|
|
242
|
+
type: 'string',
|
|
243
|
+
default: '',
|
|
244
|
+
required: true,
|
|
245
|
+
description: 'The job ID returned when the email was sent',
|
|
246
|
+
displayOptions: { show: { resource: ['email'], operation: ['get'] } },
|
|
247
|
+
},
|
|
248
|
+
// Email Batch
|
|
249
|
+
{
|
|
250
|
+
displayName: 'Emails (JSON)',
|
|
251
|
+
name: 'emails',
|
|
252
|
+
type: 'json',
|
|
253
|
+
default: '[]',
|
|
254
|
+
required: true,
|
|
255
|
+
description: 'Array of email objects to send in batch',
|
|
256
|
+
displayOptions: { show: { resource: ['email'], operation: ['batch'] } },
|
|
257
|
+
},
|
|
258
|
+
// ════════════════════════════════════════════════
|
|
259
|
+
// SCHEDULED FIELDS
|
|
260
|
+
// ════════════════════════════════════════════════
|
|
261
|
+
{
|
|
262
|
+
displayName: 'Scheduled Email ID',
|
|
263
|
+
name: 'scheduledId',
|
|
264
|
+
type: 'string',
|
|
265
|
+
default: '',
|
|
266
|
+
required: true,
|
|
267
|
+
displayOptions: { show: { resource: ['scheduled'], operation: ['get', 'cancel', 'reschedule'] } },
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
displayName: 'New Scheduled Time',
|
|
271
|
+
name: 'scheduledFor',
|
|
272
|
+
type: 'dateTime',
|
|
273
|
+
default: '',
|
|
274
|
+
required: true,
|
|
275
|
+
description: 'New delivery time (ISO 8601)',
|
|
276
|
+
displayOptions: { show: { resource: ['scheduled'], operation: ['reschedule'] } },
|
|
277
|
+
},
|
|
278
|
+
// ════════════════════════════════════════════════
|
|
279
|
+
// CONTACT FIELDS
|
|
280
|
+
// ════════════════════════════════════════════════
|
|
281
|
+
{
|
|
282
|
+
displayName: 'Contact ID',
|
|
283
|
+
name: 'contactId',
|
|
284
|
+
type: 'string',
|
|
285
|
+
default: '',
|
|
286
|
+
required: true,
|
|
287
|
+
displayOptions: { show: { resource: ['contact'], operation: ['get', 'update', 'delete'] } },
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
displayName: 'Email',
|
|
291
|
+
name: 'email',
|
|
292
|
+
type: 'string',
|
|
293
|
+
default: '',
|
|
294
|
+
required: true,
|
|
295
|
+
displayOptions: { show: { resource: ['contact'], operation: ['create'] } },
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
displayName: 'Additional Fields',
|
|
299
|
+
name: 'contactFields',
|
|
300
|
+
type: 'collection',
|
|
301
|
+
placeholder: 'Add Field',
|
|
302
|
+
default: {},
|
|
303
|
+
displayOptions: { show: { resource: ['contact'], operation: ['create', 'update'] } },
|
|
304
|
+
options: [
|
|
305
|
+
{ displayName: 'Name', name: 'name', type: 'string', default: '' },
|
|
306
|
+
{
|
|
307
|
+
displayName: 'Tags',
|
|
308
|
+
name: 'tags',
|
|
309
|
+
type: 'string',
|
|
310
|
+
default: '',
|
|
311
|
+
description: 'Comma-separated tags',
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
displayName: 'Metadata (JSON)',
|
|
315
|
+
name: 'metadata',
|
|
316
|
+
type: 'json',
|
|
317
|
+
default: '{}',
|
|
318
|
+
},
|
|
319
|
+
],
|
|
320
|
+
},
|
|
321
|
+
// ════════════════════════════════════════════════
|
|
322
|
+
// SUPPRESSION FIELDS
|
|
323
|
+
// ════════════════════════════════════════════════
|
|
324
|
+
{
|
|
325
|
+
displayName: 'Email',
|
|
326
|
+
name: 'email',
|
|
327
|
+
type: 'string',
|
|
328
|
+
default: '',
|
|
329
|
+
required: true,
|
|
330
|
+
displayOptions: { show: { resource: ['suppression'], operation: ['add'] } },
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
displayName: 'Reason',
|
|
334
|
+
name: 'reason',
|
|
335
|
+
type: 'options',
|
|
336
|
+
options: [
|
|
337
|
+
{ name: 'Bounce', value: 'bounce' },
|
|
338
|
+
{ name: 'Complaint', value: 'complaint' },
|
|
339
|
+
{ name: 'Manual', value: 'manual' },
|
|
340
|
+
],
|
|
341
|
+
default: 'manual',
|
|
342
|
+
displayOptions: { show: { resource: ['suppression'], operation: ['add'] } },
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
displayName: 'Suppression ID',
|
|
346
|
+
name: 'suppressionId',
|
|
347
|
+
type: 'string',
|
|
348
|
+
default: '',
|
|
349
|
+
required: true,
|
|
350
|
+
displayOptions: { show: { resource: ['suppression'], operation: ['remove'] } },
|
|
351
|
+
},
|
|
352
|
+
// ════════════════════════════════════════════════
|
|
353
|
+
// WEBHOOK FIELDS
|
|
354
|
+
// ════════════════════════════════════════════════
|
|
355
|
+
{
|
|
356
|
+
displayName: 'Webhook ID',
|
|
357
|
+
name: 'webhookId',
|
|
358
|
+
type: 'string',
|
|
359
|
+
default: '',
|
|
360
|
+
required: true,
|
|
361
|
+
displayOptions: { show: { resource: ['webhook'], operation: ['update', 'delete', 'test'] } },
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
displayName: 'URL',
|
|
365
|
+
name: 'url',
|
|
366
|
+
type: 'string',
|
|
367
|
+
default: '',
|
|
368
|
+
required: true,
|
|
369
|
+
placeholder: 'https://example.com/webhook',
|
|
370
|
+
displayOptions: { show: { resource: ['webhook'], operation: ['create'] } },
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
displayName: 'Events',
|
|
374
|
+
name: 'events',
|
|
375
|
+
type: 'multiOptions',
|
|
376
|
+
options: [
|
|
377
|
+
{ name: 'Send', value: 'send' },
|
|
378
|
+
{ name: 'Delivery', value: 'delivery' },
|
|
379
|
+
{ name: 'Bounce', value: 'bounce' },
|
|
380
|
+
{ name: 'Complaint', value: 'complaint' },
|
|
381
|
+
{ name: 'Open', value: 'open' },
|
|
382
|
+
{ name: 'Click', value: 'click' },
|
|
383
|
+
{ name: 'Reject', value: 'reject' },
|
|
384
|
+
{ name: 'Delivery Delay', value: 'delivery_delay' },
|
|
385
|
+
],
|
|
386
|
+
default: ['delivery', 'bounce', 'complaint'],
|
|
387
|
+
displayOptions: { show: { resource: ['webhook'], operation: ['create', 'update'] } },
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
displayName: 'Webhook URL (for Update)',
|
|
391
|
+
name: 'url',
|
|
392
|
+
type: 'string',
|
|
393
|
+
default: '',
|
|
394
|
+
displayOptions: { show: { resource: ['webhook'], operation: ['update'] } },
|
|
395
|
+
},
|
|
396
|
+
],
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
async execute() {
|
|
400
|
+
const items = this.getInputData();
|
|
401
|
+
const returnData = [];
|
|
402
|
+
const resource = this.getNodeParameter('resource', 0);
|
|
403
|
+
const operation = this.getNodeParameter('operation', 0);
|
|
404
|
+
const credentials = await this.getCredentials('posthawkApi');
|
|
405
|
+
const baseUrl = credentials.baseUrl;
|
|
406
|
+
for (let i = 0; i < items.length; i++) {
|
|
407
|
+
try {
|
|
408
|
+
let responseData;
|
|
409
|
+
// ─── Email ───
|
|
410
|
+
if (resource === 'email') {
|
|
411
|
+
if (operation === 'send') {
|
|
412
|
+
const to = this.getNodeParameter('to', i).split(',').map(s => s.trim());
|
|
413
|
+
const additional = this.getNodeParameter('additionalFields', i, {});
|
|
414
|
+
const body = {
|
|
415
|
+
from: this.getNodeParameter('from', i),
|
|
416
|
+
to: to.length === 1 ? to[0] : to,
|
|
417
|
+
subject: this.getNodeParameter('subject', i),
|
|
418
|
+
};
|
|
419
|
+
const html = this.getNodeParameter('html', i, '');
|
|
420
|
+
const text = this.getNodeParameter('text', i, '');
|
|
421
|
+
if (html)
|
|
422
|
+
body.html = html;
|
|
423
|
+
if (text)
|
|
424
|
+
body.text = text;
|
|
425
|
+
if (additional.cc)
|
|
426
|
+
body.cc = additional.cc.split(',').map(s => s.trim());
|
|
427
|
+
if (additional.bcc)
|
|
428
|
+
body.bcc = additional.bcc.split(',').map(s => s.trim());
|
|
429
|
+
if (additional.replyTo)
|
|
430
|
+
body.replyTo = additional.replyTo;
|
|
431
|
+
if (additional.templateId)
|
|
432
|
+
body.templateId = additional.templateId;
|
|
433
|
+
if (additional.variables)
|
|
434
|
+
body.variables = JSON.parse(additional.variables);
|
|
435
|
+
if (additional.scheduledFor)
|
|
436
|
+
body.scheduledFor = additional.scheduledFor;
|
|
437
|
+
if (additional.timezone)
|
|
438
|
+
body.timezone = additional.timezone;
|
|
439
|
+
if (additional.tags)
|
|
440
|
+
body.tags = JSON.parse(additional.tags);
|
|
441
|
+
if (additional.metadata)
|
|
442
|
+
body.metadata = JSON.parse(additional.metadata);
|
|
443
|
+
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'posthawkApi', {
|
|
444
|
+
method: 'POST',
|
|
445
|
+
url: `${baseUrl}/v1/emails/send`,
|
|
446
|
+
body,
|
|
447
|
+
json: true,
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
else if (operation === 'batch') {
|
|
451
|
+
const emails = JSON.parse(this.getNodeParameter('emails', i));
|
|
452
|
+
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'posthawkApi', {
|
|
453
|
+
method: 'POST',
|
|
454
|
+
url: `${baseUrl}/v1/emails/batch`,
|
|
455
|
+
body: { emails },
|
|
456
|
+
json: true,
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
else if (operation === 'get') {
|
|
460
|
+
const jobId = this.getNodeParameter('jobId', i);
|
|
461
|
+
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'posthawkApi', {
|
|
462
|
+
method: 'GET',
|
|
463
|
+
url: `${baseUrl}/v1/emails/send/${jobId}`,
|
|
464
|
+
json: true,
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
// ─── Scheduled ───
|
|
469
|
+
else if (resource === 'scheduled') {
|
|
470
|
+
if (operation === 'list') {
|
|
471
|
+
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'posthawkApi', {
|
|
472
|
+
method: 'GET',
|
|
473
|
+
url: `${baseUrl}/v1/scheduled`,
|
|
474
|
+
json: true,
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
else if (operation === 'get') {
|
|
478
|
+
const id = this.getNodeParameter('scheduledId', i);
|
|
479
|
+
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'posthawkApi', {
|
|
480
|
+
method: 'GET',
|
|
481
|
+
url: `${baseUrl}/v1/scheduled/${id}`,
|
|
482
|
+
json: true,
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
else if (operation === 'cancel') {
|
|
486
|
+
const id = this.getNodeParameter('scheduledId', i);
|
|
487
|
+
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'posthawkApi', {
|
|
488
|
+
method: 'DELETE',
|
|
489
|
+
url: `${baseUrl}/v1/scheduled/${id}`,
|
|
490
|
+
json: true,
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
else if (operation === 'reschedule') {
|
|
494
|
+
const id = this.getNodeParameter('scheduledId', i);
|
|
495
|
+
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'posthawkApi', {
|
|
496
|
+
method: 'PATCH',
|
|
497
|
+
url: `${baseUrl}/v1/scheduled/${id}/reschedule`,
|
|
498
|
+
body: { scheduledFor: this.getNodeParameter('scheduledFor', i) },
|
|
499
|
+
json: true,
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
// ─── Contact ───
|
|
504
|
+
else if (resource === 'contact') {
|
|
505
|
+
if (operation === 'list') {
|
|
506
|
+
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'posthawkApi', {
|
|
507
|
+
method: 'GET',
|
|
508
|
+
url: `${baseUrl}/v1/emails/contacts`,
|
|
509
|
+
json: true,
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
else if (operation === 'get') {
|
|
513
|
+
const id = this.getNodeParameter('contactId', i);
|
|
514
|
+
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'posthawkApi', {
|
|
515
|
+
method: 'GET',
|
|
516
|
+
url: `${baseUrl}/v1/emails/contacts/${id}`,
|
|
517
|
+
json: true,
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
else if (operation === 'create') {
|
|
521
|
+
const fields = this.getNodeParameter('contactFields', i, {});
|
|
522
|
+
const body = {
|
|
523
|
+
email: this.getNodeParameter('email', i),
|
|
524
|
+
};
|
|
525
|
+
if (fields.name)
|
|
526
|
+
body.name = fields.name;
|
|
527
|
+
if (fields.tags)
|
|
528
|
+
body.tags = fields.tags.split(',').map(s => s.trim());
|
|
529
|
+
if (fields.metadata)
|
|
530
|
+
body.metadata = JSON.parse(fields.metadata);
|
|
531
|
+
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'posthawkApi', {
|
|
532
|
+
method: 'POST',
|
|
533
|
+
url: `${baseUrl}/v1/emails/contacts`,
|
|
534
|
+
body,
|
|
535
|
+
json: true,
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
else if (operation === 'update') {
|
|
539
|
+
const id = this.getNodeParameter('contactId', i);
|
|
540
|
+
const fields = this.getNodeParameter('contactFields', i, {});
|
|
541
|
+
const body = {};
|
|
542
|
+
if (fields.name)
|
|
543
|
+
body.name = fields.name;
|
|
544
|
+
if (fields.tags)
|
|
545
|
+
body.tags = fields.tags.split(',').map(s => s.trim());
|
|
546
|
+
if (fields.metadata)
|
|
547
|
+
body.metadata = JSON.parse(fields.metadata);
|
|
548
|
+
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'posthawkApi', {
|
|
549
|
+
method: 'PATCH',
|
|
550
|
+
url: `${baseUrl}/v1/emails/contacts/${id}`,
|
|
551
|
+
body,
|
|
552
|
+
json: true,
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
else if (operation === 'delete') {
|
|
556
|
+
const id = this.getNodeParameter('contactId', i);
|
|
557
|
+
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'posthawkApi', {
|
|
558
|
+
method: 'DELETE',
|
|
559
|
+
url: `${baseUrl}/v1/emails/contacts/${id}`,
|
|
560
|
+
json: true,
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
// ─── Suppression ───
|
|
565
|
+
else if (resource === 'suppression') {
|
|
566
|
+
if (operation === 'list') {
|
|
567
|
+
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'posthawkApi', {
|
|
568
|
+
method: 'GET',
|
|
569
|
+
url: `${baseUrl}/v1/emails/suppressions`,
|
|
570
|
+
json: true,
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
else if (operation === 'add') {
|
|
574
|
+
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'posthawkApi', {
|
|
575
|
+
method: 'POST',
|
|
576
|
+
url: `${baseUrl}/v1/emails/suppressions`,
|
|
577
|
+
body: {
|
|
578
|
+
email: this.getNodeParameter('email', i),
|
|
579
|
+
reason: this.getNodeParameter('reason', i),
|
|
580
|
+
},
|
|
581
|
+
json: true,
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
else if (operation === 'remove') {
|
|
585
|
+
const id = this.getNodeParameter('suppressionId', i);
|
|
586
|
+
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'posthawkApi', {
|
|
587
|
+
method: 'DELETE',
|
|
588
|
+
url: `${baseUrl}/v1/emails/suppressions/${id}`,
|
|
589
|
+
json: true,
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
// ─── Webhook ───
|
|
594
|
+
else if (resource === 'webhook') {
|
|
595
|
+
if (operation === 'list') {
|
|
596
|
+
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'posthawkApi', {
|
|
597
|
+
method: 'GET',
|
|
598
|
+
url: `${baseUrl}/v1/webhooks`,
|
|
599
|
+
json: true,
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
else if (operation === 'create') {
|
|
603
|
+
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'posthawkApi', {
|
|
604
|
+
method: 'POST',
|
|
605
|
+
url: `${baseUrl}/v1/webhooks`,
|
|
606
|
+
body: {
|
|
607
|
+
url: this.getNodeParameter('url', i),
|
|
608
|
+
events: this.getNodeParameter('events', i),
|
|
609
|
+
},
|
|
610
|
+
json: true,
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
else if (operation === 'update') {
|
|
614
|
+
const id = this.getNodeParameter('webhookId', i);
|
|
615
|
+
const body = {};
|
|
616
|
+
const url = this.getNodeParameter('url', i, '');
|
|
617
|
+
const events = this.getNodeParameter('events', i, []);
|
|
618
|
+
if (url)
|
|
619
|
+
body.url = url;
|
|
620
|
+
if (events.length)
|
|
621
|
+
body.events = events;
|
|
622
|
+
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'posthawkApi', {
|
|
623
|
+
method: 'PATCH',
|
|
624
|
+
url: `${baseUrl}/v1/webhooks/${id}`,
|
|
625
|
+
body,
|
|
626
|
+
json: true,
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
else if (operation === 'delete') {
|
|
630
|
+
const id = this.getNodeParameter('webhookId', i);
|
|
631
|
+
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'posthawkApi', {
|
|
632
|
+
method: 'DELETE',
|
|
633
|
+
url: `${baseUrl}/v1/webhooks/${id}`,
|
|
634
|
+
json: true,
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
else if (operation === 'test') {
|
|
638
|
+
const id = this.getNodeParameter('webhookId', i);
|
|
639
|
+
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'posthawkApi', {
|
|
640
|
+
method: 'POST',
|
|
641
|
+
url: `${baseUrl}/v1/webhooks/${id}/test`,
|
|
642
|
+
json: true,
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
if (Array.isArray(responseData)) {
|
|
647
|
+
returnData.push(...responseData.map(d => ({ json: d })));
|
|
648
|
+
}
|
|
649
|
+
else {
|
|
650
|
+
returnData.push({ json: responseData ?? { success: true } });
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
catch (error) {
|
|
654
|
+
if (this.continueOnFail()) {
|
|
655
|
+
returnData.push({ json: { error: error.message } });
|
|
656
|
+
continue;
|
|
657
|
+
}
|
|
658
|
+
throw error;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
return [returnData];
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
exports.Posthawk = Posthawk;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { IHookFunctions, IWebhookFunctions, INodeType, INodeTypeDescription, IWebhookResponseData } from 'n8n-workflow';
|
|
2
|
+
export declare class PosthawkTrigger implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
webhookMethods: {
|
|
5
|
+
default: {
|
|
6
|
+
checkExists(this: IHookFunctions): Promise<boolean>;
|
|
7
|
+
create(this: IHookFunctions): Promise<boolean>;
|
|
8
|
+
delete(this: IHookFunctions): Promise<boolean>;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
webhook(this: IWebhookFunctions): Promise<IWebhookResponseData>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PosthawkTrigger = void 0;
|
|
4
|
+
class PosthawkTrigger {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.description = {
|
|
7
|
+
displayName: 'Posthawk Trigger',
|
|
8
|
+
name: 'posthawkTrigger',
|
|
9
|
+
icon: 'file:posthawk.svg',
|
|
10
|
+
group: ['trigger'],
|
|
11
|
+
version: 1,
|
|
12
|
+
subtitle: '={{$parameter["events"].join(", ")}}',
|
|
13
|
+
description: 'Triggers workflow on Posthawk email events (delivery, bounce, open, etc.)',
|
|
14
|
+
defaults: {
|
|
15
|
+
name: 'Posthawk Trigger',
|
|
16
|
+
},
|
|
17
|
+
inputs: [],
|
|
18
|
+
outputs: ['main'],
|
|
19
|
+
credentials: [
|
|
20
|
+
{
|
|
21
|
+
name: 'posthawkApi',
|
|
22
|
+
required: true,
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
webhooks: [
|
|
26
|
+
{
|
|
27
|
+
name: 'default',
|
|
28
|
+
httpMethod: 'POST',
|
|
29
|
+
responseMode: 'onReceived',
|
|
30
|
+
path: 'webhook',
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
properties: [
|
|
34
|
+
{
|
|
35
|
+
displayName: 'Events',
|
|
36
|
+
name: 'events',
|
|
37
|
+
type: 'multiOptions',
|
|
38
|
+
required: true,
|
|
39
|
+
options: [
|
|
40
|
+
{ name: 'Send', value: 'send', description: 'Email accepted by SES' },
|
|
41
|
+
{ name: 'Delivery', value: 'delivery', description: 'Email delivered to recipient' },
|
|
42
|
+
{ name: 'Bounce', value: 'bounce', description: 'Email bounced' },
|
|
43
|
+
{ name: 'Complaint', value: 'complaint', description: 'Recipient marked as spam' },
|
|
44
|
+
{ name: 'Open', value: 'open', description: 'Email opened by recipient' },
|
|
45
|
+
{ name: 'Click', value: 'click', description: 'Link clicked in email' },
|
|
46
|
+
{ name: 'Reject', value: 'reject', description: 'Email rejected by SES' },
|
|
47
|
+
{ name: 'Delivery Delay', value: 'delivery_delay', description: 'Delivery delayed' },
|
|
48
|
+
],
|
|
49
|
+
default: ['delivery', 'bounce', 'complaint'],
|
|
50
|
+
description: 'Which email events should trigger this workflow',
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
};
|
|
54
|
+
this.webhookMethods = {
|
|
55
|
+
default: {
|
|
56
|
+
async checkExists() {
|
|
57
|
+
const webhookUrl = this.getNodeWebhookUrl('default');
|
|
58
|
+
const credentials = await this.getCredentials('posthawkApi');
|
|
59
|
+
const baseUrl = credentials.baseUrl;
|
|
60
|
+
const response = await this.helpers.httpRequestWithAuthentication.call(this, 'posthawkApi', {
|
|
61
|
+
method: 'GET',
|
|
62
|
+
url: `${baseUrl}/v1/webhooks`,
|
|
63
|
+
json: true,
|
|
64
|
+
});
|
|
65
|
+
const webhooks = Array.isArray(response) ? response : response?.webhooks ?? [];
|
|
66
|
+
const existing = webhooks.find((w) => w.url === webhookUrl);
|
|
67
|
+
if (existing) {
|
|
68
|
+
const webhookData = this.getWorkflowStaticData('node');
|
|
69
|
+
webhookData.webhookId = existing.id;
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
},
|
|
74
|
+
async create() {
|
|
75
|
+
const webhookUrl = this.getNodeWebhookUrl('default');
|
|
76
|
+
const events = this.getNodeParameter('events');
|
|
77
|
+
const credentials = await this.getCredentials('posthawkApi');
|
|
78
|
+
const baseUrl = credentials.baseUrl;
|
|
79
|
+
const response = await this.helpers.httpRequestWithAuthentication.call(this, 'posthawkApi', {
|
|
80
|
+
method: 'POST',
|
|
81
|
+
url: `${baseUrl}/v1/webhooks`,
|
|
82
|
+
body: {
|
|
83
|
+
url: webhookUrl,
|
|
84
|
+
events,
|
|
85
|
+
},
|
|
86
|
+
json: true,
|
|
87
|
+
});
|
|
88
|
+
if (!response?.id && !response?.webhook?.id) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
const webhookData = this.getWorkflowStaticData('node');
|
|
92
|
+
webhookData.webhookId = response.id || response.webhook.id;
|
|
93
|
+
return true;
|
|
94
|
+
},
|
|
95
|
+
async delete() {
|
|
96
|
+
const webhookData = this.getWorkflowStaticData('node');
|
|
97
|
+
const webhookId = webhookData.webhookId;
|
|
98
|
+
if (!webhookId)
|
|
99
|
+
return true;
|
|
100
|
+
const credentials = await this.getCredentials('posthawkApi');
|
|
101
|
+
const baseUrl = credentials.baseUrl;
|
|
102
|
+
try {
|
|
103
|
+
await this.helpers.httpRequestWithAuthentication.call(this, 'posthawkApi', {
|
|
104
|
+
method: 'DELETE',
|
|
105
|
+
url: `${baseUrl}/v1/webhooks/${webhookId}`,
|
|
106
|
+
json: true,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// Webhook may have been deleted already
|
|
111
|
+
}
|
|
112
|
+
delete webhookData.webhookId;
|
|
113
|
+
return true;
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
async webhook() {
|
|
119
|
+
const body = this.getBodyData();
|
|
120
|
+
// Posthawk sends webhook payloads with signature in X-Posthawk-Signature header
|
|
121
|
+
// The event type is in the body
|
|
122
|
+
return {
|
|
123
|
+
workflowData: [
|
|
124
|
+
this.helpers.returnJsonArray([body]),
|
|
125
|
+
],
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
exports.PosthawkTrigger = PosthawkTrigger;
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "n8n-nodes-posthawk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "n8n node for Posthawk — send, schedule, and manage emails via the Posthawk API",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"n8n-community-node-package",
|
|
7
|
+
"n8n",
|
|
8
|
+
"posthawk",
|
|
9
|
+
"email",
|
|
10
|
+
"transactional-email",
|
|
11
|
+
"email-api"
|
|
12
|
+
],
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"author": {
|
|
15
|
+
"name": "Posthawk",
|
|
16
|
+
"email": "support@posthawk.dev"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/endibuka/posthawk"
|
|
21
|
+
},
|
|
22
|
+
"main": "dist/index.js",
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc && gulp build:icons",
|
|
25
|
+
"dev": "tsc --watch",
|
|
26
|
+
"lint": "tsc --noEmit",
|
|
27
|
+
"prepublishOnly": "npm run build"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist"
|
|
31
|
+
],
|
|
32
|
+
"n8n": {
|
|
33
|
+
"n8nNodesApiVersion": 1,
|
|
34
|
+
"credentials": [
|
|
35
|
+
"dist/credentials/PosthawkApi.credentials.js"
|
|
36
|
+
],
|
|
37
|
+
"nodes": [
|
|
38
|
+
"dist/nodes/Posthawk/Posthawk.node.js",
|
|
39
|
+
"dist/nodes/Posthawk/PosthawkTrigger.node.js"
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/node": "^22.0.0",
|
|
44
|
+
"gulp": "^5.0.0",
|
|
45
|
+
"n8n-workflow": "^1.0.0",
|
|
46
|
+
"typescript": "^5.5.0"
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"n8n-workflow": "*"
|
|
50
|
+
}
|
|
51
|
+
}
|