ad2app-lib 1.44.0 → 1.47.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.
@@ -79,6 +79,25 @@ export declare const ACCESS_DENIAL_CODES: {
79
79
  * per person, FR-004). Names the case and NOTHING else about that account.
80
80
  */
81
81
  readonly AGENCY_ALREADY_IN_ORGANIZATION: "agency-already-in-organization";
82
+ /**
83
+ * The owner invited an address that is ALREADY on this organization's team,
84
+ * themselves included (T063). A different case from the one above: the owner
85
+ * is told this person is already in, not that they belong elsewhere.
86
+ */
87
+ readonly AGENCY_ALREADY_MEMBER: "agency-already-member";
88
+ /**
89
+ * The owner tried to cover a creator who pays for their own plan. Covering
90
+ * is for a creator without a paid plan (US4 scenario 3); covering a payer
91
+ * would leave them paying for what the agency also pays for (T066).
92
+ */
93
+ readonly AGENCY_CREATOR_PAYS_OWN_PLAN: "agency-creator-pays-own-plan";
94
+ /**
95
+ * A creator whose plan an agency covers asked for the billing portal, and
96
+ * has no Stripe customer of their own to open it for (FR-014). The body
97
+ * names the covering agency; the client hides the portal button instead of
98
+ * showing a Stripe error.
99
+ */
100
+ readonly SPONSORED_NO_PORTAL: "sponsored-no-portal";
82
101
  };
83
102
  export type AccessDenialCode = (typeof ACCESS_DENIAL_CODES)[keyof typeof ACCESS_DENIAL_CODES];
84
103
  /**
@@ -83,6 +83,25 @@ exports.ACCESS_DENIAL_CODES = {
83
83
  * per person, FR-004). Names the case and NOTHING else about that account.
84
84
  */
85
85
  AGENCY_ALREADY_IN_ORGANIZATION: "agency-already-in-organization",
86
+ /**
87
+ * The owner invited an address that is ALREADY on this organization's team,
88
+ * themselves included (T063). A different case from the one above: the owner
89
+ * is told this person is already in, not that they belong elsewhere.
90
+ */
91
+ AGENCY_ALREADY_MEMBER: "agency-already-member",
92
+ /**
93
+ * The owner tried to cover a creator who pays for their own plan. Covering
94
+ * is for a creator without a paid plan (US4 scenario 3); covering a payer
95
+ * would leave them paying for what the agency also pays for (T066).
96
+ */
97
+ AGENCY_CREATOR_PAYS_OWN_PLAN: "agency-creator-pays-own-plan",
98
+ /**
99
+ * A creator whose plan an agency covers asked for the billing portal, and
100
+ * has no Stripe customer of their own to open it for (FR-014). The body
101
+ * names the covering agency; the client hides the portal button instead of
102
+ * showing a Stripe error.
103
+ */
104
+ SPONSORED_NO_PORTAL: "sponsored-no-portal",
86
105
  };
87
106
  /**
88
107
  * WHICH precondition the entitlement wall refused on (backend#305, spec 170).
@@ -93,6 +93,12 @@ export interface I_AgencyMember {
93
93
  /** Null while an e-mail invitation is outstanding — nobody has accepted yet. */
94
94
  userId?: string;
95
95
  invitedEmail: string;
96
+ /**
97
+ * The account's own e-mail once the invitation is accepted (and always for
98
+ * the owner, who applied rather than being invited). The Team panel names a
99
+ * person by this, never by the organization's contact address (T063).
100
+ */
101
+ accountEmail?: string;
96
102
  role: AgencyMemberRole;
97
103
  invitedAt: string;
98
104
  /** "Who is in" is exactly `acceptedAt != null`. */
@@ -152,7 +158,27 @@ export interface I_CreatorAccessGrant {
152
158
  };
153
159
  /** Which platforms the grant reaches right now — derived at read time, never stored. */
154
160
  platformsReached: PublishPlatform[];
161
+ /**
162
+ * The agency's OPEN second ask on an active grant, for scopes the creator
163
+ * did not grant (FR-008). Kept apart from `requestedScopes`, so the consent
164
+ * screen names exactly this ask and nothing refused earlier (review H4).
165
+ * Absent when there is no open ask.
166
+ */
167
+ reaskedScopes?: I_AgencyGrantScope[];
168
+ /** When that open ask expires unanswered. */
169
+ reaskExpiresAt?: string;
155
170
  }
