migma 1.3.3 → 1.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.
- package/README.md +196 -5
- package/dist/index.d.mts +300 -23
- package/dist/index.d.ts +300 -23
- package/dist/index.js +102 -32
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +102 -32
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -97,7 +97,7 @@ await migma.sending.send({
|
|
|
97
97
|
|
|
98
98
|
## Features
|
|
99
99
|
|
|
100
|
-
- Full coverage of all Migma API v1 endpoints (80+ methods across
|
|
100
|
+
- Full coverage of all Migma API v1 endpoints (80+ methods across 16 resources)
|
|
101
101
|
- TypeScript-first with complete type definitions
|
|
102
102
|
- `{ data, error }` return pattern — methods never throw
|
|
103
103
|
- Automatic retries with exponential backoff on 5xx/429
|
|
@@ -126,6 +126,7 @@ Every method returns `{ data, error }` instead of throwing. TypeScript narrows t
|
|
|
126
126
|
```typescript
|
|
127
127
|
const { data, error } = await migma.contacts.create({
|
|
128
128
|
email: 'john@example.com',
|
|
129
|
+
status: 'subscribed', // only for contacts with marketing consent
|
|
129
130
|
projectId: 'proj_abc123',
|
|
130
131
|
});
|
|
131
132
|
|
|
@@ -206,7 +207,7 @@ const { data: contact } = await migma.contacts.create({
|
|
|
206
207
|
// List with filters
|
|
207
208
|
const { data: contacts } = await migma.contacts.list({
|
|
208
209
|
projectId: 'proj_abc123',
|
|
209
|
-
status: '
|
|
210
|
+
status: 'subscribed',
|
|
210
211
|
tags: 'newsletter',
|
|
211
212
|
limit: 50,
|
|
212
213
|
});
|
|
@@ -219,6 +220,40 @@ const { data: result } = await migma.contacts.bulkImport({
|
|
|
219
220
|
],
|
|
220
221
|
projectId: 'proj_abc123',
|
|
221
222
|
});
|
|
223
|
+
|
|
224
|
+
// Batch delete by email (up to 1000; unmatched emails come back in notFound)
|
|
225
|
+
const { data: deletion } = await migma.contacts.bulkDeleteByEmail({
|
|
226
|
+
emails: ['a@example.com', 'b@example.com'],
|
|
227
|
+
projectId: 'proj_abc123',
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// Import contacts from a CSV file (multipart upload, processed in the background).
|
|
231
|
+
// `file` accepts a Blob/File, a Buffer, or a ReadableStream.
|
|
232
|
+
import { readFileSync } from 'node:fs';
|
|
233
|
+
|
|
234
|
+
const { data: job } = await migma.contacts.imports.create({
|
|
235
|
+
file: readFileSync('./contacts.csv'),
|
|
236
|
+
projectId: 'proj_abc123',
|
|
237
|
+
columnMap: {
|
|
238
|
+
email: 'Email',
|
|
239
|
+
firstName: 'First Name',
|
|
240
|
+
customFields: { plan: 'Plan' },
|
|
241
|
+
},
|
|
242
|
+
onConflict: 'upsert',
|
|
243
|
+
tags: ['newsletter'],
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// Poll for status + counts until the import finishes.
|
|
247
|
+
const { data: status } = await migma.contacts.imports.get(job!.id);
|
|
248
|
+
// status.status → 'pending' | 'processing' | 'completed' | 'failed'
|
|
249
|
+
// status.counts → { total, created, updated, skipped, failed }
|
|
250
|
+
|
|
251
|
+
// Safe retries: pass an Idempotency-Key so a retried write never duplicates.
|
|
252
|
+
// Same key + same body within 24h replays the original response.
|
|
253
|
+
await migma.contacts.create(
|
|
254
|
+
{ email: 'jane@example.com', projectId: 'proj_abc123' },
|
|
255
|
+
{ idempotencyKey: 'signup-jane@example.com-1779292800' },
|
|
256
|
+
);
|
|
222
257
|
```
|
|
223
258
|
|
|
224
259
|
[Contacts API Reference](https://docs.migma.ai/api-reference/contacts/get-contact)
|
|
@@ -273,6 +308,23 @@ await migma.sending.send({
|
|
|
273
308
|
|
|
274
309
|
[Sending API Reference](https://docs.migma.ai/api-reference/sending/send-email)
|
|
275
310
|
|
|
311
|
+
### Sending Metrics
|
|
312
|
+
|
|
313
|
+
Read account sending totals, delivery outcomes, and remaining daily and monthly capacity.
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
const { data: metrics } = await migma.metrics.sending({ months: 6 });
|
|
317
|
+
|
|
318
|
+
console.log(metrics?.period, metrics?.monthly.sent, metrics?.remainingMonth);
|
|
319
|
+
console.log(metrics?.today.sent, metrics?.today.remainingToday);
|
|
320
|
+
|
|
321
|
+
for (const month of metrics?.history ?? []) {
|
|
322
|
+
console.log(month.period, month.sent, month.delivered, month.bounced, month.complained);
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
Omit `months` when you only need the current month and today's capacity.
|
|
327
|
+
|
|
276
328
|
### Generated Series and Editing
|
|
277
329
|
|
|
278
330
|
Generate a series with `count`, then use each email's `emailId` to fetch, prompt-edit, or send one email.
|
|
@@ -297,6 +349,123 @@ await migma.emails.edit('email_abc123', {
|
|
|
297
349
|
});
|
|
298
350
|
```
|
|
299
351
|
|
|
352
|
+
### Campaigns
|
|
353
|
+
|
|
354
|
+
Create a campaign from a generated email, send or schedule it, then read its
|
|
355
|
+
engagement stats and per-recipient logs.
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
// Create from a generated email
|
|
359
|
+
const { data: campaign } = await migma.campaigns.create({
|
|
360
|
+
projectId: 'proj_abc123',
|
|
361
|
+
name: 'Spring Launch',
|
|
362
|
+
conversationId: 'conv_abc123',
|
|
363
|
+
emailId: 'email_abc123',
|
|
364
|
+
subject: 'Spring is here',
|
|
365
|
+
from: 'hello@yourdomain.com',
|
|
366
|
+
fromName: 'Your Brand',
|
|
367
|
+
recipientType: 'audience',
|
|
368
|
+
recipientId: 'aud_abc123',
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
// Send now (idempotency key recommended — see below)
|
|
372
|
+
await migma.campaigns.send(campaign.id, {
|
|
373
|
+
idempotencyKey: `campaign-send-${campaign.id}`,
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
// Or schedule for later
|
|
377
|
+
await migma.campaigns.schedule(
|
|
378
|
+
campaign.id,
|
|
379
|
+
{ scheduledAt: '2026-07-01T15:00:00Z', scheduledTimezone: 'America/New_York' },
|
|
380
|
+
{ idempotencyKey: `campaign-send-${campaign.id}` },
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
// Aggregated engagement metrics (cached, may be slightly stale)
|
|
384
|
+
const { data: stats } = await migma.campaigns.stats(campaign.id);
|
|
385
|
+
console.log(stats?.openRate, stats?.clickRate, stats?.lastUpdated);
|
|
386
|
+
|
|
387
|
+
// Per-recipient logs, cursor-paginated and optionally filtered by status
|
|
388
|
+
const { data: logs } = await migma.campaigns.logs(campaign.id, {
|
|
389
|
+
status: 'opened',
|
|
390
|
+
limit: 50,
|
|
391
|
+
});
|
|
392
|
+
for (const row of logs?.emails ?? []) {
|
|
393
|
+
console.log(row.to_email, row.status, row.opened_at);
|
|
394
|
+
}
|
|
395
|
+
if (logs?.hasMore) {
|
|
396
|
+
await migma.campaigns.logs(campaign.id, { cursor: logs.nextCursor ?? undefined });
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
Campaign stats are sourced from the email tracking worker and cached on the
|
|
401
|
+
campaign, so they can lag live activity. `botOpens`, `botClicks`, and `mppOpens`
|
|
402
|
+
reflect Apple Mail Privacy Protection and bot detection when that data is
|
|
403
|
+
available. Log rows come straight from the tracking store, so their field names
|
|
404
|
+
are `snake_case` (`to_email`, `opened_at`, `bounce_type`) unlike the rest of the
|
|
405
|
+
API.
|
|
406
|
+
|
|
407
|
+
[Campaigns Guide](https://docs.migma.ai/campaigns/overview)
|
|
408
|
+
|
|
409
|
+
### Idempotency
|
|
410
|
+
|
|
411
|
+
Write methods accept an optional trailing `CallOptions` argument carrying an
|
|
412
|
+
`idempotencyKey`. The key is sent as the `Idempotency-Key` header (max 100
|
|
413
|
+
characters) and is scoped to your API key for 24 hours:
|
|
414
|
+
|
|
415
|
+
- **Same key + same body** within 24h replays the original response (the server
|
|
416
|
+
marks the replay with an `Idempotent-Replayed: true` header).
|
|
417
|
+
- **Same key + different body** returns `409 IDEMPOTENCY_CONFLICT`.
|
|
418
|
+
- `429` and `5xx` responses are never cached, so they stay safe to retry — and
|
|
419
|
+
the SDK reuses your key across its own automatic retries, so a retried call
|
|
420
|
+
never duplicates.
|
|
421
|
+
|
|
422
|
+
```typescript
|
|
423
|
+
// Sending — key on the recipient + send time
|
|
424
|
+
await migma.sending.send(
|
|
425
|
+
{
|
|
426
|
+
recipientType: 'email',
|
|
427
|
+
recipientEmail: 'user@example.com',
|
|
428
|
+
from: 'hello@yourdomain.com',
|
|
429
|
+
fromName: 'Your Brand',
|
|
430
|
+
subject: 'Welcome!',
|
|
431
|
+
emailId: 'email_abc123',
|
|
432
|
+
},
|
|
433
|
+
{ idempotencyKey: 'send-email_abc123-user@example.com-now' },
|
|
434
|
+
);
|
|
435
|
+
|
|
436
|
+
// Campaign send — one key per campaign send
|
|
437
|
+
await migma.campaigns.send('camp_abc123', {
|
|
438
|
+
idempotencyKey: 'campaign-send-camp_abc123',
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
// Email generation — key on project + the turn/prompt
|
|
442
|
+
await migma.emails.generate(
|
|
443
|
+
{ projectId: 'proj_abc123', prompt: 'Welcome email' },
|
|
444
|
+
{ idempotencyKey: 'email-gen-proj_abc123-welcome' },
|
|
445
|
+
);
|
|
446
|
+
|
|
447
|
+
// Contact add — key on the email
|
|
448
|
+
await migma.contacts.create(
|
|
449
|
+
{ email: 'jane@example.com', projectId: 'proj_abc123' },
|
|
450
|
+
{ idempotencyKey: 'contact-jane@example.com' },
|
|
451
|
+
);
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
**Choose a deterministic key** so the same logical operation produces the same
|
|
455
|
+
key on every attempt:
|
|
456
|
+
|
|
457
|
+
- send: `send-<emailId>-<recipientId|to>-<scheduledAt|"now">`
|
|
458
|
+
- campaign send: `campaign-send-<campaignId>`
|
|
459
|
+
- email generate: `email-gen-<projectId>-<turnOrPromptHash>`
|
|
460
|
+
- contact add: `contact-<email>`
|
|
461
|
+
|
|
462
|
+
**Anti-patterns to avoid:**
|
|
463
|
+
|
|
464
|
+
- Don't generate the key with `uuid()` or `Date.now()` per call — a fresh key
|
|
465
|
+
every attempt defeats deduplication across retries and process restarts.
|
|
466
|
+
- Don't hash the request body to build the key — identical retries you *want*
|
|
467
|
+
deduplicated would each get a different key, and you lose the conflict signal.
|
|
468
|
+
|
|
300
469
|
### Email Validation
|
|
301
470
|
|
|
302
471
|
Run preflight checks — compatibility, links, spelling, and deliverability.
|
|
@@ -374,6 +543,14 @@ const { data: avail } = await migma.domains.checkAvailability('mycompany');
|
|
|
374
543
|
if (avail.available) {
|
|
375
544
|
await migma.domains.createManaged({ prefix: 'mycompany' });
|
|
376
545
|
}
|
|
546
|
+
|
|
547
|
+
// Provision a transactional stream (notify.example.com) to isolate
|
|
548
|
+
// transactional reputation from marketing, then publish the returned DNS records
|
|
549
|
+
await migma.domains.provisionStream({ rootDomain: 'example.com', stream: 'transactional' });
|
|
550
|
+
|
|
551
|
+
// Or provision both streams (send. marketing + notify. transactional) in one call
|
|
552
|
+
const { data } = await migma.domains.setup({ rootDomain: 'example.com' });
|
|
553
|
+
// data.marketing, data.transactional
|
|
377
554
|
```
|
|
378
555
|
|
|
379
556
|
[Domains API Reference](https://docs.migma.ai/api-reference/domains/list-domains)
|
|
@@ -391,7 +568,19 @@ const { data: tag } = await migma.tags.create({
|
|
|
391
568
|
// Segments
|
|
392
569
|
const { data: segment } = await migma.segments.create({
|
|
393
570
|
name: 'Active US Customers',
|
|
394
|
-
filters: {
|
|
571
|
+
filters: {
|
|
572
|
+
status: 'subscribed',
|
|
573
|
+
fields: [{ key: 'country', mode: 'is', values: ['US'] }],
|
|
574
|
+
activity: [{ action: 'opened', channel: 'email', mode: 'within', unit: 'days', amount: 30 }],
|
|
575
|
+
},
|
|
576
|
+
projectId: 'proj_abc123',
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
const { data: campaignSegment } = await migma.segments.create({
|
|
580
|
+
name: 'Campaign openers',
|
|
581
|
+
filters: {
|
|
582
|
+
activity: [{ action: 'opened', channel: 'email', mode: 'within', unit: 'days', amount: 14, campaignId: 'cmp_123' }],
|
|
583
|
+
},
|
|
395
584
|
projectId: 'proj_abc123',
|
|
396
585
|
});
|
|
397
586
|
|
|
@@ -450,17 +639,19 @@ await migma.images.updateLogos('proj_abc123', {
|
|
|
450
639
|
| Resource | Methods | Docs |
|
|
451
640
|
|----------|---------|------|
|
|
452
641
|
| `migma.contacts` | `create` `list` `get` `update` `remove` `bulkImport` `getBulkImportStatus` `changeStatus` | [Contacts](https://docs.migma.ai/api-reference/contacts/get-contact) |
|
|
642
|
+
| `migma.contacts.imports` | `create` `get` | [Contact imports](https://docs.migma.ai/api-reference/contacts/get-contact) |
|
|
453
643
|
| `migma.tags` | `create` `list` `get` `update` `remove` | [Tags](https://docs.migma.ai/api-reference/tags/list-tags) |
|
|
454
644
|
| `migma.segments` | `create` `list` `get` `update` `remove` | [Segments](https://docs.migma.ai/api-reference/audiences/list-audiences) |
|
|
455
645
|
| `migma.topics` | `create` `list` `get` `update` `remove` `subscribe` `unsubscribe` | [Topics](https://docs.migma.ai/api-reference/topics/list-topics) |
|
|
456
646
|
| `migma.sending` | `send` `getBatchStatus` | [Sending](https://docs.migma.ai/api-reference/sending/send-email) |
|
|
647
|
+
| `migma.metrics` | `sending` | [API Reference](https://docs.migma.ai/api-reference/introduction) |
|
|
457
648
|
| `migma.projects` | `list` `get` `import` `getImportStatus` `retryImport` `fieldCatalog` `importAndWait` | [Projects](https://docs.migma.ai/api-reference/projects/list-projects) |
|
|
458
649
|
| `migma.emails` | `generate` `getGenerationStatus` `generateAndWait` `sendTest` `get` `edit` | [Email Generation](https://docs.migma.ai/api-reference/email/generate-email-async) |
|
|
459
|
-
| `migma.campaigns` | `list` `create` `get` `send` `schedule` `cancel` `archive` `unarchive` | [Campaigns](https://docs.migma.ai/campaigns/overview) |
|
|
650
|
+
| `migma.campaigns` | `list` `create` `get` `send` `schedule` `cancel` `stats` `logs` `archive` `unarchive` | [Campaigns](https://docs.migma.ai/campaigns/overview) |
|
|
460
651
|
| `migma.validation` | `all` `compatibility` `links` `spelling` `deliverability` | [Validation](https://docs.migma.ai/api-reference/email-validation/run-all-validation-checks) |
|
|
461
652
|
| `migma.previews` | `create` `get` `getStatus` `getDevice` `getSupportedDevices` `createAndWait` | [Previews](https://docs.migma.ai/api-reference/email-previews/create-email-preview) |
|
|
462
653
|
| `migma.export` | `getFormats` `getStatus` `html` `mjml` `pdf` `klaviyo` `mailchimp` `hubspot` | [Export](https://docs.migma.ai/api-reference/export/list-export-formats) |
|
|
463
|
-
| `migma.domains` | `create` `list` `get` `verify` `update` `remove` `checkAvailability` `listManaged` `createManaged` `removeManaged` | [Domains](https://docs.migma.ai/api-reference/domains/list-domains) |
|
|
654
|
+
| `migma.domains` | `create` `list` `get` `verify` `update` `remove` `checkAvailability` `listManaged` `createManaged` `removeManaged` `provisionStream` `setup` | [Domains](https://docs.migma.ai/api-reference/domains/list-domains) |
|
|
464
655
|
| `migma.webhooks` | `create` `list` `get` `update` `remove` `test` `getDeliveries` `getEvents` `getStats` | [Webhooks](https://docs.migma.ai/webhooks) |
|
|
465
656
|
| `migma.knowledgeBase` | `list` `add` `update` `remove` | [API Ref](https://docs.migma.ai/api-reference/introduction) |
|
|
466
657
|
| `migma.images` | `add` `update` `remove` `updateLogos` | [API Ref](https://docs.migma.ai/api-reference/introduction) |
|