@consentera/consent-sdk 2.0.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/CHANGELOG.md +245 -0
- package/LICENSE +21 -0
- package/README.md +489 -0
- package/dist/consentera-consent.cjs +4919 -0
- package/dist/consentera-consent.cjs.map +1 -0
- package/dist/consentera-consent.min.js +2 -0
- package/dist/consentera-consent.min.js.map +1 -0
- package/dist/consentera-consent.mjs +4864 -0
- package/dist/consentera-consent.mjs.map +1 -0
- package/dist/react/index.cjs +2731 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.mjs +2724 -0
- package/dist/react/index.mjs.map +1 -0
- package/dist/types/consent/CallbackHandler.d.ts +246 -0
- package/dist/types/consent/ConsentManager.d.ts +128 -0
- package/dist/types/consent/ConsentSession.d.ts +127 -0
- package/dist/types/consent/ConsentValidator.d.ts +63 -0
- package/dist/types/consent/artifactRead.d.ts +48 -0
- package/dist/types/consent/consentPopup.d.ts +115 -0
- package/dist/types/core/ConsentEraClient.d.ts +106 -0
- package/dist/types/core/ConsenteraConsent.d.ts +163 -0
- package/dist/types/core/errors.d.ts +108 -0
- package/dist/types/core/http.d.ts +176 -0
- package/dist/types/core/version.d.ts +36 -0
- package/dist/types/df/DFConfigClient.d.ts +59 -0
- package/dist/types/gcm/ConsentModeBridge.d.ts +54 -0
- package/dist/types/gpp/GPPManager.d.ts +62 -0
- package/dist/types/index.d.mts +5 -0
- package/dist/types/index.d.ts +28 -0
- package/dist/types/principal/PrincipalClient.d.ts +34 -0
- package/dist/types/react/ConsentEraProvider.d.ts +58 -0
- package/dist/types/react/ConsentGate.d.ts +40 -0
- package/dist/types/react/index.d.mts +4 -0
- package/dist/types/react/index.d.ts +10 -0
- package/dist/types/react/useConsentEra.d.ts +65 -0
- package/dist/types/react/useConsentValidation.d.ts +23 -0
- package/dist/types/storage/ConsentStorage.d.ts +39 -0
- package/dist/types/tcf/TCFManager.d.ts +46 -0
- package/dist/types/types/consent-lifecycle.d.ts +804 -0
- package/dist/types/types/index.d.ts +311 -0
- package/dist/types/ui/ConsentBanner.d.ts +22 -0
- package/dist/types/ui/PreferenceCenter.d.ts +24 -0
- package/dist/types/utils/EventEmitter.d.ts +32 -0
- package/dist/types/utils/Logger.d.ts +16 -0
- package/dist/types/utils/browserStorage.d.ts +35 -0
- package/dist/types/utils/context.d.ts +81 -0
- package/dist/types/utils/helpers.d.ts +48 -0
- package/package.json +132 -0
|
@@ -0,0 +1,804 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ConsentEra Consent SDK — Consent Lifecycle Types
|
|
3
|
+
* Types matching the ConsentEra Go API request/response shapes
|
|
4
|
+
*/
|
|
5
|
+
import type { RetryPolicy } from '../core/http';
|
|
6
|
+
import type { ContextMode } from '../utils/context';
|
|
7
|
+
export interface ConsentEraClientConfig {
|
|
8
|
+
/** Secret API key for server-side use (tiq_live_*). Do NOT embed in frontend code. */
|
|
9
|
+
apiKey?: string;
|
|
10
|
+
/** Public site key for frontend use (tiq_pub_*). Safe to embed in client-side code. */
|
|
11
|
+
siteKey?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Your tenant id. REQUIRED with `apiEndpoint` (direct mode), where it is sent
|
|
14
|
+
* as X-Tenant-Id. OPTIONAL with `proxyEndpoint`: your server holds the key and
|
|
15
|
+
* the key names the tenant, and the SDK sends no tenant header through a proxy
|
|
16
|
+
* (SDK register WEB-036).
|
|
17
|
+
*/
|
|
18
|
+
tenantId?: string;
|
|
19
|
+
/** ConsentEra API base URL (for direct mode, e.g. 'http://localhost:8080') */
|
|
20
|
+
apiEndpoint?: string;
|
|
21
|
+
/** Proxy endpoint (for proxy mode, e.g. '/api/consentera') — API key injected server-side */
|
|
22
|
+
proxyEndpoint?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Absolute URL to redirect back to after consent collection. createSession
|
|
25
|
+
* adds this SDK's own `state` to it (the callback binding), so `state` is
|
|
26
|
+
* reserved: a URL that already carries one is refused.
|
|
27
|
+
*/
|
|
28
|
+
callbackUrl?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Default language for the consent session. It is sent as the wire field
|
|
31
|
+
* `language`; when it is absent NOTHING is sent, which means the TENANT'S
|
|
32
|
+
* own default language.
|
|
33
|
+
*
|
|
34
|
+
* THE WIRE NAME IS `language` SINCE U58. It was `locale_pref`, and
|
|
35
|
+
* `notice_language` and `template_language` stood beside it — three fields
|
|
36
|
+
* for one question. The API decodes exactly one now
|
|
37
|
+
* (`core/dpintake/dpintake.go:109`) and the other three are deleted from the
|
|
38
|
+
* request struct. A caller still sending them has them dropped in silence,
|
|
39
|
+
* because the handler decodes without `DisallowUnknownFields`.
|
|
40
|
+
*/
|
|
41
|
+
language?: string;
|
|
42
|
+
/** Enable debug logging. Never logs a request or response BODY — see core/http.ts. */
|
|
43
|
+
debug?: boolean;
|
|
44
|
+
/** Custom headers to include in every request. Can be a static object or a function for dynamic values. */
|
|
45
|
+
customHeaders?: Record<string, string> | (() => Record<string, string>);
|
|
46
|
+
/**
|
|
47
|
+
* Allow a SECRET `apiKey` although a `window` exists. Refused by default —
|
|
48
|
+
* see the header of core/ConsentEraClient.ts. Every request then warns.
|
|
49
|
+
*/
|
|
50
|
+
unsafeAllowSecretKeyInBrowser?: boolean;
|
|
51
|
+
/** Per-request deadline in ms. Default 10000. */
|
|
52
|
+
timeoutMs?: number;
|
|
53
|
+
/** Retry policy: attempts (default 3), baseDelayMs (250), maxDelayMs (4000). */
|
|
54
|
+
retry?: Partial<RetryPolicy>;
|
|
55
|
+
/**
|
|
56
|
+
* How much browser environment travels with a consent mutation.
|
|
57
|
+
* Default `minimal`; 1.x behaved as `full` with no way to turn it off.
|
|
58
|
+
*/
|
|
59
|
+
collectContext?: ContextMode;
|
|
60
|
+
/**
|
|
61
|
+
* Last look at every request body before it leaves. Return the body to send;
|
|
62
|
+
* return null to refuse the call (which raises, never sends nothing quietly).
|
|
63
|
+
*/
|
|
64
|
+
beforeSend?: (req: {
|
|
65
|
+
method: string;
|
|
66
|
+
path: string;
|
|
67
|
+
body: unknown;
|
|
68
|
+
}) => unknown | null;
|
|
69
|
+
/** Inject a fetch implementation (tests, a polyfill, a traced fetch). */
|
|
70
|
+
fetchImpl?: typeof fetch;
|
|
71
|
+
/**
|
|
72
|
+
* Verify the platform's `sig` on a consent callback. The signing key is the
|
|
73
|
+
* DF's callback_signing_secret and must NOT be in a browser, so in a browser
|
|
74
|
+
* this calls your own server. See {@link CallbackSignatureVerifier}.
|
|
75
|
+
*/
|
|
76
|
+
verifyCallbackSignature?: import('../consent/CallbackHandler').CallbackSignatureVerifier;
|
|
77
|
+
/**
|
|
78
|
+
* How long the callback road waits for the consent artifact to become
|
|
79
|
+
* readable. It is written asynchronously after submit (5.2–10.8s measured),
|
|
80
|
+
* so a read at callback time 404s. Default 15000; 0 disables the wait and
|
|
81
|
+
* returns `pending` at once.
|
|
82
|
+
*/
|
|
83
|
+
artifactWaitMs?: number;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* `data_principal` — the person's identifiers, KEYED BY THE FIELD NAMES OF THE
|
|
87
|
+
* TENANT'S LOCKED INTEGRATION KEY.
|
|
88
|
+
*
|
|
89
|
+
* A MAP AND NOT AN INTERFACE, and that is the point, not a shortcut. The
|
|
90
|
+
* admissible key set is a PER-TENANT fact the platform reads at request time:
|
|
91
|
+
* a tenant keyed on `{email, mobile}` accepts exactly `email` and `mobile`. A
|
|
92
|
+
* closed interface would have to name every identifier type the platform
|
|
93
|
+
* knows, and then every tenant's request object would carry every OTHER
|
|
94
|
+
* tenant's fields as optional — which is the second, unchecked identifier door
|
|
95
|
+
* U58 exists to delete. The server's own type is
|
|
96
|
+
* `map[string]string` (`core/dpintake/dpintake.go:82`).
|
|
97
|
+
*
|
|
98
|
+
* THE TYPE COMES FROM THE FIELD NAME. There is no declared type to disagree
|
|
99
|
+
* with the value and no shape to guess from, so `data_principal_ref_type` and
|
|
100
|
+
* the shape-guesser behind it are both gone. A vernacular spelling is NOT
|
|
101
|
+
* folded any more either: on a tenant keyed on `{email, mobile}`, `phone` is an
|
|
102
|
+
* UNKNOWN FIELD (400 `UNKNOWN_IDENTIFIER_FIELD`, which lists the allowed
|
|
103
|
+
* fields) rather than a spelling of `mobile`.
|
|
104
|
+
*
|
|
105
|
+
* { email: 'riya@example.in' }
|
|
106
|
+
* { customer_id: 'CUST-90210', mobile: '+919876500000' }
|
|
107
|
+
*/
|
|
108
|
+
export type DataPrincipal = Record<string, string>;
|
|
109
|
+
/**
|
|
110
|
+
* `age` — the ONE age signal, and it is a date.
|
|
111
|
+
*
|
|
112
|
+
* `is_minor`, `is_declared_adult` and `self_declared_adult` are deleted from
|
|
113
|
+
* this request, because a flag cannot graduate anyone at eighteen: a person
|
|
114
|
+
* marked `is_minor` in March is still marked one the following March, while a
|
|
115
|
+
* date of birth answers the question again every time it is asked. The date is
|
|
116
|
+
* SERVER-AUTHORITATIVE — the platform derives the age and is never told it
|
|
117
|
+
* (`core/dpintake/dpintake.go:92-94`).
|
|
118
|
+
*/
|
|
119
|
+
export interface Age {
|
|
120
|
+
/**
|
|
121
|
+
* `YYYY-MM-DD` and nothing else. Any other format, a future date, or an
|
|
122
|
+
* implausible one is refused 400 `INVALID_DATE_OF_BIRTH`.
|
|
123
|
+
*
|
|
124
|
+
* SEND IT. With no date the person's age is UNKNOWN — the session is still
|
|
125
|
+
* created and the response carries a warning, because the consequence is
|
|
126
|
+
* otherwise invisible: purposes restricted for children are refused, and if
|
|
127
|
+
* this person IS a child no parental consent can be sought.
|
|
128
|
+
*
|
|
129
|
+
* If the date IS a child's, the request must also carry a guardian channel
|
|
130
|
+
* (`guardian_email` or `guardian_phone`) or it is refused 412
|
|
131
|
+
* `GUARDIAN_REQUIRED`.
|
|
132
|
+
*/
|
|
133
|
+
date_of_birth?: string;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Session-create request — POST /api/v1/public/consent/sessions
|
|
137
|
+
*
|
|
138
|
+
* ─── U58 (IK-WIRE): THIS IS A PUBLIC WIRE BREAK ───────────────────────────
|
|
139
|
+
*
|
|
140
|
+
* The identity, age and language halves of this body are the platform's
|
|
141
|
+
* `core/dpintake.Object`, EMBEDDED in the request struct
|
|
142
|
+
* (`consent/collection.go:152`), so `data_principal`, `data_principal_id`,
|
|
143
|
+
* `age` and `language` decode at the TOP LEVEL. The same four fields identify
|
|
144
|
+
* a person on all four consent-collection roads — the hosted page, the kiosk,
|
|
145
|
+
* QR identify and this endpoint.
|
|
146
|
+
*
|
|
147
|
+
* WHAT WENT, AND WHY:
|
|
148
|
+
*
|
|
149
|
+
* data_principal_ref, data_principal_ref_type
|
|
150
|
+
* -> data_principal. THE TYPE COMES FROM THE FIELD NAME.
|
|
151
|
+
* data_principal_details.{email,phone,customer_id,aadhaar,pan}
|
|
152
|
+
* -> deleted. A second, unchecked identifier door: these became refs
|
|
153
|
+
* ALONGSIDE data_principal_ref, judged against no scheme.
|
|
154
|
+
* data_principal_details.name -> read by nothing.
|
|
155
|
+
* data_principal_details.guardian_ref -> names a DIFFERENT person; the
|
|
156
|
+
* guardian channel below replaces it.
|
|
157
|
+
* is_minor, is_declared_adult -> age.date_of_birth.
|
|
158
|
+
* locale_pref, notice_language, template_language -> language.
|
|
159
|
+
* purpose_ids -> deleted. The NOTICE decides the
|
|
160
|
+
* purposes (DPDP §5); the field bought only a fail-fast 400 on ids
|
|
161
|
+
* that were never going to be used.
|
|
162
|
+
*
|
|
163
|
+
* THERE IS NO OVERLAP WINDOW. The handler decodes with a plain `json.Decoder`
|
|
164
|
+
* and NO `DisallowUnknownFields` on both wires, so a body in the other shape is
|
|
165
|
+
* not refused for being the other shape — its identifiers are dropped without a
|
|
166
|
+
* word and the request is then refused for carrying none. This SDK version
|
|
167
|
+
* talks to an API carrying U58 and to no other.
|
|
168
|
+
*
|
|
169
|
+
* IDENTITY IS REQUIRED AND THE TYPE SAYS SO. Send `data_principal` (the
|
|
170
|
+
* identifiers) or `data_principal_id` (the platform's own uuid for the person,
|
|
171
|
+
* returned by every session create), or both — sent together they must agree,
|
|
172
|
+
* or the call is refused 409 `IDENTITY_MISMATCH`. A request carrying neither
|
|
173
|
+
* falls to the key's floor (400 `IDENTIFIER_REQUIRED`), so the union below
|
|
174
|
+
* makes that a COMPILE error instead of a round trip.
|
|
175
|
+
*/
|
|
176
|
+
export type CreateSessionRequest = CreateSessionRequestFields & ({
|
|
177
|
+
data_principal: DataPrincipal;
|
|
178
|
+
data_principal_id?: string;
|
|
179
|
+
} | {
|
|
180
|
+
data_principal?: DataPrincipal;
|
|
181
|
+
data_principal_id: string;
|
|
182
|
+
});
|
|
183
|
+
/** The non-identity half of {@link CreateSessionRequest}. */
|
|
184
|
+
export interface CreateSessionRequestFields {
|
|
185
|
+
/**
|
|
186
|
+
* The person's identifiers, by the field names of this organisation's locked
|
|
187
|
+
* integration key. See {@link DataPrincipal}.
|
|
188
|
+
*
|
|
189
|
+
* A field outside the key is refused 400 `UNKNOWN_IDENTIFIER_FIELD` and the
|
|
190
|
+
* message LISTS the allowed fields, because an integrator cannot read the key
|
|
191
|
+
* from outside. Too few of them is 400 `IDENTIFIER_REQUIRED`, whose message
|
|
192
|
+
* names the fields and never the mode.
|
|
193
|
+
*/
|
|
194
|
+
data_principal?: DataPrincipal;
|
|
195
|
+
/** The platform's own uuid for the person, from any previous CreateSessionResponse. */
|
|
196
|
+
data_principal_id?: string;
|
|
197
|
+
/** The ONE age signal. See {@link Age}. */
|
|
198
|
+
age?: Age;
|
|
199
|
+
/**
|
|
200
|
+
* Preferred language for the consent page, as an ISO code. It goes to the
|
|
201
|
+
* FRONT of the locale fallback chain, AHEAD of the tenant's own default — so
|
|
202
|
+
* send it only when a language was actually asked for. A language the
|
|
203
|
+
* platform does not serve is refused 400 `UNSUPPORTED_LANGUAGE`, and that
|
|
204
|
+
* message names the list.
|
|
205
|
+
*
|
|
206
|
+
* ONE FIELD. It replaces `locale_pref`, `notice_language` and
|
|
207
|
+
* `template_language` together.
|
|
208
|
+
*/
|
|
209
|
+
language?: string;
|
|
210
|
+
session_ref?: string;
|
|
211
|
+
notice_internal_name?: string;
|
|
212
|
+
/**
|
|
213
|
+
* Bind exactly this version NUMBER of the notice code, which must be active;
|
|
214
|
+
* omit to bind the code's default version.
|
|
215
|
+
*
|
|
216
|
+
* This field was `notice_version_id` (a UUID string) and the API has never
|
|
217
|
+
* had such a key: it decoded into nothing and was silently dropped, so every
|
|
218
|
+
* caller that set it silently got the default version and no error.
|
|
219
|
+
*/
|
|
220
|
+
notice_version_number?: number;
|
|
221
|
+
ui_mode?: 'redirect' | 'embedded_popup' | 'embedded_page' | 'api_only';
|
|
222
|
+
/** Absolute return URL. createSession adds its own `state` to it; `state` is reserved. */
|
|
223
|
+
callback_url?: string;
|
|
224
|
+
/** HMAC-signed token from a campaign SMS URL, linking this session to an existing customer record. */
|
|
225
|
+
customer_token?: string;
|
|
226
|
+
/** Where the guardian's verification request is sent. Not with `guardian_phone`. */
|
|
227
|
+
guardian_email?: string;
|
|
228
|
+
/** Where the guardian's verification request is sent. Not with `guardian_email`. */
|
|
229
|
+
guardian_phone?: string;
|
|
230
|
+
/**
|
|
231
|
+
* What the child SAYS the guardian is to them ('mother', 'father', 'legal
|
|
232
|
+
* guardian', …). Recorded as a CLAIM on the pending link and confirmed or
|
|
233
|
+
* corrected by the guardian themselves on the verification road; nothing
|
|
234
|
+
* downstream treats it as proven.
|
|
235
|
+
*
|
|
236
|
+
* There is no `guardian_name`: a name is a claim about a second person made
|
|
237
|
+
* by somebody else, held in the child's own record, with no way to reach them
|
|
238
|
+
* and nobody able to correct it.
|
|
239
|
+
*/
|
|
240
|
+
guardian_relationship?: string;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Session-create response.
|
|
244
|
+
*
|
|
245
|
+
* THE FIELD NAMES ARE NOT UNIFORM AND THAT IS THE WIRE, not a transcription
|
|
246
|
+
* error here: `expiresAt`, `languageCode` and `challengeNonce` are camelCase on
|
|
247
|
+
* the wire and the rest are snake_case. They are pinned that way in the API's
|
|
248
|
+
* own struct as an entrenched contract
|
|
249
|
+
* (`consent/collection.go:238-260`, `canonical-field:waive`).
|
|
250
|
+
*/
|
|
251
|
+
export interface CreateSessionResponse {
|
|
252
|
+
consent_session_id: string;
|
|
253
|
+
/** Always returned, so a caller can store it and send it next time. */
|
|
254
|
+
data_principal_id: string;
|
|
255
|
+
consent_url: string;
|
|
256
|
+
/** camelCase on the wire. */
|
|
257
|
+
expiresAt: string;
|
|
258
|
+
notice_version_id: string;
|
|
259
|
+
notice_hash: string;
|
|
260
|
+
/** The language the session actually resolved to. camelCase on the wire. */
|
|
261
|
+
languageCode: string;
|
|
262
|
+
/** camelCase on the wire. */
|
|
263
|
+
challengeNonce: string;
|
|
264
|
+
ui_schema_version: string;
|
|
265
|
+
/**
|
|
266
|
+
* Consequences that did not refuse the request — an unknown age, for one.
|
|
267
|
+
* Read them: the effect is otherwise invisible.
|
|
268
|
+
*/
|
|
269
|
+
warnings?: string[];
|
|
270
|
+
/**
|
|
271
|
+
* Present when this session is a child's and an invitation went out to the
|
|
272
|
+
* guardian channel this request carried. The consent is NOT recorded until
|
|
273
|
+
* that guardian verifies.
|
|
274
|
+
*/
|
|
275
|
+
guardian_verification?: GuardianVerificationPending;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* The guardian invitation raised by a session create that carried a channel.
|
|
279
|
+
*
|
|
280
|
+
* IT CARRIES NO LINK AND NO TOKEN, deliberately. The verification URL is a
|
|
281
|
+
* bearer credential — address-bound, 72 hours, single use — and it goes to the
|
|
282
|
+
* guardian's own address. Returning it on this response would put it in the
|
|
283
|
+
* organisation's logs, and the organisation is not the party the link is for.
|
|
284
|
+
*/
|
|
285
|
+
export interface GuardianVerificationPending {
|
|
286
|
+
/** `'pending'`: the guardianship exists and nothing about it is proven yet. */
|
|
287
|
+
status: string;
|
|
288
|
+
/**
|
|
289
|
+
* Which channel the invitation went out on — `'email'` or `'sms'`. Never the
|
|
290
|
+
* address, which the organisation already has.
|
|
291
|
+
*/
|
|
292
|
+
channel: string;
|
|
293
|
+
/** Names the guardianship, so it can be followed on the guardian APIs. */
|
|
294
|
+
link_id: string;
|
|
295
|
+
}
|
|
296
|
+
export interface SessionRenderResponse {
|
|
297
|
+
session_id: string;
|
|
298
|
+
notice: NoticeContent;
|
|
299
|
+
purposes: RenderPurpose[];
|
|
300
|
+
notice_details: NoticeDetails;
|
|
301
|
+
branding: BrandingConfig;
|
|
302
|
+
}
|
|
303
|
+
export interface NoticeContent {
|
|
304
|
+
title: string;
|
|
305
|
+
description: string;
|
|
306
|
+
version: string;
|
|
307
|
+
}
|
|
308
|
+
export interface RenderPurpose {
|
|
309
|
+
id: string;
|
|
310
|
+
code: string;
|
|
311
|
+
name: string;
|
|
312
|
+
description: string;
|
|
313
|
+
mandatory: boolean;
|
|
314
|
+
default_status: string;
|
|
315
|
+
sort_order: number;
|
|
316
|
+
legal_basis?: string;
|
|
317
|
+
retention_period?: string;
|
|
318
|
+
}
|
|
319
|
+
export interface NoticeDetails {
|
|
320
|
+
dpo_name?: string;
|
|
321
|
+
dpo_email?: string;
|
|
322
|
+
dpo_phone?: string;
|
|
323
|
+
grievance_url?: string;
|
|
324
|
+
privacy_policy_url?: string;
|
|
325
|
+
}
|
|
326
|
+
export interface BrandingConfig {
|
|
327
|
+
logo_url?: string;
|
|
328
|
+
primary_color?: string;
|
|
329
|
+
secondary_color?: string;
|
|
330
|
+
font_family?: string;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* GET /v1/consent/artifacts/{artifact_id}.
|
|
334
|
+
*
|
|
335
|
+
* TRANSCRIBED FROM ArtifactDetailsResponse
|
|
336
|
+
* (consent/collection.go:4270-4278). It used to declare `data_principal_ref`,
|
|
337
|
+
* `notice_version_id`, `status` and `updated_at` — the API sends NONE of those
|
|
338
|
+
* four, so each read back `undefined` and nothing said so. `artifact_type` and
|
|
339
|
+
* `data_principal_id`, which it DOES send, were missing.
|
|
340
|
+
*/
|
|
341
|
+
export interface ConsentArtifact {
|
|
342
|
+
artifact_id: string;
|
|
343
|
+
artifact_version: number;
|
|
344
|
+
artifact_type: string;
|
|
345
|
+
/** The platform's own uuid for the person. The artifact names no identifier. */
|
|
346
|
+
data_principal_id: string;
|
|
347
|
+
purposes: ArtifactPurpose[];
|
|
348
|
+
created_at: string;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* The 202 body of GET /v1/consent/artifacts/{artifact_id}?session_id=… — the
|
|
352
|
+
* consent WAS recorded and acknowledged, and its artifact is still being
|
|
353
|
+
* written (walk finding F018).
|
|
354
|
+
*
|
|
355
|
+
* Transcribed from ArtifactPendingResponse
|
|
356
|
+
* (consent/collection.go:4373-4400 at 4fda7e3d05). A DIFFERENT SHAPE FROM
|
|
357
|
+
* {@link ConsentArtifact} on purpose: a pending notice read as an artifact
|
|
358
|
+
* would be an artifact with no purposes, which looks like "consented to
|
|
359
|
+
* nothing". The status CODE is the platform's contract; `status: 'pending'`
|
|
360
|
+
* is the stable body discriminant beside it.
|
|
361
|
+
*/
|
|
362
|
+
export interface ArtifactPending {
|
|
363
|
+
status: 'pending';
|
|
364
|
+
/** The id that WILL carry this consent — the one the callback named. */
|
|
365
|
+
artifact_id: string;
|
|
366
|
+
/** The session the read presented, echoed. */
|
|
367
|
+
session_id: string;
|
|
368
|
+
/**
|
|
369
|
+
* Diagnostic, not the decision: `projection_pending`, `projection_committing`,
|
|
370
|
+
* or `parked_<reason>` when an operator owes the projection.
|
|
371
|
+
*/
|
|
372
|
+
reason: string;
|
|
373
|
+
/**
|
|
374
|
+
* Seconds, as on the Retry-After header (1 while the worker owes it, 60 when
|
|
375
|
+
* parked). The wire name is `retry_after`.
|
|
376
|
+
*/
|
|
377
|
+
retry_after: number;
|
|
378
|
+
/** Human-readable, and not a contract. */
|
|
379
|
+
message: string;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* What an artifact read can answer, as a union the compiler makes you branch
|
|
383
|
+
* on. A 404 is not in it: that is a thrown ConsenteraNotFoundError, and with a
|
|
384
|
+
* `session_id` it means the id was never issued for that session.
|
|
385
|
+
*/
|
|
386
|
+
export type ArtifactReadResult = {
|
|
387
|
+
state: 'recorded';
|
|
388
|
+
artifact: ConsentArtifact;
|
|
389
|
+
requestId?: string;
|
|
390
|
+
} | {
|
|
391
|
+
state: 'pending';
|
|
392
|
+
pending: ArtifactPending;
|
|
393
|
+
/** When to read again, from Retry-After (or `retry_after`), in ms. */
|
|
394
|
+
retryAfterMs: number;
|
|
395
|
+
requestId?: string;
|
|
396
|
+
};
|
|
397
|
+
export interface ArtifactPurpose {
|
|
398
|
+
purpose_id: string;
|
|
399
|
+
purpose_code: string;
|
|
400
|
+
purpose_name?: string;
|
|
401
|
+
status: ConsentStatus;
|
|
402
|
+
effective_at?: string;
|
|
403
|
+
expires_at?: string;
|
|
404
|
+
legal_basis?: string;
|
|
405
|
+
}
|
|
406
|
+
export type ConsentStatus = 'granted' | 'denied' | 'withdrawn' | 'not_set' | 'expired';
|
|
407
|
+
/**
|
|
408
|
+
* `data_principal_identifiers` — how every consent LIFECYCLE road names a
|
|
409
|
+
* person.
|
|
410
|
+
*
|
|
411
|
+
* ─── IT IS THE SAME SHAPE AS `data_principal` ON SESSION CREATE (F015) ─────
|
|
412
|
+
*
|
|
413
|
+
* An OPEN map keyed by THE TENANT'S OWN LOCKED INTEGRATION KEY — the same
|
|
414
|
+
* `core/dpintake.DataPrincipal` create takes, validated by the same
|
|
415
|
+
* `dpintake.Validate` (consent/update_context.go:104-119). An earlier turn of
|
|
416
|
+
* this SDK typed it as a CLOSED five-field object; F015
|
|
417
|
+
* (consent/one-identifier-vocabulary-20260922) deleted that struct, because a
|
|
418
|
+
* closed struct has three faults a per-tenant vocabulary cannot carry:
|
|
419
|
+
*
|
|
420
|
+
* * ONE VOCABULARY NOW, ONE SPELLING. The mobile atom is `mobile` on BOTH
|
|
421
|
+
* roads. It used to be `phone` here and `mobile` on create, folded
|
|
422
|
+
* server-side; F015 removed the fold, so `phone` is refused BY NAME
|
|
423
|
+
* (400 UNKNOWN_IDENTIFIER_FIELD, "…This platform spells that identifier
|
|
424
|
+
* 'mobile': use mobile"). Do NOT send `phone`.
|
|
425
|
+
* * THE ADMISSIBLE SET IS PER-TENANT, so this SDK cannot know it and must
|
|
426
|
+
* NOT allow-list. A tenant keyed on `{customer_id}` names people by
|
|
427
|
+
* `customer_id`; one keyed on `{email, mobile}` by those. Send the fields
|
|
428
|
+
* the organisation locked; the SERVER answers UNKNOWN_IDENTIFIER_FIELD,
|
|
429
|
+
* naming the field and listing the key\'s actual fields, when you get it
|
|
430
|
+
* wrong. A closed type here would reject a legal key at compile time.
|
|
431
|
+
* * THERE IS NO `pan` KEY. `pan` is an evidence-class identifier and can
|
|
432
|
+
* never be a scheme field (tenant_identifier_scheme_types_chk, 00697), so
|
|
433
|
+
* it is not an example and not a member of any allow-list.
|
|
434
|
+
*
|
|
435
|
+
* ─── THE KEY DIFFERS FROM CREATE; THE VALUE DOES NOT ──────────────────────
|
|
436
|
+
*
|
|
437
|
+
* Create spells the object `data_principal`; these roads keep
|
|
438
|
+
* `data_principal_identifiers`. Only the wire KEY differs — the value is the
|
|
439
|
+
* same open scheme-keyed map. The two answer different questions:
|
|
440
|
+
* `data_principal` on create MAY MINT a person, while this one is resolve-only
|
|
441
|
+
* and never creates anybody (lifecycle_identity.go). Unifying the key would be
|
|
442
|
+
* a wire break for every live integration.
|
|
443
|
+
*
|
|
444
|
+
* ANY ONE FIELD IS ENOUGH. A raw 12-digit `aadhaar` value is refused
|
|
445
|
+
* 400 INVALID_IDENTIFIER_FORMAT (F015 folded the old AADHAAR_RAW_REFUSED into
|
|
446
|
+
* the one format refusal create already answers); send the Aadhaar-LINKED
|
|
447
|
+
* token, never the number.
|
|
448
|
+
*
|
|
449
|
+
* THE REFUSALS A CALLER MEETS, all from the ONE validator create shares:
|
|
450
|
+
* - `UNKNOWN_IDENTIFIER_FIELD` 400 a field outside the tenant's key, by
|
|
451
|
+
* name, with the atom to use for a
|
|
452
|
+
* vernacular spelling ("phone" -> mobile)
|
|
453
|
+
* - `IDENTIFIER_REQUIRED` 400 nothing named a person
|
|
454
|
+
* - `INVALID_IDENTIFIER_FORMAT` 400 a value that cannot be an identifier of
|
|
455
|
+
* its type (raw Aadhaar lives here now)
|
|
456
|
+
* - `SCHEME_NOT_CONFIGURED` the organisation has not locked how it
|
|
457
|
+
* identifies people, so no field means
|
|
458
|
+
* anything yet
|
|
459
|
+
*/
|
|
460
|
+
export type DataPrincipalIdentifiers = Record<string, string>;
|
|
461
|
+
/**
|
|
462
|
+
* POST /v1/consent/validate.
|
|
463
|
+
*
|
|
464
|
+
* ─── data_principal_ref IS REFUSED OUTRIGHT ──────────────────────────────
|
|
465
|
+
*
|
|
466
|
+
* Owner ruling, 2026-09-21: no transition period and no deprecation window
|
|
467
|
+
* (consent/lifecycle_identity.go:8-11). A request carrying it is answered
|
|
468
|
+
* 400 VALIDATION_ERROR whose message begins `DATA_PRINCIPAL_REF_REFUSED`
|
|
469
|
+
* (the one machine-readable token, lifecycle_identity.go:68) — and the refusal
|
|
470
|
+
* fires EVEN IF `data_principal_id` is also present, because two fields naming
|
|
471
|
+
* a person can disagree and the caller would never learn which one the answer
|
|
472
|
+
* was about.
|
|
473
|
+
*
|
|
474
|
+
* WHAT WAS WRONG WITH IT, since it is easy to get backwards: the old field was
|
|
475
|
+
* ONE OPAQUE UNTYPED HANDLE. An untyped value with no locked scheme to narrow
|
|
476
|
+
* it is fanned across every probe order, and under a two-identifier scheme a
|
|
477
|
+
* bare 12-digit value is undecidable between `aadhaar` and `customer_id` — so
|
|
478
|
+
* the spine refused it rather than guess, because guessing wrong writes a
|
|
479
|
+
* person split in two. This is OPAQUE HANDLE -> TYPED IDENTIFIERS.
|
|
480
|
+
*
|
|
481
|
+
* Name the person with `data_principal_id`, or with
|
|
482
|
+
* `data_principal_identifiers`; the union below makes sending neither a
|
|
483
|
+
* COMPILE error instead of a round trip.
|
|
484
|
+
*/
|
|
485
|
+
export type ValidateRequest = ValidateRequestFields & ({
|
|
486
|
+
data_principal_id: string;
|
|
487
|
+
data_principal_identifiers?: DataPrincipalIdentifiers;
|
|
488
|
+
} | {
|
|
489
|
+
data_principal_id?: string;
|
|
490
|
+
data_principal_identifiers: DataPrincipalIdentifiers;
|
|
491
|
+
});
|
|
492
|
+
/** The non-identity half of {@link ValidateRequest}. */
|
|
493
|
+
export interface ValidateRequestFields {
|
|
494
|
+
data_principal_id?: string;
|
|
495
|
+
data_principal_identifiers?: DataPrincipalIdentifiers;
|
|
496
|
+
purpose_code?: string;
|
|
497
|
+
purpose_id?: string;
|
|
498
|
+
/** Optional: validate against the purpose's allowed_channels. */
|
|
499
|
+
channel?: string;
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* POST /v1/consent/validate — the platform's answer, field for field.
|
|
503
|
+
*
|
|
504
|
+
* TRANSCRIBED FROM ValidateResponse (consent/validate.go:142-165 at
|
|
505
|
+
* 4fda7e3d05) and checked against a live answer from setup.consentera.in
|
|
506
|
+
* (fixtures/platform-wire/validate.200.allow.json). It used to lack
|
|
507
|
+
* `data_principal_id`, the field the platform returns so a caller can drop
|
|
508
|
+
* the identifiers on the next call, and which the runbook's validate step
|
|
509
|
+
* reads (SDK register WEB-037). It also declared an `artifact_id` that the
|
|
510
|
+
* platform never sends. That field is gone.
|
|
511
|
+
*/
|
|
512
|
+
export interface ValidateResponse {
|
|
513
|
+
decision: 'ALLOW' | 'DENY';
|
|
514
|
+
reason_code: ConsentReasonCode;
|
|
515
|
+
/** The platform's uuid for the person asked about. Hold it; later calls need no identifier. */
|
|
516
|
+
data_principal_id: string;
|
|
517
|
+
purpose_id: string;
|
|
518
|
+
/** Omitted by the platform when the purpose has no code. */
|
|
519
|
+
purpose_code?: string;
|
|
520
|
+
effective_at?: string;
|
|
521
|
+
expires_at?: string;
|
|
522
|
+
/** Server time of the decision. The request's own timestamp is ignored. */
|
|
523
|
+
validated_at: string;
|
|
524
|
+
/** DPDP §4 basis: `consent`, `legitimate_use`, … */
|
|
525
|
+
legal_basis?: string;
|
|
526
|
+
is_mandatory?: boolean;
|
|
527
|
+
/** The §7 / §17 limb invoked when decision is ALLOW and legal_basis is not consent. */
|
|
528
|
+
dpdp_exemption_basis?: string;
|
|
529
|
+
/** Human-readable, and not a contract. */
|
|
530
|
+
message?: string;
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* `reason_code` on a validate response — the DPDP reason the decision came out
|
|
534
|
+
* the way it did. Transcribed from the Reason* constants in
|
|
535
|
+
* consentera-api/internal/modules/consent/consent/validate.go.
|
|
536
|
+
*
|
|
537
|
+
* NOT A CLOSED UNION, and the `string` arm is not laziness: validate.go mints
|
|
538
|
+
* `"CONSENT_" + strings.ToUpper(currentStatus)` for any consent status it has
|
|
539
|
+
* no named constant for, so the set genuinely cannot be enumerated ahead of
|
|
540
|
+
* time. A closed union would make a legitimate response a type error.
|
|
541
|
+
*
|
|
542
|
+
* TWO MEMBERS THIS UNION USED TO CARRY WERE NOT REASON CODES.
|
|
543
|
+
* `PURPOSE_MANDATORY` appears nowhere in the platform — not in a Go file, not
|
|
544
|
+
* in a doc — and the code it was presumably meant to be is `PURPOSE_INACTIVE`.
|
|
545
|
+
* `CONSENT_NOT_FOUND` is real but belongs to a DIFFERENT vocabulary: it is an
|
|
546
|
+
* HTTP error code (apierrors/errors.go, a 404 on a receipt or a withdrawal),
|
|
547
|
+
* never a validate reason. The reason code for "no consent record exists" is
|
|
548
|
+
* `NO_CONSENT_FOUND`, which this union did not have — so the one outcome an
|
|
549
|
+
* integrator most needs to branch on was the one it could not name.
|
|
550
|
+
*/
|
|
551
|
+
export type ConsentReasonCode = 'CONSENT_GRANTED' | 'LEGITIMATE_USE' | 'LEGAL_OBLIGATION' | 'MEDICAL_EMERGENCY' | 'EMPLOYMENT_PURPOSE' | 'PUBLIC_INTEREST' | 'NO_CONSENT_FOUND' | 'CONSENT_DENIED' | 'CONSENT_WITHDRAWN' | 'CONSENT_EXPIRED' | 'CONSENT_NOT_EFFECTIVE' | 'CONSENT_PENDING_CONFIRMATION' | 'DATA_PRINCIPAL_NOT_FOUND' | 'PURPOSE_INACTIVE' | 'MINOR_NO_GUARDIAN' | 'CHANNEL_NOT_ALLOWED' | 'LU_CONTRACT_LAPSED' | (string & {});
|
|
552
|
+
/**
|
|
553
|
+
* POST /v1/consent/validate/bulk.
|
|
554
|
+
*
|
|
555
|
+
* `data_principal_ref` AND `data_principal_refs` are both refused, with the
|
|
556
|
+
* same `DATA_PRINCIPAL_REF_REFUSED` prefix (consent/validate.go:249,257).
|
|
557
|
+
* One person by `data_principal_id` or `data_principal_identifiers`; many by
|
|
558
|
+
* `data_principal_ids` or `data_principal_identifiers_list`.
|
|
559
|
+
*/
|
|
560
|
+
export type BulkValidateRequest = BulkValidateRequestFields & ({
|
|
561
|
+
data_principal_id: string;
|
|
562
|
+
} | {
|
|
563
|
+
data_principal_identifiers: DataPrincipalIdentifiers;
|
|
564
|
+
} | {
|
|
565
|
+
data_principal_ids: string[];
|
|
566
|
+
} | {
|
|
567
|
+
data_principal_identifiers_list: DataPrincipalIdentifiers[];
|
|
568
|
+
});
|
|
569
|
+
/** The non-identity half of {@link BulkValidateRequest}. */
|
|
570
|
+
export interface BulkValidateRequestFields {
|
|
571
|
+
/** One person. */
|
|
572
|
+
data_principal_id?: string;
|
|
573
|
+
data_principal_identifiers?: DataPrincipalIdentifiers;
|
|
574
|
+
/** Many people, one per element. */
|
|
575
|
+
data_principal_ids?: string[];
|
|
576
|
+
data_principal_identifiers_list?: DataPrincipalIdentifiers[];
|
|
577
|
+
purpose_codes?: string[];
|
|
578
|
+
purpose_code?: string;
|
|
579
|
+
purpose_id?: string;
|
|
580
|
+
}
|
|
581
|
+
/** BulkValidateResponse (consent/validate.go:290-295). */
|
|
582
|
+
export interface BulkValidateResponse {
|
|
583
|
+
results: ValidateResponse[];
|
|
584
|
+
total_count: number;
|
|
585
|
+
allow_count: number;
|
|
586
|
+
deny_count: number;
|
|
587
|
+
}
|
|
588
|
+
export interface UpdateContextResponse {
|
|
589
|
+
data_principal_id: string;
|
|
590
|
+
consents: ConsentContextItem[];
|
|
591
|
+
notice_version_id: string;
|
|
592
|
+
notice_hash: string;
|
|
593
|
+
language_code: string;
|
|
594
|
+
}
|
|
595
|
+
export interface ConsentContextItem {
|
|
596
|
+
purpose_id: string;
|
|
597
|
+
purpose_code?: string;
|
|
598
|
+
purpose_name: string;
|
|
599
|
+
current_status: ConsentStatus;
|
|
600
|
+
mandatory: boolean;
|
|
601
|
+
withdrawable: boolean;
|
|
602
|
+
last_updated_at?: string;
|
|
603
|
+
artifact_id?: string;
|
|
604
|
+
artifact_version?: number;
|
|
605
|
+
}
|
|
606
|
+
export interface UpdateConsentRequest {
|
|
607
|
+
data_principal_id: string;
|
|
608
|
+
notice_version_id: string;
|
|
609
|
+
notice_hash: string;
|
|
610
|
+
language_code?: string;
|
|
611
|
+
updates: ConsentUpdate[];
|
|
612
|
+
affirmative_action: AffirmativeAction;
|
|
613
|
+
client_context?: ClientContext;
|
|
614
|
+
}
|
|
615
|
+
export interface ConsentUpdate {
|
|
616
|
+
purpose_id: string;
|
|
617
|
+
new_status: 'granted' | 'denied';
|
|
618
|
+
}
|
|
619
|
+
export interface WithdrawalContextResponse {
|
|
620
|
+
data_principal_id: string;
|
|
621
|
+
consents: ConsentContextItem[];
|
|
622
|
+
notice_version_id: string;
|
|
623
|
+
notice_hash: string;
|
|
624
|
+
}
|
|
625
|
+
export interface WithdrawRequest {
|
|
626
|
+
data_principal_id: string;
|
|
627
|
+
purposes: string[];
|
|
628
|
+
reason?: string;
|
|
629
|
+
affirmative_action: AffirmativeAction;
|
|
630
|
+
client_context?: ClientContext;
|
|
631
|
+
}
|
|
632
|
+
export interface BulkWithdrawRequest {
|
|
633
|
+
data_principal_id: string;
|
|
634
|
+
purposes: string[];
|
|
635
|
+
reason?: string;
|
|
636
|
+
affirmative_action: AffirmativeAction;
|
|
637
|
+
client_context?: ClientContext;
|
|
638
|
+
}
|
|
639
|
+
export interface RenewalContextResponse {
|
|
640
|
+
data_principal_id: string;
|
|
641
|
+
expiring_consents: ExpiringConsent[];
|
|
642
|
+
notice_version_id: string;
|
|
643
|
+
notice_hash: string;
|
|
644
|
+
}
|
|
645
|
+
export interface ExpiringConsent {
|
|
646
|
+
purpose_id: string;
|
|
647
|
+
purpose_name: string;
|
|
648
|
+
current_status: ConsentStatus;
|
|
649
|
+
expires_at: string;
|
|
650
|
+
days_remaining: number;
|
|
651
|
+
renewable: boolean;
|
|
652
|
+
}
|
|
653
|
+
export interface RenewRequest {
|
|
654
|
+
data_principal_id: string;
|
|
655
|
+
purpose_ids: string[];
|
|
656
|
+
notice_hash: string;
|
|
657
|
+
/** Client observation time; the server owns the recorded time. */
|
|
658
|
+
captured_at?: string;
|
|
659
|
+
client_context?: ClientContext;
|
|
660
|
+
}
|
|
661
|
+
/** The canonical renewal evidence returned by the existing renewal roads. */
|
|
662
|
+
export interface RenewedConsentInfo {
|
|
663
|
+
purpose_id: string;
|
|
664
|
+
previous_expiry?: string;
|
|
665
|
+
new_expiry?: string;
|
|
666
|
+
}
|
|
667
|
+
export interface RenewResponse {
|
|
668
|
+
/** Present when durable acceptance is awaiting the canonical stored artifact. */
|
|
669
|
+
status?: 'pending';
|
|
670
|
+
/** Server guidance for the existing bound artifact GET; never repeat the POST. */
|
|
671
|
+
retry_after_seconds?: number;
|
|
672
|
+
artifact_id: string;
|
|
673
|
+
artifact_version: number;
|
|
674
|
+
data_principal_id: string;
|
|
675
|
+
renewed_consents: RenewedConsentInfo[];
|
|
676
|
+
/** Existing artifact-read binding; retain it when the POST is accepted with 202. */
|
|
677
|
+
session_id?: string;
|
|
678
|
+
/** Only present when the server returned the canonical stored artifact hash. */
|
|
679
|
+
integrity_hash?: string;
|
|
680
|
+
}
|
|
681
|
+
export interface BulkRenewResponse extends RenewResponse {
|
|
682
|
+
success_count: number;
|
|
683
|
+
failure_count: number;
|
|
684
|
+
failed_renewals?: Array<{
|
|
685
|
+
purpose_id: string;
|
|
686
|
+
error: string;
|
|
687
|
+
}>;
|
|
688
|
+
}
|
|
689
|
+
export interface BulkRenewRequest {
|
|
690
|
+
data_principal_id: string;
|
|
691
|
+
renewals: Array<{
|
|
692
|
+
purpose_id: string;
|
|
693
|
+
new_expiry_days?: number;
|
|
694
|
+
}>;
|
|
695
|
+
notice_hash: string;
|
|
696
|
+
/** Client observation time; the server owns the recorded time. */
|
|
697
|
+
captured_at?: string;
|
|
698
|
+
client_context?: ClientContext;
|
|
699
|
+
}
|
|
700
|
+
export interface AffirmativeAction {
|
|
701
|
+
type: 'button' | 'toggle' | 'checkbox';
|
|
702
|
+
ui_event_id: string;
|
|
703
|
+
captured_at: string;
|
|
704
|
+
}
|
|
705
|
+
export interface ClientContext {
|
|
706
|
+
ip?: string;
|
|
707
|
+
user_agent?: string;
|
|
708
|
+
platform: 'web' | 'mobile' | 'api';
|
|
709
|
+
device_type?: 'desktop' | 'mobile' | 'tablet';
|
|
710
|
+
screen_resolution?: string;
|
|
711
|
+
timezone?: string;
|
|
712
|
+
browser_name?: string;
|
|
713
|
+
browser_version?: string;
|
|
714
|
+
os_name?: string;
|
|
715
|
+
os_version?: string;
|
|
716
|
+
session_id?: string;
|
|
717
|
+
page_url?: string;
|
|
718
|
+
referrer?: string;
|
|
719
|
+
}
|
|
720
|
+
export interface DFConfig {
|
|
721
|
+
tenant_id: string;
|
|
722
|
+
tenant_name: string;
|
|
723
|
+
purposes: DFPurpose[];
|
|
724
|
+
notices: DFNotice[];
|
|
725
|
+
regulations: string[];
|
|
726
|
+
languages: string[];
|
|
727
|
+
branding: BrandingConfig;
|
|
728
|
+
}
|
|
729
|
+
export interface DFPurpose {
|
|
730
|
+
id: string;
|
|
731
|
+
code: string;
|
|
732
|
+
name: string;
|
|
733
|
+
description: string;
|
|
734
|
+
category: string;
|
|
735
|
+
mandatory: boolean;
|
|
736
|
+
default_status: string;
|
|
737
|
+
retention_period?: string;
|
|
738
|
+
legal_basis?: string;
|
|
739
|
+
sort_order: number;
|
|
740
|
+
}
|
|
741
|
+
export interface DFNotice {
|
|
742
|
+
id: string;
|
|
743
|
+
internal_name: string;
|
|
744
|
+
title: string;
|
|
745
|
+
version: string;
|
|
746
|
+
status: string;
|
|
747
|
+
}
|
|
748
|
+
export interface NoticePurposesResponse {
|
|
749
|
+
notice_id: string;
|
|
750
|
+
notice_internal_name: string;
|
|
751
|
+
notice_title: string;
|
|
752
|
+
notice_version: string;
|
|
753
|
+
purposes: NoticePurpose[];
|
|
754
|
+
}
|
|
755
|
+
export interface NoticePurpose {
|
|
756
|
+
id: string;
|
|
757
|
+
code: string;
|
|
758
|
+
name: string;
|
|
759
|
+
description: string;
|
|
760
|
+
mandatory: boolean;
|
|
761
|
+
sort_order: number;
|
|
762
|
+
legal_basis?: string;
|
|
763
|
+
retention_period?: string;
|
|
764
|
+
data_categories?: string[];
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* POST /v1/df/principal-token.
|
|
768
|
+
*
|
|
769
|
+
* THIS ROAD STILL TAKES data_principal_ref, AND THAT IS CORRECT — do not
|
|
770
|
+
* "fix" it. It lives in identity/dfclient (handlers.go:1418-1428), not in the
|
|
771
|
+
* consent module, so the 2026-09-21 ruling that refuses the field across the
|
|
772
|
+
* consent surface does not reach it: it still accepts data_principal_id OR
|
|
773
|
+
* data_principal_ref and requires one of the two.
|
|
774
|
+
*/
|
|
775
|
+
export interface PrincipalTokenRequest {
|
|
776
|
+
data_principal_ref: string;
|
|
777
|
+
redirect_path?: string;
|
|
778
|
+
}
|
|
779
|
+
export interface PrincipalTokenResponse {
|
|
780
|
+
token: string;
|
|
781
|
+
redirect_url: string;
|
|
782
|
+
expires_in: number;
|
|
783
|
+
}
|
|
784
|
+
export interface WithdrawalAnalytics {
|
|
785
|
+
total_withdrawals: number;
|
|
786
|
+
by_purpose: Record<string, number>;
|
|
787
|
+
by_period: Record<string, number>;
|
|
788
|
+
reasons: WithdrawalReason[];
|
|
789
|
+
}
|
|
790
|
+
export interface WithdrawalReason {
|
|
791
|
+
reason: string;
|
|
792
|
+
count: number;
|
|
793
|
+
percentage: number;
|
|
794
|
+
}
|
|
795
|
+
export interface ApiResponse<T> {
|
|
796
|
+
data?: T;
|
|
797
|
+
error?: ApiError;
|
|
798
|
+
}
|
|
799
|
+
export interface ApiError {
|
|
800
|
+
code: string;
|
|
801
|
+
message: string;
|
|
802
|
+
details?: Record<string, unknown>;
|
|
803
|
+
}
|
|
804
|
+
export type ConsentLifecycleEvent = 'session:created' | 'session:completed' | 'session:expired' | 'consent:validated' | 'consent:updated' | 'consent:withdrawn' | 'consent:renewed' | 'config:loaded' | 'principal:redirected' | 'error';
|