@itm-studio/partner-sdk 0.2.1 → 0.2.3

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 CHANGED
@@ -64,6 +64,31 @@ Get your partner API token from the ITM backstage dashboard: **Settings > Partne
64
64
 
65
65
  The token is sent as the `x-partner-api-key` header on every request. Your brand is determined automatically from the token — no need to pass brand IDs.
66
66
 
67
+ Keep the partner token on your server. Do not expose it from browser code or client-side mobile code.
68
+
69
+ For request-scoped mutations such as partner brand subscriptions, create the client inside the incoming request and forward `x-forwarded-for` and `user-agent` so the backend can enrich compliance metadata when those fields are omitted from `input.compliance`.
70
+
71
+ ```typescript
72
+ import { createITMPartnerClient } from '@itm-studio/partner-sdk';
73
+
74
+ export function createITMClientForRequest(request: Request) {
75
+ return createITMPartnerClient({
76
+ token: process.env.ITM_PARTNER_TOKEN!,
77
+ headers: () => {
78
+ const forwardedFor = request.headers.get('x-forwarded-for');
79
+ const userAgent = request.headers.get('user-agent');
80
+
81
+ return {
82
+ ...(forwardedFor ? { 'x-forwarded-for': forwardedFor } : {}),
83
+ ...(userAgent ? { 'user-agent': userAgent } : {}),
84
+ };
85
+ },
86
+ });
87
+ }
88
+ ```
89
+
90
+ If you also send `input.compliance`, the backend keeps the values you provide and backfills missing `ipAddress` and `userAgent` from the forwarded headers when available.
91
+
67
92
  ## Available Queries
68
93
 
69
94
  ### `getPartnerMomentsForBrand`
@@ -114,6 +139,29 @@ const { getPartnerMoment } = await itm.query({
114
139
  });
