ad2app-lib 1.43.0 → 1.44.1

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.
@@ -9,6 +9,7 @@
9
9
  * Do NOT rename events after they ship — historical data does not migrate.
10
10
  */
11
11
  import type { AccessDenialCode } from "../types/I_AccessDenial";
12
+ import type { SponsoredTier } from "../types/agency";
12
13
  /** Canonical PostHog event names. */
13
14
  export declare const EVENTS: {
14
15
  readonly LANDING_CTA_CLICKED: "landing_cta_clicked";
@@ -90,6 +91,21 @@ export declare const EVENTS: {
90
91
  readonly NATIVE_SESSION_LAUNCHED: "native_session_launched";
91
92
  readonly NATIVE_SESSION_RENEWED: "native_session_renewed";
92
93
  readonly NATIVE_SESSION_ENDED: "native_session_ended";
94
+ readonly AGENCY_SIGNUP_SUBMITTED: "agency_signup_submitted";
95
+ readonly AGENCY_APPROVED: "agency_approved";
96
+ readonly AGENCY_REJECTED: "agency_rejected";
97
+ readonly AGENCY_MEMBER_INVITED: "agency_member_invited";
98
+ readonly AGENCY_INVITE_LINK_ROTATED: "agency_invite_link_rotated";
99
+ readonly AGENCY_INVITE_OPENED: "agency_invite_opened";
100
+ readonly AGENCY_CONSENT_ACCEPTED: "agency_consent_accepted";
101
+ readonly AGENCY_CONSENT_DECLINED: "agency_consent_declined";
102
+ readonly AGENCY_GRANT_REVOKED: "agency_grant_revoked";
103
+ readonly AGENCY_ROSTER_VIEWED: "agency_roster_viewed";
104
+ readonly AGENCY_CREATOR_VIEWED: "agency_creator_viewed";
105
+ readonly AGENCY_SPONSORSHIP_STARTED: "agency_sponsorship_started";
106
+ readonly AGENCY_SPONSORSHIP_ENDED: "agency_sponsorship_ended";
107
+ readonly AGENCY_DISCOVER_SEARCHED: "agency_discover_searched";
108
+ readonly AGENCY_ACCESS_REQUESTED: "agency_access_requested";
93
109
  };
94
110
  export type EventName = (typeof EVENTS)[keyof typeof EVENTS];
95
111
  /** Which email program a send belongs to (spec 120). Wire values — do not rename. */
@@ -215,6 +231,28 @@ export type SignInFailureReason = 'canceled' | 'missing_code' | 'invalid_provide
215
231
  export declare const SIGN_IN_FAILURE_REASONS: readonly ["canceled", "missing_code", "invalid_provider", "no_email", "api_failed", "exception"];
216
232
  export type PublishFailureReason = 'auth' | 'rate_limit' | 'media' | 'content' | 'network' | 'platform' | 'unknown';
217
233
  /** Property shape per event. Keeps emitters honest across repos. */
234
+ /**
235
+ * The shared shape every agency event carries (spec 175, contract lib-types.md).
236
+ *
237
+ * `organization_id` is mandatory because every agency event is an act BY an
238
+ * organization — an agency event without one cannot be attributed, and the
239
+ * whole point of the set is reading adoption per agency after launch (FR-018).
240
+ */
241
+ export interface AgencyEventProperties {
242
+ organization_id: string;
243
+ /** Present once a specific consent is the subject of the act. */
244
+ grant_id?: string;
245
+ actor_role: 'owner' | 'member' | 'creator' | 'admin' | 'system';
246
+ origin?: 'invite_link' | 'agency_request';
247
+ terms_version?: string;
248
+ tier?: SponsoredTier;
249
+ /**
250
+ * The creator the act concerns, as a PROPERTY. Never the distinct id an
251
+ * agency-side event is captured under (audit F17): an agency browsing its
252
+ * roster must not write events into its creators' own timelines.
253
+ */
254
+ creator_user_id?: string;
255
+ }
218
256
  export interface EventProperties {
219
257
  [EVENTS.LANDING_CTA_CLICKED]: {
220
258
  location: 'hero' | 'pricing' | 'final_cta' | 'nav' | 'ai_connector' | 'free_skills';
@@ -529,6 +567,23 @@ export interface EventProperties {
529
567
  [EVENTS.NATIVE_SESSION_ENDED]: {
530
568
  cause: 'rejected' | 'sign_out';
531
569
  };
570
+ [EVENTS.AGENCY_SIGNUP_SUBMITTED]: AgencyEventProperties;
571
+ [EVENTS.AGENCY_APPROVED]: AgencyEventProperties;
572
+ [EVENTS.AGENCY_REJECTED]: AgencyEventProperties;
573
+ [EVENTS.AGENCY_MEMBER_INVITED]: AgencyEventProperties;
574
+ [EVENTS.AGENCY_INVITE_LINK_ROTATED]: AgencyEventProperties;
575
+ [EVENTS.AGENCY_INVITE_OPENED]: AgencyEventProperties;
576
+ [EVENTS.AGENCY_CONSENT_ACCEPTED]: AgencyEventProperties;
577
+ [EVENTS.AGENCY_CONSENT_DECLINED]: AgencyEventProperties;
578
+ [EVENTS.AGENCY_GRANT_REVOKED]: AgencyEventProperties;
579
+ [EVENTS.AGENCY_ROSTER_VIEWED]: AgencyEventProperties;
580
+ [EVENTS.AGENCY_CREATOR_VIEWED]: AgencyEventProperties;
581
+ [EVENTS.AGENCY_SPONSORSHIP_STARTED]: AgencyEventProperties;
582
+ [EVENTS.AGENCY_SPONSORSHIP_ENDED]: AgencyEventProperties;
583
+ [EVENTS.AGENCY_DISCOVER_SEARCHED]: AgencyEventProperties & {
584
+ matched: boolean;
585
+ };
586
+ [EVENTS.AGENCY_ACCESS_REQUESTED]: AgencyEventProperties;
532
587
  }
533
588
  /** Canonical person property keys (set via identify / $set). */
534
589
  export declare const PERSON_PROPS: {
@@ -155,6 +155,28 @@ exports.EVENTS = {
155
155
  NATIVE_SESSION_LAUNCHED: 'native_session_launched',
156
156
  NATIVE_SESSION_RENEWED: 'native_session_renewed',
157
157
  NATIVE_SESSION_ENDED: 'native_session_ended',
158
+ // Agency domain (175). Every one of these is captured under the acting
159
+ // person's own distinct id: agency-side events under the MEMBER's, with
160
+ // creator_user_id as a property, never under the creator's (audit F17) --
161
+ // otherwise an agency's activity would rewrite the creator's own timeline.
162
+ AGENCY_SIGNUP_SUBMITTED: 'agency_signup_submitted',
163
+ AGENCY_APPROVED: 'agency_approved', // server-owned (backend)
164
+ AGENCY_REJECTED: 'agency_rejected', // server-owned (backend)
165
+ AGENCY_MEMBER_INVITED: 'agency_member_invited',
166
+ AGENCY_INVITE_LINK_ROTATED: 'agency_invite_link_rotated',
167
+ AGENCY_INVITE_OPENED: 'agency_invite_opened',
168
+ AGENCY_CONSENT_ACCEPTED: 'agency_consent_accepted',
169
+ AGENCY_CONSENT_DECLINED: 'agency_consent_declined',
170
+ AGENCY_GRANT_REVOKED: 'agency_grant_revoked',
171
+ AGENCY_ROSTER_VIEWED: 'agency_roster_viewed',
172
+ AGENCY_CREATOR_VIEWED: 'agency_creator_viewed',
173
+ AGENCY_SPONSORSHIP_STARTED: 'agency_sponsorship_started',
174
+ AGENCY_SPONSORSHIP_ENDED: 'agency_sponsorship_ended',
175
+ // Carries `matched` and NEVER the query (FR-015, audit F17): the search term
176
+ // is the one field that would turn product analytics into a record of which
177
+ // creators an agency went looking for.
178
+ AGENCY_DISCOVER_SEARCHED: 'agency_discover_searched',
179
+ AGENCY_ACCESS_REQUESTED: 'agency_access_requested',
158
180
  };
159
181
  /**
160
182
  * Canonical email event property KEYS (spec 120 T001). Emitters and tests
@@ -24,6 +24,61 @@ export declare const ACCESS_DENIAL_CODES: {
24
24
  * for this one and do NOT redirect.
25
25
  */
26
26
  readonly ENTITLEMENT_PRECONDITION: "entitlement-precondition";
27
+ /**
28
+ * The caller is not an admin.
29
+ *
30
+ * `RoleGuard` answered a 401 with Polish prose until 2026-09-21, which the
31
+ * frontend's session handling reads as an expired session — so a non-admin
32
+ * who opened an admin screen could be signed out rather than refused. A 403
33
+ * with a code says "you may not", which is the true statement.
34
+ */
35
+ readonly ADMIN_REQUIRED: "admin-required";
36
+ /** The caller holds no accepted membership in any organization. */
37
+ readonly AGENCY_NOT_MEMBER: "agency-not-member";
38
+ /** Their organization is still awaiting a human decision (FR-002). */
39
+ readonly AGENCY_PENDING: "agency-pending";
40
+ /** Their organization was rejected; the reason travels in the body. */
41
+ readonly AGENCY_REJECTED: "agency-rejected";
42
+ /** No live subscription: the roster is dark until the plan is paid (FR-013a). */
43
+ readonly AGENCY_UNPAID: "agency-unpaid";
44
+ /**
45
+ * ONE code for EVERY "cannot see this creator" case — no grant, another
46
+ * organization's creator, a non-existent id, a non-discoverable account.
47
+ *
48
+ * The single code IS the privacy control (FR-011, SC-005, audit F6). Distinct
49
+ * codes would let an agency learn that an account exists by the shape of its
50
+ * refusal, which is the same existence oracle the discover query is
51
+ * structurally built to avoid.
52
+ */
53
+ readonly AGENCY_NO_ACCESS: "agency-no-access";
54
+ /**
55
+ * The grant exists but does not carry the scope this read needs.
56
+ *
57
+ * Returned ONLY for a creator the caller already holds on the roster — for
58
+ * anyone else it would prove a grant exists, so they get AGENCY_NO_ACCESS
59
+ * instead (SC-012).
60
+ */
61
+ readonly AGENCY_SCOPE_NOT_GRANTED: "agency-scope-not-granted";
62
+ /** The consent screen is off in this environment (FR-020, the legal gate). */
63
+ readonly AGENCY_CONSENT_DISABLED: "agency-consent-disabled";
64
+ /** The roster invite link expired, was rotated, or its organization is not approved. */
65
+ readonly AGENCY_LINK_INVALID: "agency-link-invalid";
66
+ /** An admin may not decide on an organization they belong to (FR-017). */
67
+ readonly AGENCY_ADMIN_SELF_APPROVAL: "agency-admin-self-approval";
68
+ /**
69
+ * A sole owner tried to leave or delete their account. Refused until they
70
+ * hand ownership over or close the organization explicitly (FR-004): closing
71
+ * carries a fan-out that cancels a subscription and ends sponsorships, and
72
+ * that must be a decision rather than a side effect.
73
+ */
74
+ readonly AGENCY_SOLE_OWNER_BLOCKED: "agency-sole-owner-blocked";
75
+ /** A member invitation token that is expired, already used, or revoked (FR-005). */
76
+ readonly AGENCY_MEMBER_INVITE_INVALID: "agency-member-invite-invalid";
77
+ /**
78
+ * The invited or accepting person already belongs to an organization (one
79
+ * per person, FR-004). Names the case and NOTHING else about that account.
80
+ */
81
+ readonly AGENCY_ALREADY_IN_ORGANIZATION: "agency-already-in-organization";
27
82
  };
28
83
  export type AccessDenialCode = (typeof ACCESS_DENIAL_CODES)[keyof typeof ACCESS_DENIAL_CODES];
29
84
  /**
@@ -27,6 +27,62 @@ exports.ACCESS_DENIAL_CODES = {
27
27
  * for this one and do NOT redirect.
28
28
  */
29
29
  ENTITLEMENT_PRECONDITION: "entitlement-precondition",
30
+ /**
31
+ * The caller is not an admin.
32
+ *
33
+ * `RoleGuard` answered a 401 with Polish prose until 2026-09-21, which the
34
+ * frontend's session handling reads as an expired session — so a non-admin
35
+ * who opened an admin screen could be signed out rather than refused. A 403
36
+ * with a code says "you may not", which is the true statement.
37
+ */
38
+ ADMIN_REQUIRED: "admin-required",
39
+ // ── Agency domain (spec 175) ──────────────────────────────────────────────
40
+ /** The caller holds no accepted membership in any organization. */
41
+ AGENCY_NOT_MEMBER: "agency-not-member",
42
+ /** Their organization is still awaiting a human decision (FR-002). */
43
+ AGENCY_PENDING: "agency-pending",
44
+ /** Their organization was rejected; the reason travels in the body. */
45
+ AGENCY_REJECTED: "agency-rejected",
46
+ /** No live subscription: the roster is dark until the plan is paid (FR-013a). */
47
+ AGENCY_UNPAID: "agency-unpaid",
48
+ /**
49
+ * ONE code for EVERY "cannot see this creator" case — no grant, another
50
+ * organization's creator, a non-existent id, a non-discoverable account.
51
+ *
52
+ * The single code IS the privacy control (FR-011, SC-005, audit F6). Distinct
53
+ * codes would let an agency learn that an account exists by the shape of its
54
+ * refusal, which is the same existence oracle the discover query is
55
+ * structurally built to avoid.
56
+ */
57
+ AGENCY_NO_ACCESS: "agency-no-access",
58
+ /**
59
+ * The grant exists but does not carry the scope this read needs.
60
+ *
61
+ * Returned ONLY for a creator the caller already holds on the roster — for
62
+ * anyone else it would prove a grant exists, so they get AGENCY_NO_ACCESS
63
+ * instead (SC-012).
64
+ */
65
+ AGENCY_SCOPE_NOT_GRANTED: "agency-scope-not-granted",
66
+ /** The consent screen is off in this environment (FR-020, the legal gate). */
67
+ AGENCY_CONSENT_DISABLED: "agency-consent-disabled",
68
+ /** The roster invite link expired, was rotated, or its organization is not approved. */
69
+ AGENCY_LINK_INVALID: "agency-link-invalid",
70
+ /** An admin may not decide on an organization they belong to (FR-017). */
71
+ AGENCY_ADMIN_SELF_APPROVAL: "agency-admin-self-approval",
72
+ /**
73
+ * A sole owner tried to leave or delete their account. Refused until they
74
+ * hand ownership over or close the organization explicitly (FR-004): closing
75
+ * carries a fan-out that cancels a subscription and ends sponsorships, and
76
+ * that must be a decision rather than a side effect.
77
+ */
78
+ AGENCY_SOLE_OWNER_BLOCKED: "agency-sole-owner-blocked",
79
+ /** A member invitation token that is expired, already used, or revoked (FR-005). */
80
+ AGENCY_MEMBER_INVITE_INVALID: "agency-member-invite-invalid",
81
+ /**
82
+ * The invited or accepting person already belongs to an organization (one
83
+ * per person, FR-004). Names the case and NOTHING else about that account.
84
+ */
85
+ AGENCY_ALREADY_IN_ORGANIZATION: "agency-already-in-organization",
30
86
  };
31
87
  /**
32
88
  * WHICH precondition the entitlement wall refused on (backend#305, spec 170).
@@ -0,0 +1,278 @@
1
+ /**
2
+ * Agency domain — organizations, memberships and creator access grants (spec 175).
3
+ *
4
+ * The shape behind "an agency sees its creators' numbers, because the creators
5
+ * said yes". Two doors, one product: a creator account is unchanged by any of
6
+ * this, and gains at most one grant.
7
+ *
8
+ * The one idea worth carrying into every consumer: a GRANT IS READ-ONLY AND
9
+ * CREATOR-OWNED. The agency asks for scopes; only a creator's own act ever
10
+ * writes `grantedScopes` (FR-008, SC-002). Nothing here can express a write on
11
+ * a creator's account, and that is deliberate — the absence is the feature.
12
+ *
13
+ * See specs/175-agency-roster/contracts/lib-types.md.
14
+ */
15
+ import type { PublishPlatform } from '../publish-limits/types';
16
+ /**
17
+ * Paths that BOTH repos have to agree on, with one home here.
18
+ *
19
+ * The backend mails this path; the frontend registers it. They deploy
20
+ * separately, so nothing else notices them drifting — and the first version of
21
+ * that link pointed at a route no repo served, which is how an entire user
22
+ * story shipped with its headline capability unreachable.
23
+ *
24
+ * A test in each repo asserts its own side against these constants. The first
25
+ * attempt at that guard read across the filesystem into a sibling checkout,
26
+ * which worked on one laptop and threw ENOENT in CI; a shared constant is the
27
+ * version that actually runs.
28
+ */
29
+ export declare const AGENCY_ROUTE_PATHS: {
30
+ /** A colleague accepting a team invitation (FR-005). */
31
+ readonly joinTeam: "/agency/join-team";
32
+ /** A creator consenting to an agency's access (FR-007). */
33
+ readonly join: "/agency/join";
34
+ /** A creator answering an access request (FR-016). */
35
+ readonly requests: "/agency/requests";
36
+ };
37
+ /**
38
+ * The complete set of reads a grant may carry. A CEILING, not a starting point.
39
+ *
40
+ * All three are reads of what the creator already sees on their own screens.
41
+ * Private messages, comment replies and moderation, publishing, connection
42
+ * changes and settings are absent by construction (FR-010) — there is no scope
43
+ * string that could name them, so a widened grant is a spec change rather than
44
+ * a config change.
45
+ */
46
+ export declare const AGENCY_GRANT_SCOPES: readonly ["analytics", "comments", "posts"];
47
+ export type I_AgencyGrantScope = (typeof AGENCY_GRANT_SCOPES)[number];
48
+ /**
49
+ * Narrowing guard — an unrecognised scope must fail closed.
50
+ *
51
+ * Scope strings arrive from request bodies (the agency's ask, the creator's
52
+ * answer), so this is a trust boundary, not a convenience.
53
+ */
54
+ export declare const isAgencyGrantScope: (value: string) => value is I_AgencyGrantScope;
55
+ /** `'brand'` arrives with spec 148; one column, no enum table. */
56
+ export type AgencyOrganizationType = 'agency';
57
+ /**
58
+ * `closed` was added by the 2026-09-21 requirements review (FR-004b).
59
+ *
60
+ * It exists so closure has an end state that is NOT a deleted row: grants are
61
+ * `on delete cascade` from the organization, so hard-deleting one would destroy
62
+ * the consent records FR-009 says are never deleted — and which are also the
63
+ * invoice basis.
64
+ */
65
+ export declare const AGENCY_ORGANIZATION_STATUSES: readonly ["pending", "approved", "rejected", "closed"];
66
+ export type AgencyOrganizationStatus = (typeof AGENCY_ORGANIZATION_STATUSES)[number];
67
+ /** Mirrored from Stripe. `comped` is a 100% coupon and is treated as active. */
68
+ export type AgencySubscriptionStatus = 'none' | 'active' | 'trialing' | 'past_due' | 'unpaid' | 'canceled' | 'comped';
69
+ /** Two roles, no more (FR-004). Only the owner reaches billing, members and the link. */
70
+ export type AgencyMemberRole = 'owner' | 'member';
71
+ export type SponsoredTier = 'starter' | 'pro';
72
+ export interface I_AgencyOrganization {
73
+ id: string;
74
+ type: AgencyOrganizationType;
75
+ name: string;
76
+ website?: string;
77
+ /** NIP/KRS or a foreign equivalent — free text, verified by a human, never by code. */
78
+ registryId: string;
79
+ /** ISO-3166 alpha-2 of the register the identifier belongs to. */
80
+ registryCountry: string;
81
+ contactEmail: string;
82
+ status: AgencyOrganizationStatus;
83
+ /** The reason an admin gave; shown to a rejected applicant (FR-002). */
84
+ statusReason?: string;
85
+ subscriptionStatus: AgencySubscriptionStatus;
86
+ /** The date sponsorships run to when a grant is revoked or the plan lapses. */
87
+ currentPeriodEnd?: string;
88
+ createdAt: string;
89
+ }
90
+ export interface I_AgencyMember {
91
+ id: string;
92
+ organizationId: string;
93
+ /** Null while an e-mail invitation is outstanding — nobody has accepted yet. */
94
+ userId?: string;
95
+ invitedEmail: string;
96
+ role: AgencyMemberRole;
97
+ invitedAt: string;
98
+ /** "Who is in" is exactly `acceptedAt != null`. */
99
+ acceptedAt?: string;
100
+ revokedAt?: string;
101
+ }
102
+ /**
103
+ * The five states the data model pins.
104
+ *
105
+ * `declined` and `expired` belong to the US5 request path (FR-016) and were
106
+ * missing from the spec's own Key Entities until the 2026-09-21 review. There
107
+ * is no transition OUT of `revoked`, `declined` or `expired`: a new consent is
108
+ * a new row, so the record of what was agreed is never overwritten.
109
+ */
110
+ export declare const CREATOR_ACCESS_GRANT_STATES: readonly ["requested", "active", "declined", "expired", "revoked"];
111
+ export type CreatorAccessGrantState = (typeof CREATOR_ACCESS_GRANT_STATES)[number];
112
+ export type CreatorAccessGrantOrigin = 'invite_link' | 'agency_request';
113
+ /** Who ended it. Shown in the agency's past-creators history (FR-013b). */
114
+ export type CreatorAccessGrantRevokedBy = 'creator' | 'agency' | 'organization' | 'account-deleted' | 'purge' | 'system';
115
+ export interface I_CreatorAccessGrant {
116
+ id: string;
117
+ /**
118
+ * Null once the creator's user row is hard-purged (audit D-D): the grant
119
+ * survives as the consent record, with `creatorIdentity` standing in for the
120
+ * person who is no longer there.
121
+ */
122
+ creatorUserId?: string;
123
+ organizationId: string;
124
+ organizationName: string;
125
+ /** What the agency asked for. */
126
+ requestedScopes: I_AgencyGrantScope[];
127
+ /** What the creator actually granted — a subset, possibly smaller, never larger. */
128
+ grantedScopes: I_AgencyGrantScope[];
129
+ /** Frozen at grant time; what the history shows after a purge. */
130
+ creatorIdentity: {
131
+ handle: string;
132
+ displayName?: string;
133
+ };
134
+ state: CreatorAccessGrantState;
135
+ origin: CreatorAccessGrantOrigin;
136
+ requestedAt?: string;
137
+ expiresAt?: string;
138
+ grantedAt?: string;
139
+ /** Which terms the creator accepted. A `draft-` prefix means pre-addendum (FR-020). */
140
+ termsVersion?: string;
141
+ revokedAt?: string;
142
+ revokedBy?: CreatorAccessGrantRevokedBy;
143
+ /**
144
+ * The agency covering this creator's plan. `until` is set when the
145
+ * sponsorship is ending — it runs to the close of the period already paid
146
+ * for, so a revoke never silently drops the creator's plan (FR-014).
147
+ */
148
+ sponsorship?: {
149
+ tier: SponsoredTier;
150
+ since: string;
151
+ until?: string;
152
+ };
153
+ /** Which platforms the grant reaches right now — derived at read time, never stored. */
154
+ platformsReached: PublishPlatform[];
155
+ }
156
+ /**
157
+ * Roster row states, DECLARED IN PRECEDENCE ORDER (data-model §Derived).
158
+ *
159
+ * A row holds exactly one state: the first of these that applies, most-blocking
160
+ * first. `revoked` is deliberately absent — a revoked creator leaves the roster
161
+ * entirely for the settings history (FR-011).
162
+ */
163
+ export declare const AGENCY_ROSTER_ROW_STATES: readonly ["requested", "lapsed", "disconnected", "stale", "active"];
164
+ export type AgencyRosterRowState = (typeof AGENCY_ROSTER_ROW_STATES)[number];
165
+ /**
166
+ * What "needs attention" means — ONE home for the KPI tile, the roster segment
167
+ * and the rail badge, so the three can never drift apart (FR-011).
168
+ *
169
+ * `requested` is excluded on purpose: it waits on the CREATOR, and FR-004a says
170
+ * the badge means something waits on the agency.
171
+ */
172
+ export declare const AGENCY_ROSTER_NEEDS_ATTENTION_STATES: readonly ["lapsed", "disconnected", "stale"];
173
+ export type AgencyRosterNeedsAttentionState = (typeof AGENCY_ROSTER_NEEDS_ATTENTION_STATES)[number];
174
+ export interface I_AgencyRosterRow {
175
+ grantId: string;
176
+ creator: {
177
+ id: string;
178
+ handle: string;
179
+ };
180
+ platforms: PublishPlatform[];
181
+ state: AgencyRosterRowState;
182
+ grantedScopes: I_AgencyGrantScope[];
183
+ lastSyncAt?: string;
184
+ }
185
+ export interface I_AgencyRoster {
186
+ totals: {
187
+ creators: number;
188
+ 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;
194
+ /** Count of rows in AGENCY_ROSTER_NEEDS_ATTENTION_STATES. */
195
+ needsAttention: number;
196
+ };
197
+ items: I_AgencyRosterRow[];
198
+ }
199
+ /** What a sponsored creator sees on their OWN subscription screen (FR-014). */
200
+ export interface I_SponsoredBy {
201
+ organizationName: string;
202
+ tier: SponsoredTier;
203
+ /** Present once the sponsorship is ending: the date their plan runs to. */
204
+ until?: string;
205
+ }
206
+ /**
207
+ * Every DTO below carries class-validator decorators and a constructor that
208
+ * survives being called with nothing.
209
+ *
210
+ * Both are load-bearing and were missing until 2026-09-22. The backend mounts
211
+ * a global `ValidationPipe`, which validates the decorated metadata of the
212
+ * class a handler's `@Body()` is typed with — an undecorated class, or a body
213
+ * typed as a plain interface, is simply passed through unchecked. And the pipe
214
+ * instantiates the class with NO arguments, so a constructor that reads
215
+ * `data.name` would throw a TypeError on every request rather than validate
216
+ * anything. The rest of this library's DTOs take `data?: Partial<T>` for that
217
+ * reason; these now do too.
218
+ */
219
+ export declare class CreateAgencyOrganizationDto {
220
+ name: string;
221
+ /** Rendered as a link on the admin screen, so the protocol is checked here. */
222
+ website?: string;
223
+ registryId: string;
224
+ /** ISO-3166 alpha-2: which register a reviewer should look in. */
225
+ registryCountry: string;
226
+ contactEmail: string;
227
+ constructor(data?: Partial<CreateAgencyOrganizationDto>);
228
+ }
229
+ export declare class InviteAgencyMemberDto {
230
+ email: string;
231
+ constructor(data?: Partial<InviteAgencyMemberDto>);
232
+ }
233
+ export declare class RedeemAgencyInviteDto {
234
+ token: string;
235
+ constructor(data?: Partial<RedeemAgencyInviteDto>);
236
+ }
237
+ export declare class AcceptAgencyMemberInviteDto {
238
+ token: string;
239
+ constructor(data?: Partial<AcceptAgencyMemberInviteDto>);
240
+ }
241
+ /**
242
+ * Accepting consent. Either arm identifies the grant being answered; `scopes`
243
+ * is the creator's answer and defaults to the requested set when absent.
244
+ */
245
+ export declare class AcceptGrantDto {
246
+ token?: string;
247
+ grantId?: string;
248
+ /** A subset of the three reads. Never empty: sharing nothing is declining. */
249
+ scopes?: I_AgencyGrantScope[];
250
+ constructor(data?: Partial<AcceptGrantDto>);
251
+ }
252
+ /** The creator narrowing or widening a live grant — the only other write path. */
253
+ export declare class UpdateGrantScopesDto {
254
+ scopes: I_AgencyGrantScope[];
255
+ constructor(data?: Partial<UpdateGrantScopesDto>);
256
+ }
257
+ export declare class SponsorCreatorDto {
258
+ tier: SponsoredTier;
259
+ constructor(data?: Partial<SponsorCreatorDto>);
260
+ }
261
+ export declare class AgencyDiscoverQueryDto {
262
+ /** An EXACT platform username or e-mail. Never recorded in analytics (FR-015). */
263
+ q: string;
264
+ constructor(data?: Partial<AgencyDiscoverQueryDto>);
265
+ }
266
+ export declare class AgencyAccessRequestDto {
267
+ userId: string;
268
+ scopes: I_AgencyGrantScope[];
269
+ constructor(data?: Partial<AgencyAccessRequestDto>);
270
+ }
271
+ export declare class AdminSetOrganizationStatusDto {
272
+ status: Extract<AgencyOrganizationStatus, 'approved' | 'rejected'>;
273
+ /** Required on reject — a rejected applicant is shown this (FR-002). The
274
+ * "required when rejecting" half is a rule about the PAIR, so it stays in
275
+ * the service where both fields are in hand. */
276
+ reason?: string;
277
+ constructor(data?: Partial<AdminSetOrganizationStatusDto>);
278
+ }