171
+ /**
172
+ * Outcomes of discovery and requests that are not refusals of access, sent as
173
+ * codes so the screen composes the sentence in the reader's language (FR-019,
174
+ * review M4). A 429 carries `{ code, limit, windowMinutes }`.
175
+ */
176
+ export declare const AGENCY_REQUEST_CODES: {
177
+ readonly RATE_LIMITED: "agency-rate-limited";
178
+ readonly REQUEST_EXPIRED: "agency-request-expired";
179
+ readonly REQUEST_ANSWERED: "agency-request-answered";
180
+ };
181
+ export type AgencyRequestCode = (typeof AGENCY_REQUEST_CODES)[keyof typeof AGENCY_REQUEST_CODES];
156
182
  /**
157
183
  * Roster row states, DECLARED IN PRECEDENCE ORDER (data-model §Derived).
158
184
  *
@@ -160,7 +186,7 @@ export interface I_CreatorAccessGrant {
160
186
  * first. `revoked` is deliberately absent — a revoked creator leaves the roster
161
187
  * entirely for the settings history (FR-011).
162
188
  */
163
- export declare const AGENCY_ROSTER_ROW_STATES: readonly ["requested", "lapsed", "disconnected", "stale", "active"];
189
+ export declare const AGENCY_ROSTER_ROW_STATES: readonly ["requested", "lapsed", "disconnected", "syncing", "stale", "active"];
164
190
  export type AgencyRosterRowState = (typeof AGENCY_ROSTER_ROW_STATES)[number];
165
191
  /**
166
192
  * What "needs attention" means — ONE home for the KPI tile, the roster segment
@@ -171,6 +197,32 @@ export type AgencyRosterRowState = (typeof AGENCY_ROSTER_ROW_STATES)[number];
171
197
  */
172
198
  export declare const AGENCY_ROSTER_NEEDS_ATTENTION_STATES: readonly ["lapsed", "disconnected", "stale"];
173
199
  export type AgencyRosterNeedsAttentionState = (typeof AGENCY_ROSTER_NEEDS_ATTENTION_STATES)[number];
