@tratto/angular 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 ADDED
@@ -0,0 +1,502 @@
1
+ # @tratto/angular
2
+
3
+ Official Angular SDK for the [Tratto](https://tratto.email) email platform.
4
+
5
+ [![npm](https://img.shields.io/npm/v/@tratto/angular)](https://www.npmjs.com/package/@tratto/angular)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ npm install @tratto/angular
12
+ ```
13
+
14
+ `@tratto/angular` requires Angular 22+ and `@angular/common/http`. No other peer dependencies.
15
+
16
+ ---
17
+
18
+ ## Setup
19
+
20
+ ### Standalone applications (Angular 14+)
21
+
22
+ ```ts
23
+ // app.config.ts
24
+ import { ApplicationConfig } from '@angular/core';
25
+ import { provideHttpClient } from '@angular/common/http';
26
+ import { provideTratt } from '@tratto/angular';
27
+
28
+ export const appConfig: ApplicationConfig = {
29
+ providers: [
30
+ provideHttpClient(),
31
+ provideTratt({ apiKey: 'tratto_live_...' }),
32
+ ],
33
+ };
34
+ ```
35
+
36
+ ### NgModule applications
37
+
38
+ ```ts
39
+ // app.module.ts
40
+ import { NgModule } from '@angular/core';
41
+ import { HttpClientModule } from '@angular/common/http';
42
+ import { TrattoModule } from '@tratto/angular';
43
+
44
+ @NgModule({
45
+ imports: [
46
+ HttpClientModule,
47
+ TrattoModule.forRoot({ apiKey: 'tratto_live_...' }),
48
+ ],
49
+ })
50
+ export class AppModule {}
51
+ ```
52
+
53
+ ### Custom base URL
54
+
55
+ ```ts
56
+ provideTratt({
57
+ apiKey: 'tratto_live_...',
58
+ baseUrl: 'https://api.tratto.email', // optional — this is the default
59
+ })
60
+ ```
61
+
62
+ ---
63
+
64
+ ## Usage
65
+
66
+ Inject `TrattoService` (facade) or any individual resource service.
67
+
68
+ ```ts
69
+ import { Component, inject } from '@angular/core';
70
+ import { TrattoService } from '@tratto/angular';
71
+
72
+ @Component({ ... })
73
+ export class MyComponent {
74
+ private tratto = inject(TrattoService);
75
+ }
76
+ ```
77
+
78
+ You can also inject resource services directly:
79
+
80
+ ```ts
81
+ import { EmailsService } from '@tratto/angular';
82
+
83
+ @Component({ ... })
84
+ export class MyComponent {
85
+ private emails = inject(EmailsService);
86
+ }
87
+ ```
88
+
89
+ All methods return `Observable<T>`. Subscribe or use the `async` pipe as you would with any Angular HTTP call.
90
+
91
+ ---
92
+
93
+ ## API Reference
94
+
95
+ ### Emails
96
+
97
+ ```ts
98
+ const { emails } = inject(TrattoService);
99
+ ```
100
+
101
+ #### `emails.send(params, idempotencyKey?)`
102
+
103
+ Send a transactional email. At least one of `html`, `text`, or `templateId` is required.
104
+
105
+ ```ts
106
+ emails.send({
107
+ from: 'Tratto <hello@mail.acme.com>',
108
+ to: 'user@example.com',
109
+ subject: 'Welcome!',
110
+ html: '<p>Hello world</p>',
111
+ }).subscribe(({ id }) => console.log('Email sent:', id));
112
+ ```
113
+
114
+ With a template and idempotency key:
115
+
116
+ ```ts
117
+ emails.send(
118
+ {
119
+ from: 'hello@mail.acme.com',
120
+ to: ['alice@example.com', 'bob@example.com'],
121
+ subject: 'Reset your password',
122
+ templateId: 'tpl_abc123',
123
+ variables: { name: 'Alice', link: 'https://...' },
124
+ },
125
+ 'unique-idempotency-key',
126
+ ).subscribe(({ id }) => console.log(id));
127
+ ```
128
+
129
+ #### `emails.list(params?)`
130
+
131
+ ```ts
132
+ emails.list({ limit: 20, status: 'delivered' })
133
+ .subscribe(({ data, pagination }) => console.log(data));
134
+ ```
135
+
136
+ #### `emails.get(id)`
137
+
138
+ ```ts
139
+ emails.get('em_abc123').subscribe(email => console.log(email));
140
+ ```
141
+
142
+ #### `emails.listEvents(id)`
143
+
144
+ ```ts
145
+ emails.listEvents('em_abc123').subscribe(events => console.log(events));
146
+ ```
147
+
148
+ ---
149
+
150
+ ### Contacts
151
+
152
+ ```ts
153
+ const { contacts } = inject(TrattoService);
154
+ ```
155
+
156
+ #### `contacts.create(params)`
157
+
158
+ ```ts
159
+ contacts.create({
160
+ email: 'alice@example.com',
161
+ firstName: 'Alice',
162
+ tags: ['vip'],
163
+ }).subscribe(({ id }) => console.log(id));
164
+ ```
165
+
166
+ #### `contacts.list(params?)`
167
+
168
+ ```ts
169
+ contacts.list({ status: 'subscribed', limit: 50 })
170
+ .subscribe(({ data }) => console.log(data));
171
+ ```
172
+
173
+ #### `contacts.update(id, params)`
174
+
175
+ ```ts
176
+ contacts.update('con_abc123', { status: 'unsubscribed' })
177
+ .subscribe(({ id }) => console.log(id));
178
+ ```
179
+
180
+ #### `contacts.importCsv(csvText)`
181
+
182
+ Bulk-import contacts from a CSV string (async job). Poll `getImportJob` to track progress.
183
+
184
+ ```ts
185
+ const csv = `email,first_name,last_name
186
+ alice@example.com,Alice,Smith
187
+ bob@example.com,Bob,Jones`;
188
+
189
+ contacts.importCsv(csv).subscribe(({ jobId }) => {
190
+ contacts.getImportJob(jobId).subscribe(status => console.log(status));
191
+ });
192
+ ```
193
+
194
+ ---
195
+
196
+ ### Audiences
197
+
198
+ ```ts
199
+ const { audiences } = inject(TrattoService);
200
+ ```
201
+
202
+ ```ts
203
+ // Create a dynamic segment
204
+ audiences.create({
205
+ name: 'Power users',
206
+ rules: [{ field: 'tags', operator: 'array_contains', value: 'vip' }],
207
+ }).subscribe(({ id }) => console.log(id));
208
+
209
+ // List
210
+ audiences.list().subscribe(({ data }) => console.log(data));
211
+
212
+ // Add contacts (up to 500 IDs per call)
213
+ audiences.addContacts('aud_abc123', ['con_1', 'con_2'])
214
+ .subscribe(result => console.log(result.added));
215
+ ```
216
+
217
+ ---
218
+
219
+ ### Campaigns
220
+
221
+ ```ts
222
+ const { campaigns } = inject(TrattoService);
223
+ ```
224
+
225
+ ```ts
226
+ // Create a draft campaign
227
+ campaigns.create({
228
+ name: 'June Newsletter',
229
+ templateId: 'tpl_abc123',
230
+ audienceId: 'aud_abc123',
231
+ fromName: 'Acme',
232
+ fromEmail: 'news@mail.acme.com',
233
+ subjectA: 'Our June update',
234
+ }).subscribe(({ id }) => console.log(id));
235
+
236
+ // Send immediately
237
+ campaigns.send('cmp_abc123').subscribe(({ status }) => console.log(status));
238
+
239
+ // Schedule for a future date
240
+ campaigns.send('cmp_abc123', { scheduledAt: new Date('2025-07-01T09:00:00Z') })
241
+ .subscribe(({ status }) => console.log(status));
242
+
243
+ // Delivery stats
244
+ campaigns.getStats('cmp_abc123').subscribe(stats => console.log(stats.rates));
245
+
246
+ // Pause
247
+ campaigns.pause('cmp_abc123').subscribe();
248
+
249
+ // Test send
250
+ campaigns.testSend('cmp_abc123', 'me@example.com')
251
+ .subscribe(({ emailId }) => console.log(emailId));
252
+ ```
253
+
254
+ ---
255
+
256
+ ### Templates
257
+
258
+ ```ts
259
+ const { templates } = inject(TrattoService);
260
+ ```
261
+
262
+ ```ts
263
+ // Create
264
+ templates.create({ name: 'Welcome email', html: '<h1>Hi {{name}}!</h1>' })
265
+ .subscribe(tpl => console.log(tpl.id));
266
+
267
+ // Update (auto-creates a new version)
268
+ templates.update('tpl_abc123', { html: '<h1>Hello {{name}}!</h1>' })
269
+ .subscribe(tpl => console.log(tpl.version));
270
+
271
+ // Version history
272
+ templates.listVersions('tpl_abc123').subscribe(versions => console.log(versions));
273
+ templates.getVersion('tpl_abc123', 2).subscribe(v => console.log(v.html));
274
+
275
+ // Test send
276
+ templates.testSend('tpl_abc123', 'me@example.com', { name: 'Alice' }).subscribe();
277
+
278
+ // Delete
279
+ templates.delete('tpl_abc123').subscribe();
280
+ ```
281
+
282
+ ---
283
+
284
+ ### Webhooks
285
+
286
+ ```ts
287
+ const { webhooks } = inject(TrattoService);
288
+ ```
289
+
290
+ ```ts
291
+ // Register — save the secret, it is shown only once
292
+ webhooks.create({
293
+ url: 'https://my-app.com/webhooks/tratto',
294
+ events: ['delivered', 'bounced', 'complained'],
295
+ }).subscribe(({ id, secret }) => {
296
+ console.log('Webhook ID:', id);
297
+ console.log('Signing secret (save this!):', secret);
298
+ });
299
+
300
+ // List
301
+ webhooks.list().subscribe(hooks => console.log(hooks));
302
+
303
+ // Delivery history
304
+ webhooks.listDeliveries('wh_abc123', { limit: 20 })
305
+ .subscribe(({ data }) => console.log(data));
306
+
307
+ // Test connectivity
308
+ webhooks.test('wh_abc123').subscribe(({ queued }) => console.log(queued));
309
+
310
+ // Rotate signing secret
311
+ webhooks.rotateSecret('wh_abc123').subscribe(({ secret }) => console.log(secret));
312
+
313
+ // Delete
314
+ webhooks.delete('wh_abc123').subscribe();
315
+ ```
316
+
317
+ ---
318
+
319
+ ### Domains
320
+
321
+ ```ts
322
+ const { domains } = inject(TrattoService);
323
+ ```
324
+
325
+ ```ts
326
+ // Add domain — response contains the DNS records to publish
327
+ domains.add('mail.acme.com').subscribe(domain => {
328
+ console.log('Publish these DNS records:', domain.records);
329
+ });
330
+
331
+ // Trigger SPF/DKIM/DMARC verification
332
+ domains.verify('dom_abc123').subscribe(domain => console.log(domain.status));
333
+
334
+ // List / get
335
+ domains.list().subscribe(({ data }) => console.log(data));
336
+ domains.get('dom_abc123').subscribe(domain => console.log(domain));
337
+
338
+ // Delete
339
+ domains.delete('dom_abc123').subscribe(({ deletedAt }) => console.log(deletedAt));
340
+ ```
341
+
342
+ ---
343
+
344
+ ### API Keys
345
+
346
+ ```ts
347
+ const { apiKeys } = inject(TrattoService);
348
+ ```
349
+
350
+ ```ts
351
+ // Create — raw key is shown only once
352
+ apiKeys.create({
353
+ name: 'CI deployment key',
354
+ env: 'live',
355
+ permissions: ['emails:send'],
356
+ }).subscribe(key => {
357
+ console.log('Raw key (save this!):', key.key);
358
+ });
359
+
360
+ // List (prefix only, never raw token)
361
+ apiKeys.list().subscribe(({ data }) => console.log(data));
362
+
363
+ // Revoke
364
+ apiKeys.revoke('key_abc123').subscribe(({ revokedAt }) => console.log(revokedAt));
365
+ ```
366
+
367
+ ---
368
+
369
+ ### Analytics
370
+
371
+ ```ts
372
+ const { analytics } = inject(TrattoService);
373
+ ```
374
+
375
+ ```ts
376
+ // Aggregated summary
377
+ analytics.getSummary('30d').subscribe(summary => {
378
+ console.log('Open rate:', summary.openRate);
379
+ console.log('Bounce rate:', summary.bounceRate);
380
+ });
381
+
382
+ // Daily timeseries
383
+ analytics.getTimeseries('7d').subscribe(points => console.log(points));
384
+ ```
385
+
386
+ Supported periods: `'7d'` | `'30d'` | `'90d'`. Results are cached server-side for 1 hour.
387
+
388
+ ---
389
+
390
+ ### Flows
391
+
392
+ ```ts
393
+ const { flows } = inject(TrattoService);
394
+ ```
395
+
396
+ ```ts
397
+ // Create a draft flow
398
+ flows.create({ name: 'Welcome series' }).subscribe(({ id }) => console.log(id));
399
+
400
+ // Configure trigger and steps
401
+ flows.update('flw_abc123', {
402
+ trigger: { type: 'contact_joins_audience', config: { audienceId: 'aud_abc123' } },
403
+ steps: [
404
+ { id: 'step_1', type: 'send_email', config: { templateId: 'tpl_abc123' } },
405
+ { id: 'step_2', type: 'wait', config: { delay: '3d' } },
406
+ { id: 'step_3', type: 'send_email', config: { templateId: 'tpl_def456' } },
407
+ ],
408
+ }).subscribe();
409
+
410
+ // Activate / deactivate
411
+ flows.activate('flw_abc123').subscribe(flow => console.log(flow.status));
412
+ flows.deactivate('flw_abc123').subscribe();
413
+
414
+ // Delete (draft or inactive only)
415
+ flows.delete('flw_abc123').subscribe();
416
+ ```
417
+
418
+ ---
419
+
420
+ ### Workspace
421
+
422
+ ```ts
423
+ const { workspace } = inject(TrattoService);
424
+ ```
425
+
426
+ ```ts
427
+ // Get current workspace
428
+ workspace.get().subscribe(ws => console.log(ws.name, ws.plan));
429
+
430
+ // Update settings
431
+ workspace.update({ name: 'Acme Corp', timezone: 'Europe/Rome' }).subscribe();
432
+
433
+ // Preferences
434
+ workspace.updatePreferences({
435
+ locale: 'en',
436
+ emailNotifications: { weeklyReport: true },
437
+ }).subscribe();
438
+
439
+ // Team management
440
+ workspace.inviteMember({ email: 'dev@acme.com', role: 'admin' }).subscribe();
441
+ workspace.updateMember('usr_abc123', { role: 'member' }).subscribe();
442
+ workspace.removeMember('usr_abc123').subscribe();
443
+ ```
444
+
445
+ ---
446
+
447
+ ## Error handling
448
+
449
+ HTTP errors surface as Angular `HttpErrorResponse`. Use `catchError` from RxJS:
450
+
451
+ ```ts
452
+ import { catchError, EMPTY } from 'rxjs';
453
+
454
+ emails.send({ from: '...', to: '...', subject: '...', html: '...' }).pipe(
455
+ catchError(err => {
456
+ console.error(err.status, err.error?.message);
457
+ return EMPTY;
458
+ }),
459
+ ).subscribe(({ id }) => console.log(id));
460
+ ```
461
+
462
+ ---
463
+
464
+ ## TypeScript
465
+
466
+ The SDK ships full type declarations. All request params and response shapes are exported:
467
+
468
+ ```ts
469
+ import type {
470
+ SendEmailParams,
471
+ EmailDetail,
472
+ EmailEvent,
473
+ PaginatedResponse,
474
+ Contact,
475
+ Audience,
476
+ Campaign,
477
+ CampaignStatsDetail,
478
+ Template,
479
+ Webhook,
480
+ Domain,
481
+ ApiKey,
482
+ ApiKeyCreated,
483
+ AnalyticsSummary,
484
+ TimeseriesPoint,
485
+ Flow,
486
+ Workspace,
487
+ WorkspaceMember,
488
+ } from '@tratto/angular';
489
+ ```
490
+
491
+ ---
492
+
493
+ ## Example app
494
+
495
+ See [`examples/angular-app`](examples/angular-app) for a runnable Angular 22 standalone app
496
+ that demonstrates SDK setup, email sending, contact listing, and analytics.
497
+
498
+ ---
499
+
500
+ ## License
501
+
502
+ MIT