vairified 0.3.2 → 0.6.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/dist/index.d.cts CHANGED
@@ -115,17 +115,29 @@ interface SportRatingWire {
115
115
  readonly rating: number;
116
116
  readonly abbr: string;
117
117
  readonly ratingSplits: Readonly<Record<string, RatingSplitWire>>;
118
+ readonly isVairified?: boolean;
119
+ readonly isRater?: boolean;
120
+ readonly isVairPro?: boolean;
121
+ readonly isVairProStatus?: VairProStatus;
118
122
  }
119
123
  /**
120
- * Grouped member status flags.
124
+ * VAIR-Pro lifecycle status in a sport: `'ACTIVE'` (approved, can rate),
125
+ * `'PENDING'` (paid, awaiting approval), or `null` (not a VAIR Pro here).
126
+ *
127
+ * @category Members
128
+ */
129
+ type VairProStatus = 'PENDING' | 'ACTIVE' | null;
130
+ /**
131
+ * Grouped member status flags — only the genuinely GLOBAL ones.
132
+ *
133
+ * VAIRification and VAIR-Pro status are per-sport (Vairified#783) and live on
134
+ * each {@link SportRatingWire} entry (`member.sport.get(code)`), not here.
121
135
  *
122
136
  * @category Members
123
137
  */
124
138
  interface MemberStatusWire {
125
- readonly isVairified: boolean;
126
139
  readonly isWheelchair: boolean;
127
140
  readonly isAmbassador: boolean;
128
- readonly isRater: boolean;
129
141
  readonly isConnected: boolean;
130
142
  }
131
143
  /**
@@ -145,6 +157,7 @@ interface PartnerMemberWire {
145
157
  readonly state?: string;
146
158
  readonly zip?: string;
147
159
  readonly country?: string;
160
+ readonly memberSince?: string;
148
161
  readonly gender?: Gender;
149
162
  readonly status: MemberStatusWire;
150
163
  readonly sport?: Readonly<Record<string, SportRatingWire>>;
@@ -152,6 +165,30 @@ interface PartnerMemberWire {
152
165
  readonly email?: string;
153
166
  readonly grantedScopes?: readonly string[];
154
167
  }
168
+ /**
169
+ * One resolved address in a {@link MembersByEmailResultWire}.
170
+ *
171
+ * `members` is always an array: an email address is not a unique key in
172
+ * VAIR, so one address can resolve to more than one member.
173
+ *
174
+ * @category Members
175
+ */
176
+ interface PartnerMemberEmailMatchWire {
177
+ readonly email: string;
178
+ readonly members: readonly PartnerMemberWire[];
179
+ }
180
+ /**
181
+ * Raw envelope returned by `GET /partner/members/by-email`.
182
+ *
183
+ * Every requested address appears in exactly one of `matched` or
184
+ * `notFound` — the endpoint never silently drops one.
185
+ *
186
+ * @category Members
187
+ */
188
+ interface MembersByEmailResultWire {
189
+ readonly matched: readonly PartnerMemberEmailMatchWire[];
190
+ readonly notFound: readonly string[];
191
+ }
155
192
  /**
156
193
  * Raw rating change notification.
157
194
  *
@@ -344,6 +381,10 @@ interface PlayerRankOptions {
344
381
  *
345
382
  * @category Matches
346
383
  */