200
+ /**
201
+ * The rows that carry numbers — ONE home for "which creators' numbers may the
202
+ * agency see on the roster" (decided 2026-09-24, review M1/M2).
203
+ *
204
+ * Only a row whose card OPENS carries `metrics` and counts in the followers,
205
+ * followers-delta and comments totals: `active`, and `stale` (the card opens;
206
+ * the numbers are as old as `lastSyncAt` says). A `lapsed` creator's reads are
207
+ * refused on the card, so their numbers must not reach the agency through the
208
+ * roster instead; a `disconnected` creator has nothing live to count; a
209
+ * `requested` creator has shared nothing yet.
210
+ */
211
+ export declare const AGENCY_ROSTER_METRICS_STATES: readonly ["syncing", "stale", "active"];
212
+ export type AgencyRosterMetricsState = (typeof AGENCY_ROSTER_METRICS_STATES)[number];
213
+ /**
214
+ * One creator's numbers on the roster (FR-011a: sortable metric columns), read
215
+ * from the daily-synced tables, never a vendor call. `null` is a REASON, never
216
+ * a zero: the creator did not grant that scope, or nothing has been synced yet.
217
+ */
218
+ export interface I_AgencyRosterRowMetrics {
219
+ /** Latest followers over the creator's CONNECTED platforms; null without `analytics` or before the first sync. */
220
+ followers: number | null;
221
+ /** Against the snapshot seven days earlier, per connected platform; null without `analytics` or without a week-old point. */
222
+ followersDelta7d: number | null;
223
+ /** The current Monday-to-Sunday week, organization timezone; null without `comments`. */
224
+ commentsThisWeek: number | null;
225
+ }
174
226
  export interface I_AgencyRosterRow {
175
227
  grantId: string;
176
228
  creator: {
@@ -180,22 +232,72 @@ export interface I_AgencyRosterRow {
180
232
  platforms: PublishPlatform[];
181
233
  state: AgencyRosterRowState;
182
234
  grantedScopes: I_AgencyGrantScope[];
235
+ /**
236
+ * The newest successful daily sync. Absent on a `requested` row (nothing is
237
+ * shared yet, so there is nothing to have synced for the agency) and on a
238
+ * creator never synced. `platforms` stays on a requested row: it is what the
239
+ * consent screen names, a fact about the account rather than shared data.
240
+ */
183
241
  lastSyncAt?: string;
242
+ /**
243
+ * Present ONLY on rows in AGENCY_ROSTER_METRICS_STATES (`active`, `stale`):
244
+ * the rows whose card opens. Absent on `lapsed`, `disconnected` and
245
+ * `requested` rows — no numbers, not null numbers.
246
+ */
247
+ metrics?: I_AgencyRosterRowMetrics;
248
+ /**
249
+ * The agency's own last "Ask again" (FR-008), while its one-per-TTL clock
250
+ * runs (audit F19): when it was sent and when another is possible. It says
251
+ * nothing about the ANSWER, because the clock runs whatever the creator did.
252
+ * The roster uses it to say "asked on …, again after …" instead of offering
253
+ * an ask that would not be sent. Absent once the clock has run out.
254
+ */
255
+ reaskedAt?: string;
256
+ reaskAvailableAt?: string;
257
+ }
258
+ /**
259
+ * The platforms a creator has connected, as the agency side sees them
260
+ * (discovery, the grant's "reaches"). `GET users/me/connected-platforms`,
261
+ * answered to every creator, subscribed or not.
262
+ */
263
+ export interface I_ConnectedPlatforms {
264
+ platforms: PublishPlatform[];
184
265
  }
185
266
  export interface I_AgencyRoster {
186
267
  totals: {
187
268
  creators: number;
188
269
  active: number;
189
- /** Summed only over creators holding `analytics`; disconnected platforms excluded. */
190
- followers: number;
191
- followersDelta7d: number;
192
- /** Calendar week, Mon-Sun, organization timezone, creators holding `comments`. */
193
- commentsThisWeek: number;
270
+ /**
271
+ * Summed only over rows in AGENCY_ROSTER_METRICS_STATES whose creator holds
272
+ * `analytics`; disconnected platforms excluded. `null` when no counted
273
+ * creator has a synced number yet, never a zero standing in (T067).
274
+ */
275
+ followers: number | null;
276
+ /**
277
+ * Same rows as `followers`, against the snapshot seven days earlier. `null`
278
+ * when no counted creator has a week-old point — unknown, never a zero.
279
+ */
280
+ followersDelta7d: number | null;
281
+ /**
282
+ * Calendar week, Mon-Sun, organization timezone, over rows in
283
+ * AGENCY_ROSTER_METRICS_STATES whose creator holds `comments`. Like
284
+ * `followers`, `null` when no counted creator has a synced number yet:
285
+ * unknown, never a zero (SC-008, T067).
286
+ */
287
+ commentsThisWeek: number | null;
194
288
  /** Count of rows in AGENCY_ROSTER_NEEDS_ATTENTION_STATES. */
195
289
  needsAttention: number;
196
290
  };
197
291
  items: I_AgencyRosterRow[];
198
292
  }
293
+ /**
294
+ * A coverage that ENDED recently, so the creator meeting the paywall again is
295
+ * told why (FR-014: "an honest message, never a silent loss of access").
296
+ */
297
+ export interface I_SponsorshipEnded {
298
+ organizationName: string;
299
+ endedAt: string;
300
+ }
199
301
  /** What a sponsored creator sees on their OWN subscription screen (FR-014). */
200
302
  export interface I_SponsoredBy {
201
303
  organizationName: string;
@@ -203,25 +305,140 @@ export interface I_SponsoredBy {
203
305
  /** Present once the sponsorship is ending: the date their plan runs to. */
204
306
  until?: string;
205
307
  }
308
+ /** The card on file, as Stripe describes it. Never more than these four facts. */
309
+ export interface I_AgencyPaymentMethod {
310
+ brand: string;
311
+ last4: string;
312
+ expMonth: number;
313
+ expYear: number;
314
+ }
315
+ /**
316
+ * One invoice. `amount` is Stripe's minor unit verbatim; the display edge
317
+ * formats it once, like every other price in the app.
318
+ */
319
+ export interface I_AgencyInvoice {
320
+ id: string;
321
+ number: string | null;
322
+ date: string;
323
+ amount: number;
324
+ currency: string;
325
+ status: string;
326
+ pdfUrl?: string;
327
+ hostedUrl?: string;
328
+ }
329
+ /**
330
+ * One creator on an ACTIVE grant, with whether the agency covers their plan
331
+ * (FR-013b). `tier: null` is "not covered". `until` is set while a coverage is
332
+ * ending: it runs to the close of the period already paid for.
333
+ */
334
+ export interface I_AgencyCoveredCreator {
335
+ grantId: string;
336
+ creator: {
337
+ id: string;
338
+ handle: string;
339
+ };
340
+ tier: SponsoredTier | null;
341
+ since?: string;
342
+ until?: string;
343
+ }
344
+ /**
345
+ * An ended relationship, for the past-creators history (FR-013b). `creator.id`
346
+ * is null once the creator's account was purged; the handle is the snapshot
347
+ * frozen at grant time.
348
+ */
349
+ export interface I_AgencyPastCreator {
350
+ grantId: string;
351
+ creator: {
352
+ id: string | null;
353
+ handle: string;
354
+ displayName?: string;
355
+ };
356
+ endedAt: string;
357
+ endedBy: CreatorAccessGrantRevokedBy;
358
+ coveredUntil?: string;
359
+ }
360
+ /**
361
+ * Everything the owner's billing section and panel show (FR-013, FR-013b).
362
+ *
363
+ * `seats` is the ONE home of the billed quantity: the count of active grants.
364
+ * Covering a creator never changes it (clarify 2026-09-17), so nothing here
365
+ * derives a price from `covered`.
366
+ *
367
+ * `price` is null when the seat product is not configured in this deployment
368
+ * (T031, the money gate); the screen says so rather than inventing a number.
369
+ * `nextInvoice` is Stripe's own preview, so a coupon is already applied to it.
370
+ */
371
+ export interface I_AgencyBilling {
372
+ subscriptionStatus: AgencySubscriptionStatus;
373
+ /** Creators with an active grant. */
374
+ seats: number;
375
+ /**
376
+ * What Stripe bills: `max(1, seats)`. An agency with no creators yet still
377
+ * pays for one seat, and the screen must say the number Stripe charges.
378
+ */
379
+ billedSeats: number;
380
+ price: {
381
+ amount: number;
382
+ currency: string;
383
+ interval: string;
384
+ } | null;
385
+ currentPeriodEnd?: string;
386
+ cancelAtPeriodEnd?: boolean;
387
+ nextInvoice?: {
388
+ amount: number;
389
+ currency: string;
390
+ date: string;
391
+ };
392
+ discount?: {
393
+ name?: string;
394
+ percentOff?: number;
395
+ amountOff?: number;
396
+ };
397
+ paymentMethod?: I_AgencyPaymentMethod;
398
+ billingDetails?: {
399
+ name?: string;
400
+ email?: string;
401
+ country?: string;
402
+ taxId?: string;
403
+ };
404
+ invoices: I_AgencyInvoice[];
405
+ covered: I_AgencyCoveredCreator[];
406
+ pastCreators: I_AgencyPastCreator[];
407
+ }
408
+ /**
409
+ * Every DTO below carries class-validator decorators and a constructor that
410
+ * survives being called with nothing.
411
+ *
412
+ * Both are load-bearing and were missing until 2026-09-22. The backend mounts
413
+ * a global `ValidationPipe`, which validates the decorated metadata of the
414
+ * class a handler's `@Body()` is typed with — an undecorated class, or a body
415
+ * typed as a plain interface, is simply passed through unchecked. And the pipe
416
+ * instantiates the class with NO arguments, so a constructor that reads
417
+ * `data.name` would throw a TypeError on every request rather than validate
418
+ * anything. The rest of this library's DTOs take `data?: Partial<T>` for that
419
+ * reason; these now do too.
420
+ */
206
421
  export declare class CreateAgencyOrganizationDto {
207
422
  name: string;
423
+ /** Rendered as a link on the admin screen, so the protocol is checked here. */
208
424
  website?: string;
209
425
  registryId: string;
426
+ /** ISO-3166 alpha-2: which register a reviewer should look in. */
210
427
  registryCountry: string;
211
428
  contactEmail: string;
212
- constructor(data: CreateAgencyOrganizationDto);
429
+ constructor(data?: Partial<CreateAgencyOrganizationDto>);
213
430
  }
214
431
  export declare class InviteAgencyMemberDto {
215
432
  email: string;
216
- constructor(data: InviteAgencyMemberDto);
433
+ constructor(data?: Partial<InviteAgencyMemberDto>);
217
434
  }
218
435
  export declare class RedeemAgencyInviteDto {
219
436
  token: string;
220
- constructor(data: RedeemAgencyInviteDto);
437
+ constructor(data?: Partial<RedeemAgencyInviteDto>);
221
438
  }
222
439
  export declare class AcceptAgencyMemberInviteDto {
223
440
  token: string;
224
- constructor(data: AcceptAgencyMemberInviteDto);
441
+ constructor(data?: Partial<AcceptAgencyMemberInviteDto>);
225
442
  }
226
443
  /**
227
444
  * Accepting consent. Either arm identifies the grant being answered; `scopes`
@@ -230,31 +447,74 @@ export declare class AcceptAgencyMemberInviteDto {
230
447
  export declare class AcceptGrantDto {
231
448
  token?: string;
232
449
  grantId?: string;
450
+ /** A subset of the three reads. Never empty: sharing nothing is declining. */
233
451
  scopes?: I_AgencyGrantScope[];
234
- constructor(data: AcceptGrantDto);
452
+ constructor(data?: Partial<AcceptGrantDto>);
235
453
  }
236
454
  /** The creator narrowing or widening a live grant — the only other write path. */
237
455
  export declare class UpdateGrantScopesDto {
238
456
  scopes: I_AgencyGrantScope[];
239
- constructor(data: UpdateGrantScopesDto);
457
+ constructor(data?: Partial<UpdateGrantScopesDto>);
240
458
  }
241
459
  export declare class SponsorCreatorDto {
242
460
  tier: SponsoredTier;
243
- constructor(data: SponsorCreatorDto);
461
+ constructor(data?: Partial<SponsorCreatorDto>);
244
462
  }
245
463
  export declare class AgencyDiscoverQueryDto {
246
464
  /** An EXACT platform username or e-mail. Never recorded in analytics (FR-015). */
247
465
  q: string;
248
- constructor(data: AgencyDiscoverQueryDto);
466
+ constructor(data?: Partial<AgencyDiscoverQueryDto>);
249
467
  }
250
468
  export declare class AgencyAccessRequestDto {
469
+ /**
470
+ * A user id: a UUID or a Firebase uid (letters, digits, hyphens). It was
471
+ * `@IsUUID()` until US5 was built, which refused most real creators — their
472
+ * ids are Firebase uids.
473
+ */
251
474
  userId: string;
252
475
  scopes: I_AgencyGrantScope[];
253
- constructor(data: AgencyAccessRequestDto);
476
+ constructor(data?: Partial<AgencyAccessRequestDto>);
477
+ }
478
+ /** `PATCH /users/me/agency-discoverable` — "Let agencies find me" (FR-015). */
479
+ export declare class UpdateAgencyDiscoverableDto {
480
+ discoverable: boolean;
481
+ /**
482
+ * NO default: a required boolean that defaults to false lets an empty body
483
+ * through validation and reach the database as `undefined` (review M7).
484
+ */
485
+ constructor(data?: Partial<UpdateAgencyDiscoverableDto>);
486
+ }
487
+ /**
488
+ * One discover hit: the public handle and the platforms, NEVER a metric and
489
+ * never a grant flag, so the payload cannot tell a caller whether it already
490
+ * holds the creator (FR-015). `userId` is what the request is sent for.
491
+ */
492
+ export interface I_AgencyDiscoverHit {
493
+ userId: string;
494
+ handle: string;
495
+ platforms: PublishPlatform[];
496
+ /**
497
+ * The platform whose username matched. Absent on an e-mail match. Two hits
498
+ * with the same handle are two different people (@mark on YouTube, another
499
+ * @mark on Instagram), and this is what tells them apart (T068).
500
+ */
501
+ matchedPlatform?: PublishPlatform;
502
+ }
503
+ /**
504
+ * `POST /agency/roster/discover` — the SAME shape and status for every case:
505
+ * discoverable exact matches, a non-discoverable account, a partial match and
506
+ * no account at all (SC-009). A miss is `{ results: [] }`, never a 404. An
507
+ * exact handle held by several findable creators returns each of them, one
508
+ * row per person (Maciej, 2026-09-26, T068); an e-mail matches one person.
509
+ */
510
+ export interface I_AgencyDiscoverResult {
511
+ results: I_AgencyDiscoverHit[];
254
512
  }
255
513
  export declare class AdminSetOrganizationStatusDto {
256
514
  status: Extract<AgencyOrganizationStatus, 'approved' | 'rejected'>;
257
- /** Required on reject — a rejected applicant is shown this (FR-002). */
515
+ /** Required on reject — a rejected applicant is shown this (FR-002). The
516
+ * "required when rejecting" half is a rule about the PAIR, so it stays in
517
+ * the service where both fields are in hand. */
258
518
  reason?: string;
259
- constructor(data: AdminSetOrganizationStatusDto);
519
+ constructor(data?: Partial<AdminSetOrganizationStatusDto>);
260
520
  }