115
140
  ```
116
141
 
142
+ ### `getMomentStats`
143
+
144
+ Get cached partner-facing counts for a moment owned by the authenticated partner's brand.
145
+
146
+ - `ticketsCount` counts confirmed tickets only.
147
+ - `waitlistCount` counts active waitlist entries.
148
+ - `externalRsvpCount` counts external RSVPs.
149
+ - `ticketRsvpRequestsCount` counts ticket RSVP requests.
150
+
151
+ ```typescript
152
+ const { getMomentStats } = await itm.query({
153
+ getMomentStats: {
154
+ __args: { momentSlug: 'my-event-slug' },
155
+ ticketsCount: true,
156
+ waitlistCount: true,
157
+ externalRsvpCount: true,
158
+ ticketRsvpRequestsCount: true,
159
+ },
160
+ });
161
+ ```
162
+
163
+ > **Note:** `getMomentStats` returns `null` if the moment does not exist or is not owned by the authenticated partner's brand.
164
+
117
165
  ### `getPartnerTicketsForMoment`
118
166
 
119
167
  Get paginated tickets for a moment.
@@ -295,6 +343,299 @@ const { getPartnerPublicEchoes } = await itm.query({
295
343
 
296
344
  The SDK supports write operations via `client.mutation()`. All mutations use the same authentication — your brand is determined from the token.
297
345
 
346
+ ## Partner Brand Subscription Guide
347
+
348
+ The partner brand subscription API supports two integration modes:
349
+
350
+ - Direct subscribe: create the subscription immediately in one mutation.
351
+ - OTP subscribe: send an SMS code first, then confirm with a second mutation.
352
+
353
+ Use direct subscribe when your product already has the right consent flow and you do not need to prove possession of the phone number in real time.
354
+
355
+ Use OTP subscribe when you want the user to confirm the phone number before the brand subscription is created.
356
+
357
+ ### Flow Summary
358
+
359
+ | Mode | First mutation | First response | Second mutation |
360
+ |---|---|---|---|
361
+ | Direct | `createPartnerBrandSubscription` with `requireOtpVerification: false` or omitted | `verificationRequired: false` and `subscription` populated | None |
362
+ | OTP | `createPartnerBrandSubscription` with `requireOtpVerification: true` | `verificationRequired: true`, `verificationToken`, `subscription: null` | `verifyPartnerBrandSubscription` |
363
+
364
+ ### Input Behavior
365
+
366
+ - `phoneNumber`: send an E.164 phone number such as `+14155551234`.
367
+ - `fullName`: optional, but if you send it, include both first and last name.
368
+ - `email`: optional and attached to the subscribed user if provided.
369
+ - `listNames`: optional brand list names. Missing lists are created automatically and the subscribed user is attached to them.
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
+ - `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).
373
+
374
+ ### OTP Flow Step By Step
375
+
376
+ 1. Call `createPartnerBrandSubscription` with `requireOtpVerification: true`.
377
+ 2. Store the returned `verificationToken` in the user session or another short-lived server-side state container associated with the current signup attempt.
378
+ 3. Collect the SMS code from the user.
379
+ 4. Call `verifyPartnerBrandSubscription` with that `verificationToken` and `code`.
380
+
381
+ `verificationToken` is brand-scoped. A token created under one partner token cannot be verified by another brand's partner token.
382
+
383
+ If the verification token is invalid or expired, verification fails and the subscription is not created.
384
+
385
+ ### Post-Subscription Welcome Message
386
+
387
+ `postSubscriptionMessage` lets you fan out a welcome touch across SMS, email, and push the moment a subscription becomes active. The backend dispatches each channel asynchronously after the subscription is created (direct mode) or verified (OTP mode), so the mutation still returns synchronously — delivery happens in the background.
388
+
389
+ **Channels are independent.** Provide any combination of `sms`, `email`, and `push`. Channels you omit are skipped.
390
+
391
+ ```typescript
392
+ await itm.mutation({
393
+ createPartnerBrandSubscription: {
394
+ __args: {
395
+ input: {
396
+ phoneNumber: '+14155550111',
397
+ fullName: 'Jane Doe',
398
+ email: 'jane@example.com',
399
+ postSubscriptionMessage: {
400
+ sms: {
401
+ body: 'Welcome to Acme — your first drop ships Friday. Reply STOP to opt out.',
402
+ mediaUrls: ['https://cdn.example.com/welcome-banner.png'],
403
+ },
404
+ email: {
405
+ subject: 'Welcome to Acme',
406
+ body: 'Hi Jane,\n\nThanks for subscribing — here is what to expect next...',
407
+ },
408
+ push: {
409
+ title: 'Welcome to Acme',
410
+ body: 'Tap to see what is dropping this week.',
411
+ },
412
+ },
413
+ },
414
+ },
415
+ verificationRequired: true,
416
+ subscription: { uid: true },
417
+ },
418
+ });
419
+ ```
420
+
421
+ **Channel inputs:**
422
+
423
+ | Channel | Required | Optional | Notes |
424
+ |---|---|---|---|
425
+ | `sms` | `body` | `mediaUrls` (string array of image URLs for MMS) | Delivered to `phoneNumber`. |
426
+ | `email` | `subject`, `body` | — | `body` is plain text or markdown, rendered via React Email. Skipped if the user has no primary email or the brand has no configured email alias. |
427
+ | `push` | `title`, `body` | — | Skipped if the user has no push tokens or has opted out of brand messages. |
428
+
429
+ **Delivery semantics:**
430
+
431
+ - The message is sent **at most once per active subscription period**. The backend uses an atomic per-channel claim plus an idempotency key on each underlying task, so retrying the mutation (for example after a transient network failure) will not duplicate sends.
432
+ - The message is delivered when the subscription is **newly created or reactivated** — re-subscribing an already-active user is a no-op.
433
+ - In OTP mode, the message fires **after** `verifyPartnerBrandSubscription` succeeds, not when the OTP is requested.
434
+ - Channel failures are isolated: if email cannot be sent because the user has no primary email, SMS and push are still attempted.
435
+
436
+ **Recommended usage:**
437
+
438
+ - 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
+ - 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
+ - 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.
441
+
442
+ ### `createPartnerBrandSubscription`
443
+
444
+ Subscribe a phone number to the authenticated partner brand.
445
+
446
+ - Omit `requireOtpVerification` or set it to `false` to create the subscription immediately.
447
+ - Set `requireOtpVerification: true` to start the OTP flow. The backend sends the code to the provided phone number and returns a `verificationToken`.
448
+ - If you pass `fullName`, include both first and last name.
449
+
450
+ ```typescript
451
+ const { createPartnerBrandSubscription } = await itm.mutation({
452
+ createPartnerBrandSubscription: {
453
+ __args: {
454
+ input: {
455
+ phoneNumber: '+14155550111',
456
+ fullName: 'Jane Doe',
457
+ email: 'jane@example.com',
458
+ listNames: ['VIP', 'Newsletter'],
459
+ compliance: {
460
+ pageUrl: 'https://example.com/signup',
461
+ submissionMethod: 'partner_sdk',
462
+ },
463
+ },
464
+ },
465
+ verificationRequired: true,
466
+ verificationToken: true,
467
+ subscription: {
468
+ uid: true,
469
+ isActive: true,
470
+ subscribedAt: true,
471
+ },
472
+ },
473
+ });
474
+
475
+ if (!createPartnerBrandSubscription.verificationRequired) {
476
+ console.log(createPartnerBrandSubscription.subscription?.uid);
477
+ }
478
+ ```
479
+
480
+ When `verificationRequired` is `false`, `subscription` is populated and `verificationToken` is `null`.
481
+
482
+ This is the recommended mode when:
483
+
484
+ - You are subscribing from an already-authenticated server flow.
485
+ - You already have consent and do not need phone possession verification.
486
+ - You want a single network round trip.
487
+
488
+ ### `verifyPartnerBrandSubscription`
489
+
490
+ Complete a subscription created with `requireOtpVerification: true`.
491
+
492
+ ```typescript
493
+ const { createPartnerBrandSubscription } = await itm.mutation({
494
+ createPartnerBrandSubscription: {
495
+ __args: {
496
+ input: {
497
+ phoneNumber: '+14155550112',
498
+ requireOtpVerification: true,
499
+ fullName: 'Otp User',
500
+ compliance: {
501
+ pageUrl: 'https://example.com/otp-signup',
502
+ submissionMethod: 'partner_sdk',
503
+ },
504
+ },
505
+ },
506
+ verificationRequired: true,
507
+ verificationToken: true,
508
+ },
509
+ });
510
+
511
+ if (!createPartnerBrandSubscription.verificationToken) {
512
+ throw new Error('Expected OTP verification to be required');
513
+ }
514
+
515
+ // Collect the SMS code from the user in your UI or API flow.
516
+ const code = '123456';
517
+
518
+ const { verifyPartnerBrandSubscription } = await itm.mutation({
519
+ verifyPartnerBrandSubscription: {
520
+ __args: {
521
+ input: {
522
+ verificationToken:
523
+ createPartnerBrandSubscription.verificationToken,
524
+ code,
525
+ },
526
+ },
527
+ uid: true,
528
+ isActive: true,
529
+ subscribedAt: true,
530
+ },
531
+ });
532
+
533
+ console.log(verifyPartnerBrandSubscription.uid);
534
+ ```
535
+
536
+ When OTP is required, `createPartnerBrandSubscription` returns `verificationRequired: true`, `subscription: null`, and a non-null `verificationToken`. Pass that token into `verifyPartnerBrandSubscription` together with the one-time code sent to the phone number.
537
+
538
+ This is the recommended mode when:
539
+
540
+ - The subscription starts from a public signup form.
541
+ - You want to prove the user controls the submitted phone number.
542
+ - Your compliance flow requires explicit OTP confirmation before the subscription is created.
543
+
544
+ ### Full OTP Example
545
+
546
+ This is the full two-step flow most integrators will want to implement from a server route or server action.
547
+
548
+ ```typescript
549
+ import { createITMPartnerClient } from '@itm-studio/partner-sdk';
550
+
551
+ function createRequestScopedITM(request: Request) {
552
+ return createITMPartnerClient({
553
+ token: process.env.ITM_PARTNER_TOKEN!,
554
+ headers: () => ({
555
+ ...(request.headers.get('x-forwarded-for')
556
+ ? { 'x-forwarded-for': request.headers.get('x-forwarded-for')! }
557
+ : {}),
558
+ ...(request.headers.get('user-agent')
559
+ ? { 'user-agent': request.headers.get('user-agent')! }
560
+ : {}),
561
+ }),
562
+ });
563
+ }
564
+
565
+ export async function startOtpSubscription(request: Request) {
566
+ const itm = createRequestScopedITM(request);
567
+
568
+ const { createPartnerBrandSubscription } = await itm.mutation({
569
+ createPartnerBrandSubscription: {
570
+ __args: {
571
+ input: {
572
+ phoneNumber: '+14155550112',
573
+ requireOtpVerification: true,
574
+ fullName: 'Otp User',
575
+ email: 'otp@example.com',
576
+ listNames: ['Members', 'SMS'],
577
+ compliance: {
578
+ pageUrl: 'https://example.com/join',
579
+ submissionMethod: 'partner_sdk',
580
+ submittedAt: new Date().toISOString(),
581
+ timezone: 'America/New_York',
582
+ },
583
+ },
584
+ },
585
+ verificationRequired: true,
586
+ verificationToken: true,
587
+ subscription: {
588
+ uid: true,
589
+ },
590
+ },
591
+ });
592
+
593
+ if (!createPartnerBrandSubscription.verificationRequired) {
594
+ return {
595
+ status: 'subscribed',
596
+ subscriptionUid: createPartnerBrandSubscription.subscription?.uid ?? null,
597
+ };
598
+ }
599
+
600
+ return {
601
+ status: 'otp_required',
602
+ verificationToken: createPartnerBrandSubscription.verificationToken,
603
+ };
604
+ }
605
+
606
+ export async function finishOtpSubscription(
607
+ request: Request,
608
+ verificationToken: string,
609
+ code: string,
610
+ ) {
611
+ const itm = createRequestScopedITM(request);
612
+
613
+ const { verifyPartnerBrandSubscription } = await itm.mutation({
614
+ verifyPartnerBrandSubscription: {
615
+ __args: {
616
+ input: {
617
+ verificationToken,
618
+ code,
619
+ },
620
+ },
621
+ uid: true,
622
+ isActive: true,
623
+ subscribedAt: true,
624
+ },
625
+ });
626
+
627
+ return verifyPartnerBrandSubscription;
628
+ }
629
+ ```
630
+
631
+ ### Common Integration Notes
632
+
633
+ - Create the ITM client inside the incoming request when you need compliance backfill from headers.
634
+ - Persist the OTP `verificationToken` only for the duration of the signup flow. Treat it as short-lived state, not a durable identifier.
635
+ - Do not attempt to verify an OTP token with a different partner token than the one that created it.
636
+ - If you are building a browser form, post to your own backend first, then have your backend call the ITM Partner SDK. Do not call the SDK directly from the browser.
637
+ - If you need to associate the user with CRM-style segments on subscribe, send `listNames` during the initial create call. Those lists are created on demand.
638
+
298
639
  ### Media Upload Flow
