@tratto/email 0.1.0 → 1.0.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/LICENSE +21 -0
- package/README.md +42 -20
- package/dist/index.d.mts +1 -34
- package/dist/index.d.ts +1 -34
- package/dist/index.js +0 -18
- package/dist/index.mjs +0 -18
- package/package.json +11 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tratto
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -51,6 +51,45 @@ const tratto = new Tratto(apiKey, options?);
|
|
|
51
51
|
|
|
52
52
|
---
|
|
53
53
|
|
|
54
|
+
## Test mode
|
|
55
|
+
|
|
56
|
+
Every workspace can create **test API keys** (`tratto_test_…`) alongside live ones. A test key runs the exact same pipeline — statuses, email timeline, webhooks — but **nothing is actually delivered**: no domain verification needed, no monthly quota consumed (test sends have their own daily cap).
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
const tratto = new Tratto('tratto_test_...');
|
|
60
|
+
|
|
61
|
+
// Works immediately, even with an unverified sender domain
|
|
62
|
+
const { id, livemode } = await tratto.emails.send({
|
|
63
|
+
from: 'Acme <hello@any-domain.dev>',
|
|
64
|
+
to: 'delivered@simulator.tratto.email',
|
|
65
|
+
subject: 'Hello from test mode',
|
|
66
|
+
html: '<p>It works!</p>',
|
|
67
|
+
});
|
|
68
|
+
// livemode === false
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Simulator addresses
|
|
72
|
+
|
|
73
|
+
The recipient address picks the outcome (any other address simulates a normal delivery):
|
|
74
|
+
|
|
75
|
+
| Recipient | Outcome |
|
|
76
|
+
|---|---|
|
|
77
|
+
| `delivered@simulator.tratto.email` | `delivered` event |
|
|
78
|
+
| `bounced@simulator.tratto.email` | permanent bounce → email ends `failed` |
|
|
79
|
+
| `soft-bounced@simulator.tratto.email` | transient bounce |
|
|
80
|
+
| `complained@simulator.tratto.email` | spam complaint event |
|
|
81
|
+
|
|
82
|
+
### What to expect
|
|
83
|
+
|
|
84
|
+
- Responses and webhook payloads carry `livemode: false` so your integration can tell test traffic apart.
|
|
85
|
+
- A test key only ever sees test data: listing or fetching emails created with a live key returns 404/empty, and vice versa.
|
|
86
|
+
- Test emails are retained for 7 days.
|
|
87
|
+
- Endpoints that reach real recipients (campaign send, template test-send, flow activation) reject test keys with `403 TEST_MODE_NOT_SUPPORTED`.
|
|
88
|
+
|
|
89
|
+
Switching to production is a one-line change: swap the key for a `tratto_live_…` one (verified sender domain required).
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
54
93
|
## API Reference
|
|
55
94
|
|
|
56
95
|
All methods return `Promise<T>`. Use `async/await` or `.then()`.
|
|
@@ -276,26 +315,6 @@ const { deletedAt } = await tratto.domains.delete(domain.id);
|
|
|
276
315
|
|
|
277
316
|
---
|
|
278
317
|
|
|
279
|
-
### API Keys
|
|
280
|
-
|
|
281
|
-
```ts
|
|
282
|
-
// Create — raw key is shown only once
|
|
283
|
-
const key = await tratto.apiKeys.create({
|
|
284
|
-
name: 'CI deployment key',
|
|
285
|
-
env: 'live',
|
|
286
|
-
permissions: ['emails:send'],
|
|
287
|
-
});
|
|
288
|
-
console.log('Raw key (save this!):', key.key);
|
|
289
|
-
|
|
290
|
-
// List (prefix only, never raw token)
|
|
291
|
-
const { data } = await tratto.apiKeys.list();
|
|
292
|
-
|
|
293
|
-
// Revoke
|
|
294
|
-
const { revokedAt } = await tratto.apiKeys.revoke(key.id);
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
---
|
|
298
|
-
|
|
299
318
|
### Analytics
|
|
300
319
|
|
|
301
320
|
```ts
|
|
@@ -422,6 +441,9 @@ See the [`examples/`](examples/) folder:
|
|
|
422
441
|
| [`campaign.ts`](examples/campaign.ts) | Create, configure, and send a marketing campaign |
|
|
423
442
|
| [`analytics.ts`](examples/analytics.ts) | Fetch delivery metrics and daily timeseries |
|
|
424
443
|
| [`webhook.ts`](examples/webhook.ts) | Register a webhook and inspect delivery history |
|
|
444
|
+
| [`nextjs.ts`](examples/nextjs.ts) | Next.js App Router route handler sending a welcome email |
|
|
445
|
+
| [`express.ts`](examples/express.ts) | Express route sending a password-reset email |
|
|
446
|
+
| [`fastify.ts`](examples/fastify.ts) | Fastify route sending an order-confirmation email via template |
|
|
425
447
|
|
|
426
448
|
Run any example with [tsx](https://github.com/privatenumber/tsx):
|
|
427
449
|
|
package/dist/index.d.mts
CHANGED
|
@@ -285,29 +285,6 @@ interface ListDomainsParams {
|
|
|
285
285
|
after?: string;
|
|
286
286
|
limit?: number;
|
|
287
287
|
}
|
|
288
|
-
type ApiKeyEnv = 'live' | 'test';
|
|
289
|
-
interface ApiKey {
|
|
290
|
-
id: string;
|
|
291
|
-
name: string;
|
|
292
|
-
prefix: string;
|
|
293
|
-
env: ApiKeyEnv;
|
|
294
|
-
permissions: string[];
|
|
295
|
-
createdAt: string;
|
|
296
|
-
lastUsedAt: string | null;
|
|
297
|
-
revokedAt: string | null;
|
|
298
|
-
}
|
|
299
|
-
interface ApiKeyCreated extends ApiKey {
|
|
300
|
-
key: string;
|
|
301
|
-
}
|
|
302
|
-
interface CreateApiKeyParams {
|
|
303
|
-
name: string;
|
|
304
|
-
env: ApiKeyEnv;
|
|
305
|
-
permissions: string[];
|
|
306
|
-
}
|
|
307
|
-
interface ListApiKeysParams {
|
|
308
|
-
after?: string;
|
|
309
|
-
limit?: number;
|
|
310
|
-
}
|
|
311
288
|
type AnalyticsPeriod = '7d' | '30d' | '90d';
|
|
312
289
|
interface AnalyticsSummary {
|
|
313
290
|
period: AnalyticsPeriod;
|
|
@@ -503,15 +480,6 @@ declare class DomainsResource extends BaseResource {
|
|
|
503
480
|
}>;
|
|
504
481
|
}
|
|
505
482
|
|
|
506
|
-
declare class ApiKeysResource extends BaseResource {
|
|
507
|
-
create(params: CreateApiKeyParams, idempotencyKey?: string): Promise<ApiKeyCreated>;
|
|
508
|
-
list(params?: ListApiKeysParams): Promise<PaginatedResponse<ApiKey>>;
|
|
509
|
-
revoke(id: string): Promise<{
|
|
510
|
-
id: string;
|
|
511
|
-
revokedAt: string;
|
|
512
|
-
}>;
|
|
513
|
-
}
|
|
514
|
-
|
|
515
483
|
declare class AnalyticsResource extends BaseResource {
|
|
516
484
|
getSummary(period?: AnalyticsPeriod): Promise<AnalyticsSummary>;
|
|
517
485
|
getTimeseries(period?: AnalyticsPeriod): Promise<TimeseriesPoint[]>;
|
|
@@ -552,11 +520,10 @@ declare class Tratto {
|
|
|
552
520
|
readonly templates: TemplatesResource;
|
|
553
521
|
readonly webhooks: WebhooksResource;
|
|
554
522
|
readonly domains: DomainsResource;
|
|
555
|
-
readonly apiKeys: ApiKeysResource;
|
|
556
523
|
readonly analytics: AnalyticsResource;
|
|
557
524
|
readonly flows: FlowsResource;
|
|
558
525
|
readonly workspace: WorkspaceResource;
|
|
559
526
|
constructor(apiKey: string, options?: TrattoOptions);
|
|
560
527
|
}
|
|
561
528
|
|
|
562
|
-
export { type AddContactsToAudienceResult, type AnalyticsPeriod, type AnalyticsSummary, type
|
|
529
|
+
export { type AddContactsToAudienceResult, type AnalyticsPeriod, type AnalyticsSummary, type Audience, type AudienceRule, type AudienceRuleOperator, type Campaign, type CampaignStats, type CampaignStatsDetail, type CampaignStatus, type Contact, type ContactStatus, type CreateAudienceParams, type CreateCampaignParams, type CreateContactParams, type CreateFlowParams, type CreateTemplateParams, type CreateWebhookParams, type Domain, type DomainRecord, type DomainStatus, type DomainSummary, type EmailDetail, type EmailEvent, type EmailSummary, type Flow, type FlowStatus, type FlowStep, type FlowStepType, type FlowTrigger, type FlowTriggerType, type ImportJobStatus, type InviteMemberParams, type ListAudiencesParams, type ListCampaignsParams, type ListContactsParams, type ListDomainsParams, type ListEmailsParams, type ListFlowsParams, type ListTemplatesParams, type ListWebhookDeliveriesParams, type PaginatedResponse, type Pagination, type SendCampaignParams, type SendEmailParams, type Template, type TemplateStatus, type TemplateSummary, type TemplateVersion, type TemplateVersionSummary, type TimeseriesPoint, Tratto, TrattoError, type TrattoOptions, type UpdateContactParams, type UpdateFlowParams, type UpdateMemberParams, type UpdateTemplateParams, type UpdateWorkspaceParams, type UpdateWorkspacePreferencesParams, type Webhook, type WebhookDelivery, type WebhookDeliveryStatus, type WebhookEventType, type WebhookStatus, type Workspace, type WorkspaceLocale, type WorkspaceMember, type WorkspaceMemberRole, type WorkspacePlan, type WorkspacePreferences };
|
package/dist/index.d.ts
CHANGED
|
@@ -285,29 +285,6 @@ interface ListDomainsParams {
|
|
|
285
285
|
after?: string;
|
|
286
286
|
limit?: number;
|
|
287
287
|
}
|
|
288
|
-
type ApiKeyEnv = 'live' | 'test';
|
|
289
|
-
interface ApiKey {
|
|
290
|
-
id: string;
|
|
291
|
-
name: string;
|
|
292
|
-
prefix: string;
|
|
293
|
-
env: ApiKeyEnv;
|
|
294
|
-
permissions: string[];
|
|
295
|
-
createdAt: string;
|
|
296
|
-
lastUsedAt: string | null;
|
|
297
|
-
revokedAt: string | null;
|
|
298
|
-
}
|
|
299
|
-
interface ApiKeyCreated extends ApiKey {
|
|
300
|
-
key: string;
|
|
301
|
-
}
|
|
302
|
-
interface CreateApiKeyParams {
|
|
303
|
-
name: string;
|
|
304
|
-
env: ApiKeyEnv;
|
|
305
|
-
permissions: string[];
|
|
306
|
-
}
|
|
307
|
-
interface ListApiKeysParams {
|
|
308
|
-
after?: string;
|
|
309
|
-
limit?: number;
|
|
310
|
-
}
|
|
311
288
|
type AnalyticsPeriod = '7d' | '30d' | '90d';
|
|
312
289
|
interface AnalyticsSummary {
|
|
313
290
|
period: AnalyticsPeriod;
|
|
@@ -503,15 +480,6 @@ declare class DomainsResource extends BaseResource {
|
|
|
503
480
|
}>;
|
|
504
481
|
}
|
|
505
482
|
|
|
506
|
-
declare class ApiKeysResource extends BaseResource {
|
|
507
|
-
create(params: CreateApiKeyParams, idempotencyKey?: string): Promise<ApiKeyCreated>;
|
|
508
|
-
list(params?: ListApiKeysParams): Promise<PaginatedResponse<ApiKey>>;
|
|
509
|
-
revoke(id: string): Promise<{
|
|
510
|
-
id: string;
|
|
511
|
-
revokedAt: string;
|
|
512
|
-
}>;
|
|
513
|
-
}
|
|
514
|
-
|
|
515
483
|
declare class AnalyticsResource extends BaseResource {
|
|
516
484
|
getSummary(period?: AnalyticsPeriod): Promise<AnalyticsSummary>;
|
|
517
485
|
getTimeseries(period?: AnalyticsPeriod): Promise<TimeseriesPoint[]>;
|
|
@@ -552,11 +520,10 @@ declare class Tratto {
|
|
|
552
520
|
readonly templates: TemplatesResource;
|
|
553
521
|
readonly webhooks: WebhooksResource;
|
|
554
522
|
readonly domains: DomainsResource;
|
|
555
|
-
readonly apiKeys: ApiKeysResource;
|
|
556
523
|
readonly analytics: AnalyticsResource;
|
|
557
524
|
readonly flows: FlowsResource;
|
|
558
525
|
readonly workspace: WorkspaceResource;
|
|
559
526
|
constructor(apiKey: string, options?: TrattoOptions);
|
|
560
527
|
}
|
|
561
528
|
|
|
562
|
-
export { type AddContactsToAudienceResult, type AnalyticsPeriod, type AnalyticsSummary, type
|
|
529
|
+
export { type AddContactsToAudienceResult, type AnalyticsPeriod, type AnalyticsSummary, type Audience, type AudienceRule, type AudienceRuleOperator, type Campaign, type CampaignStats, type CampaignStatsDetail, type CampaignStatus, type Contact, type ContactStatus, type CreateAudienceParams, type CreateCampaignParams, type CreateContactParams, type CreateFlowParams, type CreateTemplateParams, type CreateWebhookParams, type Domain, type DomainRecord, type DomainStatus, type DomainSummary, type EmailDetail, type EmailEvent, type EmailSummary, type Flow, type FlowStatus, type FlowStep, type FlowStepType, type FlowTrigger, type FlowTriggerType, type ImportJobStatus, type InviteMemberParams, type ListAudiencesParams, type ListCampaignsParams, type ListContactsParams, type ListDomainsParams, type ListEmailsParams, type ListFlowsParams, type ListTemplatesParams, type ListWebhookDeliveriesParams, type PaginatedResponse, type Pagination, type SendCampaignParams, type SendEmailParams, type Template, type TemplateStatus, type TemplateSummary, type TemplateVersion, type TemplateVersionSummary, type TimeseriesPoint, Tratto, TrattoError, type TrattoOptions, type UpdateContactParams, type UpdateFlowParams, type UpdateMemberParams, type UpdateTemplateParams, type UpdateWorkspaceParams, type UpdateWorkspacePreferencesParams, type Webhook, type WebhookDelivery, type WebhookDeliveryStatus, type WebhookEventType, type WebhookStatus, type Workspace, type WorkspaceLocale, type WorkspaceMember, type WorkspaceMemberRole, type WorkspacePlan, type WorkspacePreferences };
|
package/dist/index.js
CHANGED
|
@@ -288,23 +288,6 @@ var DomainsResource = class extends BaseResource {
|
|
|
288
288
|
}
|
|
289
289
|
};
|
|
290
290
|
|
|
291
|
-
// src/resources/api-keys.ts
|
|
292
|
-
var ApiKeysResource = class extends BaseResource {
|
|
293
|
-
create(params, idempotencyKey) {
|
|
294
|
-
return this.fetchData("POST", "/v1/api-keys", {
|
|
295
|
-
body: params,
|
|
296
|
-
headers: idempotencyKey ? { "Idempotency-Key": idempotencyKey } : void 0
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
list(params) {
|
|
300
|
-
const qs = this.buildQuery({ after: params?.after, limit: params?.limit });
|
|
301
|
-
return this.fetch("GET", `/v1/api-keys${qs}`);
|
|
302
|
-
}
|
|
303
|
-
revoke(id) {
|
|
304
|
-
return this.fetchData("DELETE", `/v1/api-keys/${id}`);
|
|
305
|
-
}
|
|
306
|
-
};
|
|
307
|
-
|
|
308
291
|
// src/resources/analytics.ts
|
|
309
292
|
var AnalyticsResource = class extends BaseResource {
|
|
310
293
|
getSummary(period = "30d") {
|
|
@@ -390,7 +373,6 @@ var Tratto = class {
|
|
|
390
373
|
this.templates = new TemplatesResource(apiKey, baseUrl);
|
|
391
374
|
this.webhooks = new WebhooksResource(apiKey, baseUrl);
|
|
392
375
|
this.domains = new DomainsResource(apiKey, baseUrl);
|
|
393
|
-
this.apiKeys = new ApiKeysResource(apiKey, baseUrl);
|
|
394
376
|
this.analytics = new AnalyticsResource(apiKey, baseUrl);
|
|
395
377
|
this.flows = new FlowsResource(apiKey, baseUrl);
|
|
396
378
|
this.workspace = new WorkspaceResource(apiKey, baseUrl);
|
package/dist/index.mjs
CHANGED
|
@@ -261,23 +261,6 @@ var DomainsResource = class extends BaseResource {
|
|
|
261
261
|
}
|
|
262
262
|
};
|
|
263
263
|
|
|
264
|
-
// src/resources/api-keys.ts
|
|
265
|
-
var ApiKeysResource = class extends BaseResource {
|
|
266
|
-
create(params, idempotencyKey) {
|
|
267
|
-
return this.fetchData("POST", "/v1/api-keys", {
|
|
268
|
-
body: params,
|
|
269
|
-
headers: idempotencyKey ? { "Idempotency-Key": idempotencyKey } : void 0
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
list(params) {
|
|
273
|
-
const qs = this.buildQuery({ after: params?.after, limit: params?.limit });
|
|
274
|
-
return this.fetch("GET", `/v1/api-keys${qs}`);
|
|
275
|
-
}
|
|
276
|
-
revoke(id) {
|
|
277
|
-
return this.fetchData("DELETE", `/v1/api-keys/${id}`);
|
|
278
|
-
}
|
|
279
|
-
};
|
|
280
|
-
|
|
281
264
|
// src/resources/analytics.ts
|
|
282
265
|
var AnalyticsResource = class extends BaseResource {
|
|
283
266
|
getSummary(period = "30d") {
|
|
@@ -363,7 +346,6 @@ var Tratto = class {
|
|
|
363
346
|
this.templates = new TemplatesResource(apiKey, baseUrl);
|
|
364
347
|
this.webhooks = new WebhooksResource(apiKey, baseUrl);
|
|
365
348
|
this.domains = new DomainsResource(apiKey, baseUrl);
|
|
366
|
-
this.apiKeys = new ApiKeysResource(apiKey, baseUrl);
|
|
367
349
|
this.analytics = new AnalyticsResource(apiKey, baseUrl);
|
|
368
350
|
this.flows = new FlowsResource(apiKey, baseUrl);
|
|
369
351
|
this.workspace = new WorkspaceResource(apiKey, baseUrl);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tratto/email",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Tratto Node.js SDK
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Tratto Node.js SDK \u2014 send transactional and marketing email",
|
|
5
5
|
"author": "Tratto <hello@tratto.email>",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://tratto.email",
|
|
@@ -22,7 +22,9 @@
|
|
|
22
22
|
"require": "./dist/index.js"
|
|
23
23
|
}
|
|
24
24
|
},
|
|
25
|
-
"files": [
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
26
28
|
"sideEffects": false,
|
|
27
29
|
"scripts": {
|
|
28
30
|
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
@@ -30,12 +32,17 @@
|
|
|
30
32
|
"lint": "eslint src --ext .ts",
|
|
31
33
|
"typecheck": "tsc --noEmit",
|
|
32
34
|
"test": "vitest run",
|
|
33
|
-
"test:watch": "vitest"
|
|
35
|
+
"test:watch": "vitest",
|
|
36
|
+
"test:coverage": "vitest run --coverage"
|
|
34
37
|
},
|
|
35
38
|
"devDependencies": {
|
|
39
|
+
"@types/express": "^5.0.0",
|
|
36
40
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
37
41
|
"@typescript-eslint/parser": "^8.0.0",
|
|
42
|
+
"@vitest/coverage-v8": "^2.0.0",
|
|
38
43
|
"eslint": "^9.0.0",
|
|
44
|
+
"express": "^5.0.0",
|
|
45
|
+
"fastify": "^5.0.0",
|
|
39
46
|
"tsup": "^8.0.0",
|
|
40
47
|
"typescript": "^5.5.0",
|
|
41
48
|
"vitest": "^2.0.0"
|