384
+ interface TournamentImportCreatedGhostWire {
385
+ readonly ref: string;
386
+ readonly memberId: number;
387
+ }
347
388
  interface TournamentImportResultWire {
348
389
  readonly success: boolean;
349
390
  readonly matchesImported: number;
@@ -353,6 +394,7 @@ interface TournamentImportResultWire {
353
394
  readonly dryRun?: boolean;
354
395
  readonly message?: string;
355
396
  readonly errors?: readonly string[];
397
+ readonly createdGhostMembers?: readonly TournamentImportCreatedGhostWire[];
356
398
  }
357
399
  /**
358
400
  * Wire shape for a single webhook delivery attempt.
@@ -393,6 +435,24 @@ interface ApiErrorResponse {
393
435
  readonly error?: string;
394
436
  readonly statusCode?: number;
395
437
  }
438
+ type AttributionOutcome = 'attributed' | 'already_attributed' | 'account_predates_event' | 'not_found';
439
+ interface MemberAttributionWire {
440
+ readonly memberId: number;
441
+ readonly attributed: boolean;
442
+ readonly ambassadorMemberId?: number | null;
443
+ readonly attributedAt?: string;
444
+ }
445
+ interface MembersAttributionResultWire {
446
+ readonly attributions?: readonly MemberAttributionWire[];
447
+ readonly notFound?: readonly number[];
448
+ }
449
+ interface AttributionResultWire {
450
+ readonly attributed: number;
451
+ readonly results?: readonly {
452
+ readonly memberId: number;
453
+ readonly outcome: AttributionOutcome;
454
+ }[];
455
+ }
396
456
 
397
457
  /**
398
458
  * {@link LeaderboardResource} — read-only leaderboard queries.
@@ -452,6 +512,17 @@ declare class MatchBatchResult {
452
512
  * @module
453
513
  */
454
514
 
515
+ /**
516
+ * One ghost player created by a tournament import.
517
+ *
518
+ * @category Matches
519
+ */
520
+ interface TournamentImportCreatedGhost {
521
+ /** The email or phone you supplied for this person in `ghostMembers[]`. */
522
+ readonly ref: string;
523
+ /** The public member id allocated to them, usable in `matches.submit()`. */
524
+ readonly memberId: number;
525
+ }
455
526
  /**
456
527
  * Result of a tournament import submission.
457
528
  *
@@ -466,6 +537,19 @@ declare class TournamentImportResult {
466
537
  readonly dryRun: boolean;
467
538
  readonly message: string | undefined;
468
539
  readonly errors: readonly string[];
540
+ /**
541
+ * Public member ids for the ghost players THIS import created, keyed by the
542
+ * `ref` supplied in `ghostMembers[]`.
543
+ *
544
+ * Empty on a dry-run, and empty for entries the import matched to a player who
545
+ * already existed — resolving an existing email to a member requires the
546
+ * `key:player:lookup` scope and `members.getByEmail()`.
547
+ *
548
+ * Use these ids directly in a follow-up `matches.submit()`; without them an
549
+ * import reports only how many accounts it caused and you cannot address any
550
+ * of them.
551
+ */
552
+ readonly createdGhostMembers: readonly TournamentImportCreatedGhost[];
469
553
  /** @internal */
470
554
  constructor(wire: TournamentImportResultWire);
471
555
  /** True when the import succeeded without errors. */
@@ -582,6 +666,14 @@ declare class SportRating {
582
666
  readonly rating: number;
583
667
  /** Category abbreviation for the primary rating (e.g. `'VO'`). */
584
668
  readonly abbr: string;
669
+ /** Player is VAIRified in this sport (has a verified, non-recreational rating). */
670
+ readonly isVairified: boolean;
671
+ /** Player is an active VAIR Pro (can rate) in this sport. Alias of {@link isVairPro}. */
672
+ readonly isRater: boolean;
673
+ /** Player is an active VAIR Pro (can rate) in this sport. */
674
+ readonly isVairPro: boolean;
675
+ /** VAIR-Pro lifecycle status in this sport (`'ACTIVE'` / `'PENDING'` / `null`). */
676
+ readonly isVairProStatus: VairProStatus;
585
677
  constructor(wire: SportRatingWire);
586
678
  /**
587
679
  * Look up a rating split by key (e.g. `'overall-open'`,
@@ -663,6 +755,20 @@ declare class Member {
663
755
  readonly state: string | null;
664
756
  readonly zip: string | null;
665
757
  readonly country: string | null;
758
+ /**
759
+ * The DATE this member's VAIR account was created (`YYYY-MM-DD`, UTC), or
760
+ * `null` when the endpoint does not supply it.
761
+ *
762
+ * Present on `members.get()`, `members.getBulk()` and `members.getByEmail()` —
763
+ * the calls where you already know which member you asked about. **Never on
764
+ * `members.search()`**, which is discovery: account age is not something you
765
+ * can browse strangers by.
766
+ *
767
+ * Deliberately a date, not a timestamp. It exists so you can apply a
768
+ * new-accounts-only referral rule — crediting an ambassador only for accounts
769
+ * created because of their event.
770
+ */
771
+ readonly memberSince: string | null;
666
772
  readonly gender: Gender | null;
667
773
  readonly status: MemberStatusWire;
668
774
  readonly sport: MemberSportMap;
@@ -693,6 +799,74 @@ declare class Member {
693
799
  toString(): string;
694
800
  }
695
801
 
802
+ /**
803
+ * {@link MembersByEmailResult} — result of an email → member lookup.
804
+ *
805
+ * @module
806
+ */
807
+
808
+ /**
809
+ * One requested address that resolved to at least one member.
810
+ *
811
+ * {@link members} is always an array. An email address is not a unique
812
+ * key in VAIR — an unclaimed imported record can share an address with a
813
+ * claimed account — so never assume a single element without checking.
814
+ *
815
+ * @category Members
816
+ */
817
+ declare class MemberEmailMatch {
818
+ /** The address exactly as you supplied it, not as stored. */
819
+ readonly email: string;
820
+ /** Every member holding this address. Never empty. */
821
+ readonly members: readonly Member[];
822
+ /** @internal */
823
+ constructor(wire: PartnerMemberEmailMatchWire);
824
+ /**
825
+ * The single member for this address, or `null` when the address is
826
+ * ambiguous (more than one match).
827
+ *
828
+ * Use this rather than `members[0]` when a wrong link is worse than no
829
+ * link — it refuses to guess instead of silently picking one.
830
+ */
831
+ get sole(): Member | null;
832
+ /** Whether this address resolved to more than one member. */
833
+ get isAmbiguous(): boolean;
834
+ toString(): string;
835
+ }
836
+ /**
837
+ * Result of a {@link MembersResource.getByEmail} call.
838
+ *
839
+ * Every address you supplied appears in exactly one of {@link matched} or
840
+ * {@link notFound} — the endpoint never silently drops one, so consume
841
+ * {@link notFound} directly rather than diffing your input against the
842
+ * results.
843
+ *
844
+ * A `notFound` address is not proof the person has no VAIR account:
845
+ * unclaimed imported records are deliberately excluded from this lookup.
846
+ *
847
+ * @category Members
848
+ */
849
+ declare class MembersByEmailResult {
850
+ readonly matched: readonly MemberEmailMatch[];
851
+ /** Addresses that resolved to nothing, echoed as you supplied them. */
852
+ readonly notFound: readonly string[];
853
+ /** @internal */
854
+ constructor(wire: MembersByEmailResultWire);
855
+ /**
856
+ * Look up one address's match, case-insensitively.
857
+ *
858
+ * Saves callers a linear scan and, more importantly, saves them from
859
+ * matching case-sensitively against an address the server echoed back
860
+ * in whatever case they originally sent.
861
+ */
862
+ get(email: string): MemberEmailMatch | null;
863
+ /** Whether every requested address resolved to at least one member. */
864
+ get allResolved(): boolean;
865
+ /** Total number of members across every matched address. */
866
+ get memberCount(): number;
867
+ toString(): string;
868
+ }
869
+
696
870
  /**
697
871
  * {@link RatingUpdate} — a single rating change notification.
698
872
  *
@@ -830,6 +1004,50 @@ declare class MembersResource {
830
1004
  getBulk(ids: number[], options?: {
831
1005
  sport?: string;
832
1006
  }): Promise<Member[]>;
1007
+ /**
1008
+ * Resolve up to 100 members by their **exact** email address.
1009
+ *
1010
+ * Use this to link your users to their VAIR identity when you hold
1011
+ * their email but not their member ID — e.g. resolving a tournament
1012
+ * roster at registration instead of waiting for each player to
1013
+ * complete SSO.
1014
+ *
1015
+ * **Requires the `key:player:lookup` scope**, which is granted per
1016
+ * partner on approval. Holding `key:player:search` does not imply it.
1017
+ *
1018
+ * Matching is exact and case-insensitive; there is deliberately no
1019
+ * partial, prefix or fuzzy matching. Every address you supply comes
1020
+ * back in either `matched` or `notFound`, so read `notFound` directly
1021
+ * instead of diffing your input against the results.
1022
+ *
1023
+ * A `notFound` address is **not** proof the person has no VAIR
1024
+ * account — unclaimed imported records are excluded from this lookup.
1025
+ *
1026
+ * @param emails - Email addresses to resolve (max 100).
1027
+ * @param options - Optional filters.
1028
+ * @param options.sport - Sport code to scope ratings (e.g. `'pickleball'`).
1029
+ * @throws {@link ValidationError} If more than 100 addresses are
1030
+ * provided, or the list is empty.
1031
+ * @category Members
1032
+ *
1033
+ * @example
1034
+ * ```ts
1035
+ * const result = await client.members.getByEmail([
1036
+ * 'ada@example.com',
1037
+ * 'nobody@example.com',
1038
+ * ]);
1039
+ *
1040
+ * for (const match of result.matched) {
1041
+ * const member = match.sole; // null when the address is ambiguous
1042
+ * if (member) console.log(match.email, '->', member.memberId);
1043
+ * }
1044
+ *
1045
+ * console.log('no VAIR account found for:', result.notFound);
1046
+ * ```
1047
+ */
1048
+ getByEmail(emails: readonly string[], options?: {
1049
+ sport?: string;
1050
+ }): Promise<MembersByEmailResult>;
833
1051
  /**
834
1052
  * Poll for rating change notifications.
835
1053
  *
@@ -961,9 +1179,17 @@ declare function describeScopes(scopes: readonly string[]): readonly {
961
1179
  * Generate a cryptographically-random CSRF state token suitable for
962
1180
  * use with {@link OAuthResource.authorize}.
963
1181
  *
964
- * Uses the Web Crypto API (available in Node 19+ and all modern
965
- * browsers). The returned string is URL-safe base64 of 32 random bytes.
1182
+ * Uses the Web Crypto API `crypto.getRandomValues` (built into Node 19+
1183
+ * and all modern browsers). The returned string is URL-safe, unpadded
1184
+ * base64 of 32 random bytes.
966
1185
  *
1186
+ * On runtimes without Web Crypto — notably **React Native / Hermes** —
1187
+ * this throws a descriptive `Error` (rather than a bare `ReferenceError`)
1188
+ * telling you to install and import `react-native-get-random-values` at
1189
+ * your app entry, or to pass your own high-entropy `state` string. Base64
1190
+ * encoding is done in pure JS, so no `btoa` polyfill is required.
1191
+ *
1192
+ * @throws {Error} when `crypto.getRandomValues` is unavailable.
967
1193
  * @category OAuth
968
1194
  */
969
1195
  declare function generateState(): string;
@@ -1022,6 +1248,170 @@ declare class OAuthResource {
1022
1248
  }[]>;
1023
1249
  }
1024
1250
 
1251
+ /**
1252
+ * {@link MemberAttribution}, {@link MembersAttributionResult} and
1253
+ * {@link AttributionResult} — ambassador referral credit.
1254
+ *
1255
+ * @module
1256
+ */
1257
+
1258
+ /**
1259
+ * Who currently earns referral credit for one member.
1260
+ *
1261
+ * @category Referrals
1262
+ */
1263
+ declare class MemberAttribution {
1264
+ readonly memberId: number;
1265
+ /** True when some ambassador already holds credit for this member. */
1266
+ readonly attributed: boolean;
1267
+ /**
1268
+ * The member id of the ambassador holding the credit.
1269
+ *
1270
+ * Compare it against your own event host's member id to tell "already credited
1271
+ * to my host — nothing to do" from "credited to somebody else — a person needs
1272
+ * to look, because claiming it takes credit from them".
1273
+ *
1274
+ * `null` both when nobody holds credit and when credit is held by a record that
1275
+ * has no member id of its own, so check {@link attributed} to tell those apart.
1276
+ */
1277
+ readonly ambassadorMemberId: number | null;
1278
+ /** The date the credit was established (`YYYY-MM-DD`), or `null`. */
1279
+ readonly attributedAt: string | null;
1280
+ /** @internal */
1281
+ constructor(wire: MemberAttributionWire);
1282
+ /** True when nobody holds credit yet, so this member can be claimed. */
1283
+ get isClaimable(): boolean;
1284
+ /** True when credit is held by an ambassador OTHER than the one given. */
1285
+ heldBySomeoneOtherThan(ambassadorMemberId: number): boolean;
1286
+ }
1287
+ /**
1288
+ * Attribution for a batch of members, plus the ids that matched nothing.
1289
+ *
1290
+ * @category Referrals
1291
+ */
1292
+ declare class MembersAttributionResult {
1293
+ readonly attributions: readonly MemberAttribution[];
1294
+ /**
1295
+ * Member ids that matched no member. Read this rather than diffing your input
1296
+ * against the results — every id you sent lands in one bucket or the other.
1297
+ */
1298
+ readonly notFound: readonly number[];
1299
+ /** @internal */
1300
+ constructor(wire: MembersAttributionResultWire);
1301
+ /** Attribution for one member id, or `undefined` if it was not returned. */
1302
+ get(memberId: number): MemberAttribution | undefined;
1303
+ /** Members nobody holds credit for yet. */
1304
+ get claimable(): readonly MemberAttribution[];
1305
+ }
1306
+ /**
1307
+ * Outcome of submitting attribution for a batch of members.
1308
+ *
1309
+ * @category Referrals
1310
+ */
1311
+ declare class AttributionResult {
1312
+ /** How many members were newly attributed by this request. */
1313
+ readonly attributed: number;
1314
+ /** One entry per member id supplied, in the order supplied. */
1315
+ readonly results: readonly Readonly<{
1316
+ memberId: number;
1317
+ outcome: AttributionOutcome;
1318
+ }>[];
1319
+ /** @internal */
1320
+ constructor(wire: AttributionResultWire);
1321
+ /** Member ids with the given outcome. */
1322
+ withOutcome(outcome: AttributionOutcome): readonly number[];
1323
+ /**
1324
+ * Members already credited to somebody. These are the ones worth a human
1325
+ * look — it may be your own host, or it may be another ambassador.
1326
+ */
1327
+ get alreadyAttributed(): readonly number[];
1328
+ /**
1329
+ * Members rejected because their account pre-dates the event's registration
1330
+ * page. The event did not recruit them, so no credit is due.
1331
+ */
1332
+ get predatedEvent(): readonly number[];
1333
+ }
1334
+
1335
+ /**
1336
+ * {@link ReferralsResource} — read and record ambassador referral credit.
1337
+ *
1338
+ * @module
1339
+ */
1340
+
1341
+ /**
1342
+ * Read and record which ambassador earns referral credit for a player.
1343
+ *
1344
+ * Each method needs its own API-key permission, granted per partner:
1345
+ * `key:referral:read` for {@link get} and `key:referral:write` for
1346
+ * {@link attribute}. Neither is implied by a general read, write or admin key —
1347
+ * reading attribution exposes who recruited whom, and writing it decides who
1348
+ * earns commission.
1349
+ *
1350
+ * @category Resources
1351
+ */
1352
+ declare class ReferralsResource {
1353
+ #private;
1354
+ /** @internal */
1355
+ constructor(http: HttpTransport);
1356
+ /**
1357
+ * Who currently earns referral credit for these members.
1358
+ *
1359
+ * Use this before {@link attribute} to tell the two cases apart that matter:
1360
+ * a member already credited to your own event host (nothing to do) and one
1361
+ * credited to a different ambassador (a person should look, because claiming
1362
+ * it takes credit from them).
1363
+ *
1364
+ * @example
1365
+ * ```ts
1366
+ * const result = await client.referrals.get([4873327, 4873328]);
1367
+ *
1368
+ * for (const a of result.claimable) {
1369
+ * console.log(a.memberId, 'has no credit yet');
1370
+ * }
1371
+ * console.log('no such member:', result.notFound);
1372
+ * ```
1373
+ *
1374
+ * @throws {@link ValidationError} If the list is empty or exceeds 100 ids.
1375
+ */
1376
+ get(memberIds: readonly number[]): Promise<MembersAttributionResult>;
1377
+ /**
1378
+ * Credit an ambassador for players their event recruited.
1379
+ *
1380
+ * `registrationPublishedAt` is the date the event's registration page was
1381
+ * **first published**. Accounts created before it did not come from the event
1382
+ * and are rejected with `account_predates_event`. VAIR applies that rule
1383
+ * itself, so every partner is held to the same one.
1384
+ *
1385
+ * VAIR cannot verify the date — it holds no record of your registration pages —
1386
+ * so the value you send is recorded for audit. Send the real one.
1387
+ *
1388
+ * Safe to retry: attribution is one-per-player forever, enforced by the
1389
+ * database, so a resubmitted player returns `already_attributed` and nothing
1390
+ * changes.
1391
+ *
1392
+ * @example
1393
+ * ```ts
1394
+ * const result = await client.referrals.attribute({
1395
+ * referralCode: 'hillhurst-open',
1396
+ * registrationPublishedAt: '2026-08-01',
1397
+ * memberIds: [4873327, 4873328],
1398
+ * });
1399
+ *
1400
+ * console.log(result.attributed, 'newly credited');
1401
+ * console.log('need a human:', result.alreadyAttributed);
1402
+ * console.log('too old to credit:', result.predatedEvent);
1403
+ * ```
1404
+ *
1405
+ * @throws {@link ValidationError} If the list is empty or exceeds 500 ids, or
1406
+ * the date is not `YYYY-MM-DD`.
1407
+ */
1408
+ attribute(input: {
1409
+ referralCode: string;
1410
+ registrationPublishedAt: string;
1411
+ memberIds: readonly number[];
1412
+ }): Promise<AttributionResult>;
1413
+ }
1414
+
1025
1415
  /**
1026
1416
  * {@link WebhookDelivery} and {@link WebhookDeliveriesResult} — webhook
1027
1417
  * delivery inspection models.
@@ -1174,6 +1564,11 @@ declare class Vairified {
1174
1564
  readonly leaderboard: LeaderboardResource;
1175
1565
  /** Webhook delivery inspection — deliveries. */
1176
1566
  readonly webhooks: WebhooksResource;
1567
+ /**
1568
+ * Read and record ambassador referral credit. Each method needs its own
1569
+ * per-partner permission — see {@link ReferralsResource}.
1570
+ */
1571
+ readonly referrals: ReferralsResource;
1177
1572
  constructor(options?: VairifiedOptions);
1178
1573
  /**
1179
1574
  * API usage statistics for the current API key.
@@ -1272,4 +1667,4 @@ declare class OAuthError extends VairifiedError {
1272
1667
  constructor(message?: string, errorCode?: string, response?: unknown);
1273
1668
  }
1274
1669
 
1275
- export { type ApiErrorResponse, AuthenticationError, type AuthorizationResponse, DEFAULT_SCOPES, ENVIRONMENTS, type GameInput, type Gender, type LeaderboardOptions, LeaderboardResource, type MatchBatch, MatchBatchResult, type MatchBatchResultWire, type MatchInput, MatchesResource, Member, MemberSportMap, type MemberStatusWire, MembersResource, NotFoundError, type OAuthConfig, OAuthError, OAuthResource, type OAuthScope, type PartnerMemberWire, type PartnerRatingUpdateWire, type PlayerRankOptions, RateLimitError, type RatingSplitWire, RatingUpdate, SCOPES, type SearchFilters, SportRating, type SportRatingWire, type TokenResponse, TournamentImportResult, type TournamentImportResultWire, Vairified, type VairifiedEnvironment, VairifiedError, type VairifiedOptions, ValidationError, WebhookDeliveriesResult, type WebhookDeliveriesResultWire, WebhookDelivery, type WebhookDeliveryWire, WebhooksResource, describeScope, describeScopes, generateState, getAuthorizationUrl, validateScope };
1670
+ export { type ApiErrorResponse, AuthenticationError, type AuthorizationResponse, DEFAULT_SCOPES, ENVIRONMENTS, type GameInput, type Gender, type LeaderboardOptions, LeaderboardResource, type MatchBatch, MatchBatchResult, type MatchBatchResultWire, type MatchInput, MatchesResource, Member, MemberEmailMatch, MemberSportMap, type MemberStatusWire, MembersByEmailResult, type MembersByEmailResultWire, MembersResource, NotFoundError, type OAuthConfig, OAuthError, OAuthResource, type OAuthScope, type PartnerMemberEmailMatchWire, type PartnerMemberWire, type PartnerRatingUpdateWire, type PlayerRankOptions, RateLimitError, type RatingSplitWire, RatingUpdate, SCOPES, type SearchFilters, SportRating, type SportRatingWire, type TokenResponse, TournamentImportResult, type TournamentImportResultWire, Vairified, type VairifiedEnvironment, VairifiedError, type VairifiedOptions, ValidationError, WebhookDeliveriesResult, type WebhookDeliveriesResultWire, WebhookDelivery, type WebhookDeliveryWire, WebhooksResource, describeScope, describeScopes, generateState, getAuthorizationUrl, validateScope };