@itm-studio/partner-sdk 0.2.3 → 0.2.4
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 +120 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -369,7 +369,7 @@ Use OTP subscribe when you want the user to confirm the phone number before the
|
|
|
369
369
|
- `listNames`: optional brand list names. Missing lists are created automatically and the subscribed user is attached to them.
|
|
370
370
|
- `compliance`: optional metadata about how consent was collected. This is the right place for fields like `pageUrl`, `submissionMethod`, `submittedAt`, `timezone`, and geo/browser/device details.
|
|
371
371
|
- `compliance.ipAddress` and `compliance.userAgent`: optional. If you omit them and forward `x-forwarded-for` and `user-agent`, the backend backfills those values for you.
|
|
372
|
-
- `postSubscriptionMessage`: optional welcome message delivered automatically after the subscription becomes active. See [Post-Subscription Welcome Message](#post-subscription-welcome-message).
|
|
372
|
+
- `postSubscriptionMessage`: optional welcome message delivered automatically after the subscription becomes active. The SDK exposes this as `PostSubscriptionMessageInput` and accepts it on `CreatePartnerBrandSubscriptionInput`. See [Post-Subscription Welcome Message](#post-subscription-welcome-message).
|
|
373
373
|
|
|
374
374
|
### OTP Flow Step By Step
|
|
375
375
|
|
|
@@ -388,6 +388,37 @@ If the verification token is invalid or expired, verification fails and the subs
|
|
|
388
388
|
|
|
389
389
|
**Channels are independent.** Provide any combination of `sms`, `email`, and `push`. Channels you omit are skipped.
|
|
390
390
|
|
|
391
|
+
The field is part of the generated SDK input types:
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
import type {
|
|
395
|
+
CreatePartnerBrandSubscriptionInput,
|
|
396
|
+
PostSubscriptionMessageInput,
|
|
397
|
+
} from '@itm-studio/partner-sdk';
|
|
398
|
+
|
|
399
|
+
const welcomeMessage: PostSubscriptionMessageInput = {
|
|
400
|
+
sms: {
|
|
401
|
+
body: 'Welcome to Acme. Reply STOP to opt out.',
|
|
402
|
+
mediaUrls: ['https://cdn.example.com/welcome-banner.png'],
|
|
403
|
+
},
|
|
404
|
+
email: {
|
|
405
|
+
subject: 'Welcome to Acme',
|
|
406
|
+
body: 'Thanks for subscribing. Your first update is on the way.',
|
|
407
|
+
},
|
|
408
|
+
push: {
|
|
409
|
+
title: 'Welcome to Acme',
|
|
410
|
+
body: 'Tap to see what is dropping this week.',
|
|
411
|
+
},
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
const input: CreatePartnerBrandSubscriptionInput = {
|
|
415
|
+
phoneNumber: '+14155550111',
|
|
416
|
+
fullName: 'Jane Doe',
|
|
417
|
+
email: 'jane@example.com',
|
|
418
|
+
postSubscriptionMessage: welcomeMessage,
|
|
419
|
+
};
|
|
420
|
+
```
|
|
421
|
+
|
|
391
422
|
```typescript
|
|
392
423
|
await itm.mutation({
|
|
393
424
|
createPartnerBrandSubscription: {
|
|
@@ -418,6 +449,15 @@ await itm.mutation({
|
|
|
418
449
|
});
|
|
419
450
|
```
|
|
420
451
|
|
|
452
|
+
**Direct vs OTP timing:**
|
|
453
|
+
|
|
454
|
+
| Mode | Where to pass `postSubscriptionMessage` | When delivery starts |
|
|
455
|
+
|---|---|---|
|
|
456
|
+
| Direct subscribe | Initial `createPartnerBrandSubscription` call | After the subscription is created or reactivated |
|
|
457
|
+
| OTP subscribe | Initial `createPartnerBrandSubscription` call with `requireOtpVerification: true` | After `verifyPartnerBrandSubscription` succeeds |
|
|
458
|
+
|
|
459
|
+
Do not pass `postSubscriptionMessage` to `verifyPartnerBrandSubscription`; that mutation only accepts `verificationToken` and `code`.
|
|
460
|
+
|
|
421
461
|
**Channel inputs:**
|
|
422
462
|
|
|
423
463
|
| Channel | Required | Optional | Notes |
|
|
@@ -432,12 +472,76 @@ await itm.mutation({
|
|
|
432
472
|
- The message is delivered when the subscription is **newly created or reactivated** — re-subscribing an already-active user is a no-op.
|
|
433
473
|
- In OTP mode, the message fires **after** `verifyPartnerBrandSubscription` succeeds, not when the OTP is requested.
|
|
434
474
|
- Channel failures are isolated: if email cannot be sent because the user has no primary email, SMS and push are still attempted.
|
|
475
|
+
- Delivery is asynchronous. A successful mutation means the subscription request was accepted; it does not mean each SMS, email, or push provider has already completed delivery.
|
|
435
476
|
|
|
436
477
|
**Recommended usage:**
|
|
437
478
|
|
|
438
479
|
- Pass `postSubscriptionMessage` on the **first** `createPartnerBrandSubscription` call. In OTP mode, the message is buffered with the pending session and delivered after `verifyPartnerBrandSubscription`. You do **not** repeat it on the verify call.
|
|
439
480
|
- For a marketing-style first SMS, prefer including a clear opt-out instruction (e.g. `Reply STOP to opt out`) alongside any branded copy.
|
|
440
481
|
- For the email channel, your brand must have an email alias configured on the backend. Confirm this in backstage before relying on the email channel in production.
|
|
482
|
+
- Keep message content deterministic for a signup attempt. Because delivery is idempotent per active subscription period, retrying the same create call is safe; changing the message between retries should not be used as an update mechanism.
|
|
483
|
+
|
|
484
|
+
**Minimal SMS-only example:**
|
|
485
|
+
|
|
486
|
+
```typescript
|
|
487
|
+
await itm.mutation({
|
|
488
|
+
createPartnerBrandSubscription: {
|
|
489
|
+
__args: {
|
|
490
|
+
input: {
|
|
491
|
+
phoneNumber: '+14155550111',
|
|
492
|
+
postSubscriptionMessage: {
|
|
493
|
+
sms: {
|
|
494
|
+
body: 'You are subscribed to Acme updates. Reply STOP to opt out.',
|
|
495
|
+
},
|
|
496
|
+
},
|
|
497
|
+
},
|
|
498
|
+
},
|
|
499
|
+
verificationRequired: true,
|
|
500
|
+
subscription: { uid: true },
|
|
501
|
+
},
|
|
502
|
+
});
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
**OTP example with buffered welcome message:**
|
|
506
|
+
|
|
507
|
+
```typescript
|
|
508
|
+
const { createPartnerBrandSubscription } = await itm.mutation({
|
|
509
|
+
createPartnerBrandSubscription: {
|
|
510
|
+
__args: {
|
|
511
|
+
input: {
|
|
512
|
+
phoneNumber: '+14155550112',
|
|
513
|
+
requireOtpVerification: true,
|
|
514
|
+
email: 'jane@example.com',
|
|
515
|
+
postSubscriptionMessage: {
|
|
516
|
+
sms: {
|
|
517
|
+
body: 'Thanks for joining Acme. Reply STOP to opt out.',
|
|
518
|
+
},
|
|
519
|
+
email: {
|
|
520
|
+
subject: 'You are subscribed',
|
|
521
|
+
body: 'Your subscription is confirmed. We will send updates here.',
|
|
522
|
+
},
|
|
523
|
+
},
|
|
524
|
+
},
|
|
525
|
+
},
|
|
526
|
+
verificationRequired: true,
|
|
527
|
+
verificationToken: true,
|
|
528
|
+
},
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
// Later, after collecting the SMS code from the user:
|
|
532
|
+
await itm.mutation({
|
|
533
|
+
verifyPartnerBrandSubscription: {
|
|
534
|
+
__args: {
|
|
535
|
+
input: {
|
|
536
|
+
verificationToken: createPartnerBrandSubscription.verificationToken!,
|
|
537
|
+
code: '123456',
|
|
538
|
+
},
|
|
539
|
+
},
|
|
540
|
+
uid: true,
|
|
541
|
+
isActive: true,
|
|
542
|
+
},
|
|
543
|
+
});
|
|
544
|
+
```
|
|
441
545
|
|
|
442
546
|
### `createPartnerBrandSubscription`
|
|
443
547
|
|
|
@@ -446,6 +550,7 @@ Subscribe a phone number to the authenticated partner brand.
|
|
|
446
550
|
- Omit `requireOtpVerification` or set it to `false` to create the subscription immediately.
|
|
447
551
|
- Set `requireOtpVerification: true` to start the OTP flow. The backend sends the code to the provided phone number and returns a `verificationToken`.
|
|
448
552
|
- If you pass `fullName`, include both first and last name.
|
|
553
|
+
- Pass `postSubscriptionMessage` here if you want SMS, email, or push sent after activation. This field is accepted only on the create mutation, including OTP starts.
|
|
449
554
|
|
|
450
555
|
```typescript
|
|
451
556
|
const { createPartnerBrandSubscription } = await itm.mutation({
|
|
@@ -460,6 +565,11 @@ const { createPartnerBrandSubscription } = await itm.mutation({
|
|
|
460
565
|
pageUrl: 'https://example.com/signup',
|
|
461
566
|
submissionMethod: 'partner_sdk',
|
|
462
567
|
},
|
|
568
|
+
postSubscriptionMessage: {
|
|
569
|
+
sms: {
|
|
570
|
+
body: 'Welcome to Acme updates. Reply STOP to opt out.',
|
|
571
|
+
},
|
|
572
|
+
},
|
|
463
573
|
},
|
|
464
574
|
},
|
|
465
575
|
verificationRequired: true,
|
|
@@ -580,6 +690,15 @@ export async function startOtpSubscription(request: Request) {
|
|
|
580
690
|
submittedAt: new Date().toISOString(),
|
|
581
691
|
timezone: 'America/New_York',
|
|
582
692
|
},
|
|
693
|
+
postSubscriptionMessage: {
|
|
694
|
+
sms: {
|
|
695
|
+
body: 'You are subscribed. Reply STOP to opt out.',
|
|
696
|
+
},
|
|
697
|
+
email: {
|
|
698
|
+
subject: 'Welcome',
|
|
699
|
+
body: 'Your subscription is confirmed.',
|
|
700
|
+
},
|
|
701
|
+
},
|
|
583
702
|
},
|
|
584
703
|
},
|
|
585
704
|
verificationRequired: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@itm-studio/partner-sdk",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "TypeScript SDK for the ITM Partner API — typed GraphQL client with zero introspection required",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"build": "npm run generate && tsup",
|
|
22
22
|
"test": "vitest run",
|
|
23
23
|
"test:watch": "vitest",
|
|
24
|
-
"lint": "tsc --noEmit",
|
|
24
|
+
"lint": "tsc --noEmit -p tsconfig.json && tsc --noEmit -p tsconfig.test.json",
|
|
25
25
|
"prepublishOnly": "npm run build",
|
|
26
26
|
"schema:check": "hive schema:check ./schema/partner.graphql",
|
|
27
27
|
"schema:publish": "hive schema:publish ./schema/partner.graphql --author \"CI\" --commit \"$GITHUB_SHA\""
|