299
640
 
300
641
  Uploading media (e.g. cover images) is a two-step process:
package/dist/index.d.mts CHANGED
@@ -162,6 +162,13 @@ interface PartnerCustomer {
162
162
  tickets: Ticket[];
163
163
  __typename: 'PartnerCustomer';
164
164
  }
165
+ interface PartnerMomentStats {
166
+ ticketsCount: Scalars['Int'];
167
+ waitlistCount: Scalars['Int'];
168
+ externalRsvpCount: Scalars['Int'];
169
+ ticketRsvpRequestsCount: Scalars['Int'];
170
+ __typename: 'PartnerMomentStats';
171
+ }
165
172
  interface MomentCollectionLink {
166
173
  uid: Scalars['ID'];
167
174
  url: Scalars['String'];
@@ -260,11 +267,24 @@ interface GetBrandEchoesForUserResponse {
260
267
  nextCursor: (Scalars['String'] | null);
261
268
  __typename: 'GetBrandEchoesForUserResponse';
262
269
  }
270
+ interface BrandSubscription {
271
+ uid: Scalars['ID'];
272
+ isActive: Scalars['Boolean'];
273
+ subscribedAt: Scalars['DateTime'];
274
+ createdAt: Scalars['DateTime'];
275
+ updatedAt: Scalars['DateTime'];
276
+ __typename: 'BrandSubscription';
277
+ }
263
278
  interface Query {
264
279
  /** Get paginated moments for the brand associated with the partner token. */
265
280
  getPartnerMomentsForBrand: GetPublicMomentsResponse;
266
281
  /** Get a single moment by slug. */
267
282
  getPartnerMoment: (Moment | null);
283
+ /**
284
+ * Get cached partner-facing counts for a moment.
285
+ * Only returns stats for moments owned by the authenticated partner's brand.
286
+ */
287
+ getPartnerMomentStats: (PartnerMomentStats | null);
268
288
  /** Get paginated tickets for a moment. */
269
289
  getPartnerTicketsForMoment: GetTicketsResponse;
270
290
  /**
@@ -315,6 +335,12 @@ interface MediaAsset {
315
335
  updatedAt: Scalars['DateTime'];
316
336
  __typename: 'MediaAsset';
317
337
  }
338
+ interface CreatePartnerBrandSubscriptionResponse {
339
+ subscription: (BrandSubscription | null);
340
+ verificationRequired: Scalars['Boolean'];
341
+ verificationToken: (Scalars['String'] | null);
342
+ __typename: 'CreatePartnerBrandSubscriptionResponse';
343
+ }
318
344
  interface Mutation {
319
345
  /**
320
346
  * Get a presigned S3 URL for direct file upload.
@@ -326,11 +352,18 @@ interface Mutation {
326
352
  * Call after uploading to S3 via the presigned URL from getPartnerUploadUrl.
327
353
  */
328
354
  createPartnerMediaAsset: MediaAsset;
355
+ /**
356
+ * Subscribe a user to the authenticated partner brand.
357
+ * Set requireOtpVerification to true to start a two-step OTP flow.
358
+ */
359
+ createPartnerBrandSubscription: CreatePartnerBrandSubscriptionResponse;
329
360
  /**
330
361
  * Create a moment with optional venue and ticket tiers in one atomic operation.
331
362
  * When type is IRL, the venue field is required.
332
363
  */
333
364
  createPartnerMoment: Moment;
365
+ /** Complete a partner brand subscription that was created with requireOtpVerification=true. */
366
+ verifyPartnerBrandSubscription: BrandSubscription;
334
367
  __typename: 'Mutation';
335
368
  }
336
369
  interface CurrencyGenqlSelection {
@@ -484,6 +517,14 @@ interface PartnerCustomerGenqlSelection {
484
517
  __typename?: boolean | number;
485
518
  __scalar?: boolean | number;
486
519
  }
520
+ interface PartnerMomentStatsGenqlSelection {
521
+ ticketsCount?: boolean | number;
522
+ waitlistCount?: boolean | number;
523
+ externalRsvpCount?: boolean | number;
524
+ ticketRsvpRequestsCount?: boolean | number;
525
+ __typename?: boolean | number;
526
+ __scalar?: boolean | number;
527
+ }
487
528
  interface MomentCollectionLinkGenqlSelection {
488
529
  uid?: boolean | number;
489
530
  url?: boolean | number;
@@ -595,6 +636,15 @@ interface GetBrandEchoesForUserResponseGenqlSelection {
595
636
  __typename?: boolean | number;
596
637
  __scalar?: boolean | number;
597
638
  }
639
+ interface BrandSubscriptionGenqlSelection {
640
+ uid?: boolean | number;
641
+ isActive?: boolean | number;
642
+ subscribedAt?: boolean | number;
643
+ createdAt?: boolean | number;
644
+ updatedAt?: boolean | number;
645
+ __typename?: boolean | number;
646
+ __scalar?: boolean | number;
647
+ }
598
648
  interface QueryGenqlSelection {
599
649
  /** Get paginated moments for the brand associated with the partner token. */
600
650
  getPartnerMomentsForBrand?: (GetPublicMomentsResponseGenqlSelection & {
@@ -613,6 +663,15 @@ interface QueryGenqlSelection {
613
663
  slug: Scalars['String'];
614
664
  };
615
665
  });
666
+ /**
667
+ * Get cached partner-facing counts for a moment.
668
+ * Only returns stats for moments owned by the authenticated partner's brand.
669
+ */
670
+ getPartnerMomentStats?: (PartnerMomentStatsGenqlSelection & {
671
+ __args: {
672
+ momentSlug: Scalars['String'];
673
+ };
674
+ });
616
675
  /** Get paginated tickets for a moment. */
617
676
  getPartnerTicketsForMoment?: (GetTicketsResponseGenqlSelection & {
618
677
  __args: {
@@ -713,6 +772,79 @@ interface CreatePartnerMomentInput {
713
772
  /** Ticket tiers to create along with the moment. */
714
773
  ticketTiers?: (CreatePartnerTicketTierInput[] | null);
715
774
  }
775
+ interface SubscriptionComplianceInput {
776
+ browserName?: (Scalars['String'] | null);
777
+ browserVersion?: (Scalars['String'] | null);
778
+ deviceType?: (Scalars['String'] | null);
779
+ geoAccuracy?: (Scalars['Float'] | null);
780
+ geoCity?: (Scalars['String'] | null);
781
+ geoCountry?: (Scalars['String'] | null);
782
+ geoLatitude?: (Scalars['Float'] | null);
783
+ geoLongitude?: (Scalars['Float'] | null);
784
+ geoRegion?: (Scalars['String'] | null);
785
+ ipAddress?: (Scalars['String'] | null);
786
+ language?: (Scalars['String'] | null);
787
+ osName?: (Scalars['String'] | null);
788
+ osVersion?: (Scalars['String'] | null);
789
+ pageUrl?: (Scalars['String'] | null);
790
+ referrerUrl?: (Scalars['String'] | null);
791
+ screenHeight?: (Scalars['Int'] | null);
792
+ screenWidth?: (Scalars['Int'] | null);
793
+ submissionMethod?: (Scalars['String'] | null);
794
+ submittedAt?: (Scalars['DateTime'] | null);
795
+ timezone?: (Scalars['String'] | null);
796
+ userAgent?: (Scalars['String'] | null);
797
+ }
798
+ interface PostSubscriptionSmsInput {
799
+ /** SMS message body delivered to the subscriber. */
800
+ body: Scalars['String'];
801
+ /** Optional MMS media URLs (image links) attached to the SMS. */
802
+ mediaUrls?: (Scalars['String'][] | null);
803
+ }
804
+ interface PostSubscriptionEmailInput {
805
+ /** Email subject line. */
806
+ subject: Scalars['String'];
807
+ /** Plain-text or markdown body. Rendered via React Email. */
808
+ body: Scalars['String'];
809
+ }
810
+ interface PostSubscriptionPushInput {
811
+ /** Push notification title. */
812
+ title: Scalars['String'];
813
+ /** Push notification body. */
814
+ body: Scalars['String'];
815
+ }
816
+ /**
817
+ * Welcome message delivered after a successful subscription. Each channel is independent —
818
+ * provide any combination of sms, email, and push. Channels with no input are skipped.
819
+ * The message is sent at most once per active subscription period (atomic per-channel claim).
820
+ */
821
+ interface PostSubscriptionMessageInput {
822
+ sms?: (PostSubscriptionSmsInput | null);
823
+ email?: (PostSubscriptionEmailInput | null);
824
+ push?: (PostSubscriptionPushInput | null);
825
+ }
826
+ interface CreatePartnerBrandSubscriptionInput {
827
+ compliance?: (SubscriptionComplianceInput | null);
828
+ email?: (Scalars['String'] | null);
829
+ /** Optional full name. If provided, include first and last name. */
830
+ fullName?: (Scalars['String'] | null);
831
+ /** Optional brand list names to attach the subscribed user to. Missing lists are created automatically. */
832
+ listNames?: (Scalars['String'][] | null);
833
+ /** E.164 phone number (for example +14155551234) */
834
+ phoneNumber: Scalars['String'];
835
+ requireOtpVerification?: (Scalars['Boolean'] | null);
836
+ /**
837
+ * Optional welcome message to deliver via the chosen channels. Sent at most once per active
838
+ * subscription period — safe to retry. Email requires the user to have a primary email and
839
+ * the brand to have a configured email alias. Push requires the user to have at least one
840
+ * push token and to not have opted out of brand messages.
841
+ */
842
+ postSubscriptionMessage?: (PostSubscriptionMessageInput | null);
843
+ }
844
+ interface VerifyPartnerBrandSubscriptionInput {
845
+ code: Scalars['String'];
846
+ verificationToken: Scalars['String'];
847
+ }
716
848
  /**
717
849
  * A presigned S3 upload URL and the generated filename.
718
850
  * Returned by getPartnerUploadUrl.
@@ -745,6 +877,13 @@ interface MediaAssetGenqlSelection {
745
877
  __typename?: boolean | number;
746
878
  __scalar?: boolean | number;
747
879
  }
880
+ interface CreatePartnerBrandSubscriptionResponseGenqlSelection {
881
+ subscription?: BrandSubscriptionGenqlSelection;
882
+ verificationRequired?: boolean | number;
883
+ verificationToken?: boolean | number;
884
+ __typename?: boolean | number;
885
+ __scalar?: boolean | number;
886
+ }
748
887
  interface MutationGenqlSelection {
749
888
  /**
750
889
  * Get a presigned S3 URL for direct file upload.
@@ -767,6 +906,15 @@ interface MutationGenqlSelection {
767
906
  dimensions: DimensionsInput;
768
907
  };
769
908
  });
909
+ /**
910
+ * Subscribe a user to the authenticated partner brand.
911
+ * Set requireOtpVerification to true to start a two-step OTP flow.
912
+ */
913
+ createPartnerBrandSubscription?: (CreatePartnerBrandSubscriptionResponseGenqlSelection & {
914
+ __args: {
915
+ input: CreatePartnerBrandSubscriptionInput;
916
+ };
917
+ });
770
918
  /**
771
919
  * Create a moment with optional venue and ticket tiers in one atomic operation.
772
920
  * When type is IRL, the venue field is required.
@@ -776,6 +924,12 @@ interface MutationGenqlSelection {
776
924
  input: CreatePartnerMomentInput;
777
925
  };
778
926
  });
927
+ /** Complete a partner brand subscription that was created with requireOtpVerification=true. */
928
+ verifyPartnerBrandSubscription?: (BrandSubscriptionGenqlSelection & {
929
+ __args: {
930
+ input: VerifyPartnerBrandSubscriptionInput;
931
+ };
932
+ });
779
933
  __typename?: boolean | number;
780
934
  __scalar?: boolean | number;
781
935
  }
@@ -838,6 +992,9 @@ declare const generateMutationOp: (fields: MutationGenqlSelection & {
838
992
  * Default ITM backend production API endpoint.
839
993
  */
840
994
  declare const DEFAULT_API_URL = "https://itm-backend-production.up.railway.app/graphql";
995
+ type MaybePromise<T> = T | Promise<T>;
996
+ type ITMPartnerHeadersObject = Record<string, string>;
997
+ type ITMPartnerHeaders = ITMPartnerHeadersObject | (() => MaybePromise<ITMPartnerHeadersObject | undefined>);
841
998
  interface ITMPartnerClientConfig {
842
999
  /**
843
1000
  * Partner API token (from backstage: Settings > Partner API).
@@ -851,8 +1008,10 @@ interface ITMPartnerClientConfig {
851
1008
  baseUrl?: string;
852
1009
  /**
853
1010
  * Additional headers to include on every request.
1011
+ * A function is useful for request-scoped values like `x-forwarded-for`
1012
+ * and `user-agent` when proxying end-user traffic through your server.
854
1013
  */
855
- headers?: Record<string, string>;
1014
+ headers?: ITMPartnerHeaders;
856
1015
  }
857
1016
  type ITMPartnerClient = Client;
858
1017
  /**
@@ -885,4 +1044,4 @@ type ITMPartnerClient = Client;
885
1044
  */
886
1045
  declare function createITMPartnerClient(config: ITMPartnerClientConfig): ITMPartnerClient;
887
1046
 
888
- export { type CreatePartnerMomentInput, type CreatePartnerTicketTierInput, type Currency, DEFAULT_API_URL, type Dimensions, type DimensionsInput, type Echo, type EchoMediaAsset, type EchoType, type GetBrandEchoesForUserResponse, type GetPartnerCustomersResponse, type GetPublicMomentsResponse, type GetTicketsResponse, type ITMPartnerClient, type ITMPartnerClientConfig, type MediaAsset, type MediaAssetUploadUrl, type MerchPurchase, type Moment, type MomentCategory, type MomentCollection, type MomentCollectionLink, type MomentDiscountCodeUsage, type MomentSortOrder, type MomentStatus, type MomentSubcategory, type MomentType, type Mutation, type PartnerCustomer, type Payment, type PaymentStatus, type Query, type ThirdPartyEchoType, type Ticket, type TicketExtension, type TicketExtensionStatus, type TicketTier, type User, type Venue, type VenueInput, createITMPartnerClient, everything, generateMutationOp, generateQueryOp };
1047
+ export { type BrandSubscription, type CreatePartnerBrandSubscriptionInput, type CreatePartnerBrandSubscriptionResponse, type CreatePartnerMomentInput, type CreatePartnerTicketTierInput, type Currency, DEFAULT_API_URL, type Dimensions, type DimensionsInput, type Echo, type EchoMediaAsset, type EchoType, type GetBrandEchoesForUserResponse, type GetPartnerCustomersResponse, type GetPublicMomentsResponse, type GetTicketsResponse, type ITMPartnerClient, type ITMPartnerClientConfig, type ITMPartnerHeaders, type ITMPartnerHeadersObject, type MediaAsset, type MediaAssetUploadUrl, type MerchPurchase, type Moment, type MomentCategory, type MomentCollection, type MomentCollectionLink, type MomentDiscountCodeUsage, type MomentSortOrder, type MomentStatus, type MomentSubcategory, type MomentType, type Mutation, type PartnerCustomer, type PartnerMomentStats, type Payment, type PaymentStatus, type PostSubscriptionEmailInput, type PostSubscriptionMessageInput, type PostSubscriptionPushInput, type PostSubscriptionSmsInput, type Query, type SubscriptionComplianceInput, type ThirdPartyEchoType, type Ticket, type TicketExtension, type TicketExtensionStatus, type TicketTier, type User, type Venue, type VenueInput, type VerifyPartnerBrandSubscriptionInput, createITMPartnerClient, everything, generateMutationOp, generateQueryOp };