@dpdpguard/react-native 2.0.0 → 2.1.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.
@@ -15,8 +15,7 @@ export interface paths {
15
15
  put?: never;
16
16
  /**
17
17
  * Record a new consent on behalf of a known data principal.
18
- * @description Requires the `dpdp_cm_api_enabled` flag; while it is off, this route
19
- * behaves as if it doesn't exist (404). Blocks tracking/marketing/
18
+ * @description Blocks tracking/marketing/
20
19
  * targeting purposes for verified minors when the organization has
21
20
  * `disableTrackingForMinors` set (403).
22
21
  */
@@ -55,10 +54,7 @@ export interface paths {
55
54
  path?: never;
56
55
  cookie?: never;
57
56
  };
58
- /**
59
- * Resolve an organization by slug (public).
60
- * @description Requires the `dpdp_mobile_api_enabled` flag; while it is off, this route behaves as if it doesn't exist (404).
61
- */
57
+ /** Resolve an organization by slug (public). */
62
58
  get: operations["getOrgBySlug"];
63
59
  put?: never;
64
60
  post?: never;
@@ -131,7 +127,7 @@ export interface paths {
131
127
  };
132
128
  /**
133
129
  * Resolve org, notice, and banner styling for the no-React embeddable widget script in one round trip (public).
134
- * @description Requires the `dpdp_mobile_api_enabled` flag; while it is off, this route behaves as if it doesn't exist (404). Powers the no-React embeddable widget script's single config call: given the domain-group id its script tag was generated with (`?id=`) and the embedding page's hostname (`?domain=`), resolves the organization, the notice that applies to that domain group (its own if published, else the org's default), and the domain group's banner styling. Strict domain check: a `domainGroupId` whose organization has no matching registered `domains` row for the given hostname resolves to 404, mirroring the same trust boundary as fetching a published notice.
130
+ * @description Powers the no-React embeddable widget script's single config call: given the domain-group id its script tag was generated with (`?id=`) and the embedding page's hostname (`?domain=`), resolves the organization, the notice that applies to that domain group (its own if published, else the org's default), and the domain group's banner styling. Strict domain check: a `domainGroupId` whose organization has no matching registered `domains` row for the given hostname resolves to 404, mirroring the same trust boundary as fetching a published notice.
135
131
  */
136
132
  get: operations["getWidgetConfig"];
137
133
  put?: never;
@@ -173,7 +169,7 @@ export interface paths {
173
169
  put?: never;
174
170
  /**
175
171
  * Government CM API Bridge — receive an inbound consent signal from a registered third-party Consent Manager.
176
- * @description Requires the `dpdp_cm_inbound_signals_enabled` flag; while it is off, this route behaves as if it doesn't exist (404). Authenticated with the same org-scoped API key as `/cm/v1/consent` (never a brokered principal token) — the reverse direction of that flow: here a third-party Consent Manager pushes a signal on behalf of one of its Data Principals, rather than the fiduciary recording its own consent. MeitY's registered-CM interoperability spec isn't published yet, so every signal lands `mappingStatus: "unresolved"` unless it exactly repeats a signal already recorded, in which case it lands `"duplicate"` — CM webhooks are expected to retry, so a repeat delivery still returns 201 rather than erroring.
172
+ * @description Authenticated with the same org-scoped API key as `/cm/v1/consent` (never a brokered principal token) — the reverse direction of that flow: here a third-party Consent Manager pushes a signal on behalf of one of its Data Principals, rather than the fiduciary recording its own consent. MeitY's registered-CM interoperability spec isn't published yet, so every signal lands `mappingStatus: "unresolved"` unless it exactly repeats a signal already recorded, in which case it lands `"duplicate"` — CM webhooks are expected to retry, so a repeat delivery still returns 201 rather than erroring.
177
173
  * **Known gap:** unlike every other `/api/v1` endpoint, this route's error responses use the free-text-only `Error` shape, not `ApiError` — it predates this API's error-catalog rollout to `/api/v1` and hasn't been migrated yet. Treat this as a documented inconsistency, not a schema-design choice to imitate for new endpoints.
178
174
  */
179
175
  post: operations["receiveCmSignal"];
@@ -223,6 +219,46 @@ export interface paths {
223
219
  patch?: never;
224
220
  trace?: never;
225
221
  };
222
+ "/api/v1/consents/anonymous/withdraw": {
223
+ parameters: {
224
+ query?: never;
225
+ header?: never;
226
+ path?: never;
227
+ cookie?: never;
228
+ };
229
+ get?: never;
230
+ put?: never;
231
+ /**
232
+ * Withdraw a guest's consent for one or more purposes.
233
+ * @description Unauthenticated public write, authorised by possession of the client-generated `anonymousId` that recorded the consent. Rate-limited. Idempotent: purposes with no active consent are not counted. Notifies connected processors, as an authenticated withdrawal does.
234
+ */
235
+ post: operations["withdrawConsentAnonymous"];
236
+ delete?: never;
237
+ options?: never;
238
+ head?: never;
239
+ patch?: never;
240
+ trace?: never;
241
+ };
242
+ "/api/v1/consents/{consentId}/withdrawal-acknowledgement": {
243
+ parameters: {
244
+ query?: never;
245
+ header?: never;
246
+ path?: never;
247
+ cookie?: never;
248
+ };
249
+ get?: never;
250
+ put?: never;
251
+ /**
252
+ * Report that a connected processor acted on a `consent.withdrawn` notification (research/2026-08-30-dpdp-research.md, §5-P1).
253
+ * @description `processorNotificationLogs` (see `convex/webhooks.ts`) only ever records whether the outbound `consent.withdrawn` webhook delivery reached the processor, never whether the processor actually stopped processing. A connected processor calls this endpoint to report what it did, closing the loop the same way `/api/v1/dsr/{requestId}/processor-confirmation` does for erasure requests.
254
+ */
255
+ post: operations["acknowledgeConsentWithdrawal"];
256
+ delete?: never;
257
+ options?: never;
258
+ head?: never;
259
+ patch?: never;
260
+ trace?: never;
261
+ };
226
262
  "/api/v1/dsr": {
227
263
  parameters: {
228
264
  query?: never;
@@ -319,7 +355,7 @@ export interface paths {
319
355
  };
320
356
  /**
321
357
  * List the organization's data principals past their retention window (docs/specs/crm-backend-integration-blockers.md, B1).
322
- * @description Read-only: returns active (not yet cancelled/executed) `scheduledErasures` rows whose window has already elapsed, so a fiduciary's server integration can trigger its own erasure automation instead of a human watching the dpdpbot dashboard. Requires the `dpdp_retention_due_api_enabled` flag; while it is off, this route behaves as if it doesn't exist (404). Write access to retention policies and scheduled erasures stays dashboard-only (ADR-007 D3) — there is no corresponding write route.
358
+ * @description Read-only: returns active (not yet cancelled/executed) `scheduledErasures` rows whose window has already elapsed, so a fiduciary's server integration can trigger its own erasure automation instead of a human watching the dpdpbot dashboard. Write access to retention policies and scheduled erasures stays dashboard-only (ADR-007 D3) — there is no corresponding write route.
323
359
  */
324
360
  get: operations["listDueRetentions"];
325
361
  put?: never;
@@ -339,7 +375,7 @@ export interface paths {
339
375
  };
340
376
  /**
341
377
  * Look up an org's per-purpose consent state by email (docs/specs/ecommerce-cms-plugins.md, §4).
342
- * @description Read-only: a WooCommerce/Shopify plugin's only fact about a customer is an email address from its own storefront — every other consent-reading route requires a dpdpbot-internal consent id or a brokered principal token. This resolves an email to the calling organization's own current per-purpose consent grants, so a plugin can gate a marketing sync in real time. `found: false` (not a 404) means the identifier is not yet a customer of record for this organization — distinct from "known, with no active consent", which is `found: true` and an empty or all-opted-out `purposes` array. Requires the `dpdp_consent_status_lookup_enabled` flag; while it is off, this route behaves as if it doesn't exist (404). Write access to consent stays `POST`/`DELETE /cm/v1/consent` — there is no corresponding write route here (ADR-007 D3).
378
+ * @description Read-only: a WooCommerce/Shopify plugin's only fact about a customer is an email address from its own storefront — every other consent-reading route requires a dpdpbot-internal consent id or a brokered principal token. This resolves an email to the calling organization's own current per-purpose consent grants, so a plugin can gate a marketing sync in real time. `found: false` (not a 404) means the identifier is not yet a customer of record for this organization — distinct from "known, with no active consent", which is `found: true` and an empty or all-opted-out `purposes` array. Write access to consent stays `POST`/`DELETE /cm/v1/consent` — there is no corresponding write route here (ADR-007 D3).
343
379
  */
344
380
  get: operations["getConsentStatus"];
345
381
  put?: never;
@@ -350,6 +386,86 @@ export interface paths {
350
386
  patch?: never;
351
387
  trace?: never;
352
388
  };
389
+ "/api/v1/dsr/{requestId}/processor-confirmation": {
390
+ parameters: {
391
+ query?: never;
392
+ header?: never;
393
+ path?: never;
394
+ cookie?: never;
395
+ };
396
+ get?: never;
397
+ put?: never;
398
+ /**
399
+ * Report what a connected processor actually did about an erasure request (docs/specs/ecommerce-cms-plugins.md, §7).
400
+ * @description `dsr.completed` (§2) tells a plugin an erasure was approved on dpdpbot's side; store-side erasure is rarely total deletion (an order record may be anonymized rather than deleted for tax/accounting retention, or a downstream API call may fail). This is the inbound record of what each connected system actually did, surfaced on the DSR detail view so a DPO auditing an erasure can see more than "dpdpbot marked this complete". Additive and read-mostly from dpdpbot's perspective — it does not reopen or alter the DSR's own status.
401
+ */
402
+ post: operations["recordDsrProcessorConfirmation"];
403
+ delete?: never;
404
+ options?: never;
405
+ head?: never;
406
+ patch?: never;
407
+ trace?: never;
408
+ };
409
+ "/api/v1/withdrawals/{propagationId}/ack": {
410
+ parameters: {
411
+ query?: never;
412
+ header?: never;
413
+ path?: never;
414
+ cookie?: never;
415
+ };
416
+ get?: never;
417
+ put?: never;
418
+ /**
419
+ * Confirm that a consent withdrawal took effect at this destination (research/2026-08-30-dpdp-research.md, §5-P1).
420
+ * @description The public half of the withdrawal-propagation acknowledgement ledger: `suppressionList` proves a withdrawal was *registered*, and this closes the loop the same way `/api/v1/dsr/{requestId}/processor-confirmation` does for erasure requests — a downstream destination reports for itself that it actually stopped processing, rather than a DPO taking dpdpbot's word for it. Idempotent: acknowledging an already-acknowledged row succeeds without changing its `acknowledgedAt`, because a retried confirmation must never fail.
421
+ */
422
+ post: operations["acknowledgeWithdrawalPropagation"];
423
+ delete?: never;
424
+ options?: never;
425
+ head?: never;
426
+ patch?: never;
427
+ trace?: never;
428
+ };
429
+ "/api/v1/withdrawals/{suppressionId}/propagations": {
430
+ parameters: {
431
+ query?: never;
432
+ header?: never;
433
+ path?: never;
434
+ cookie?: never;
435
+ };
436
+ /**
437
+ * Poll "N of M systems confirmed" for a withdrawal (research/2026-08-30-dpdp-research.md, §5-P1, §6.2).
438
+ * @description The read-side counterpart to `/api/v1/consents/{consentId}/withdrawal-acknowledgement`: lets a connected plugin poll how many downstream destinations have confirmed a withdrawal it caused, the same way `/api/v1/suppression-list` lets it poll the suppression list itself, rather than requiring a DPO to check the console.
439
+ */
440
+ get: operations["getWithdrawalPropagationStatus"];
441
+ put?: never;
442
+ post?: never;
443
+ delete?: never;
444
+ options?: never;
445
+ head?: never;
446
+ patch?: never;
447
+ trace?: never;
448
+ };
449
+ "/api/v1/suppression-list": {
450
+ parameters: {
451
+ query?: never;
452
+ header?: never;
453
+ path?: never;
454
+ cookie?: never;
455
+ };
456
+ /**
457
+ * Incrementally pull identifiers to stop marketing to (docs/specs/ecommerce-cms-plugins.md, §5).
458
+ * @description Read-only, cursor-by-`suppressedAt`: the reconciliation half of the webhook + poll pattern — a plugin's fallback sweep for when a `consent.withdrawn` webhook delivery failed, since delivery is at-least-once-attempted but not guaranteed. Populated whenever a withdrawing consent's principal has a known email; an anonymous/guest consent has nothing to suppress. Write access is withdrawal/erasure-triggered only — there is no manual write route.
459
+ */
460
+ get: operations["listSuppressionList"];
461
+ put?: never;
462
+ post?: never;
463
+ delete?: never;
464
+ options?: never;
465
+ head?: never;
466
+ patch?: never;
467
+ trace?: never;
468
+ };
353
469
  "/api/v1/breaches": {
354
470
  parameters: {
355
471
  query?: never;
@@ -359,7 +475,7 @@ export interface paths {
359
475
  };
360
476
  /**
361
477
  * List the organization's breach register (docs/specs/crm-backend-integration-blockers.md, B1).
362
- * @description Read-only: returns the organization's breach register (s.8(6)), so a fiduciary's server integration can trigger its own containment-erasure automation off a reported breach instead of a human watching the dpdpbot dashboard. Simulated breaches from the Breach Drill Simulator are excluded. Requires the `dpdp_breach_register_api_enabled` flag; while it is off, this route behaves as if it doesn't exist (404). Write access to the breach register stays dashboard-only (ADR-007 D3) — there is no corresponding write route.
478
+ * @description Read-only: returns the organization's breach register (s.8(6)), so a fiduciary's server integration can trigger its own containment-erasure automation off a reported breach instead of a human watching the dpdpbot dashboard. Simulated breaches from the Breach Drill Simulator are excluded. Write access to the breach register stays dashboard-only (ADR-007 D3) — there is no corresponding write route.
363
479
  */
364
480
  get: operations["listBreaches"];
365
481
  put?: never;
@@ -370,6 +486,26 @@ export interface paths {
370
486
  patch?: never;
371
487
  trace?: never;
372
488
  };
489
+ "/api/v1/consent-portability/exports/{exportId}/manifest": {
490
+ parameters: {
491
+ query?: never;
492
+ header?: never;
493
+ path?: never;
494
+ cookie?: never;
495
+ };
496
+ /**
497
+ * Read a signed consent-portability export's manifest (research/2026-09-27-dpdp-research.md, Idea P0 "you can leave with your evidence").
498
+ * @description The certificate, not the cargo: schema version, scope, record counters, the running SHA-256 digest folded over every exported record, and its platform HMAC signature — never the record payloads themselves (those stay on the in-console `pageExportRecords` query, unreachable from this surface). Lets a fiduciary's own server integration, or the receiving party on a change of Consent Manager (Indian Bank RFP GEM/2026/B/7823086 §1.6.1(e)), fetch a signed manifest without a human in a browser. Authenticated with the same org-scoped API key as `/api/v1/breaches`.
499
+ */
500
+ get: operations["getConsentPortabilityManifest"];
501
+ put?: never;
502
+ post?: never;
503
+ delete?: never;
504
+ options?: never;
505
+ head?: never;
506
+ patch?: never;
507
+ trace?: never;
508
+ };
373
509
  "/api/v1/consent/verify": {
374
510
  parameters: {
375
511
  query?: never;
@@ -394,13 +530,9 @@ export interface paths {
394
530
  * calling key's org.
395
531
  *
396
532
  * Withdrawal itself stays `DELETE /cm/v1/consent/{consentId}`'s job —
397
- * this route never mutates the consent record. It may append an
398
- * immutable decision row to the org's Consent Gate audit log when
399
- * `dpdp_consent_gate_decision_audit_enabled` is on; that log is read
400
- * from the dashboard, not from this API.
401
- *
402
- * Requires the `dpdp_consent_gate_api_enabled` flag; while it is off,
403
- * this route behaves as if it doesn't exist (404).
533
+ * this route never mutates the consent record. It appends an
534
+ * immutable decision row to the org's Consent Gate audit log; that log
535
+ * is read from the dashboard, not from this API.
404
536
  */
405
537
  post: operations["verifyConsentGate"];
406
538
  delete?: never;
@@ -429,12 +561,8 @@ export interface paths {
429
561
  * what it was told. The log is append-only: `verifyForApiKey` is the
430
562
  * only writer and there is no corresponding write route (ADR-007 D3).
431
563
  *
432
- * Requires the `dpdp_consent_gate_audit_api_enabled` flag; while it is
433
- * off, this route behaves as if it doesn't exist (404). That flag is
434
- * *transport* only and is not a way around
435
- * `dpdp_consent_gate_decision_audit_enabled`, which gates the
436
- * recording itself — while that flag is off nothing is written, so
437
- * this route returns an empty `decisions` array rather than an error.
564
+ * Returns an empty `decisions` array rather than an error when nothing
565
+ * has been recorded.
438
566
  */
439
567
  get: operations["listConsentGateDecisions"];
440
568
  put?: never;
@@ -469,13 +597,9 @@ export interface paths {
469
597
  * as `/api/v1/consent/verify` (ADR-004 D2). Derived entirely from the
470
598
  * decision log — it stores nothing of its own and mutates nothing.
471
599
  *
472
- * Requires the `dpdp_consent_gate_audit_api_enabled` flag; while it is
473
- * off, this route behaves as if it doesn't exist (404). That flag is
474
- * *transport* only and is not a way around
475
- * `dpdp_consent_gate_bypass_alerts_enabled`, which gates the detection
476
- * itself — while that flag is off this route returns an empty `alerts`
477
- * array (with `windowMs`/`denyThreshold` still populated) rather than
478
- * an error.
600
+ * Returns an empty `alerts` array (with `windowMs`/`denyThreshold`
601
+ * still populated) rather than an error when no bypass pattern is
602
+ * detected.
479
603
  */
480
604
  get: operations["listConsentGateBypassAlerts"];
481
605
  put?: never;
@@ -495,7 +619,7 @@ export interface paths {
495
619
  };
496
620
  /**
497
621
  * List the organization's cross-border transfer register (docs/specs/crm-backend-integration-blockers.md, B1).
498
- * @description Read-only: returns the organization's RoPA (`processingActivities`) entries declared as crossing borders (s.16), so a fiduciary's server integration can act on that register programmatically instead of a human reading it off the RoPA Builder dashboard. Archived activities are excluded. Requires the `dpdp_cross_border_transfers_api_enabled` flag; while it is off, this route behaves as if it doesn't exist (404). Write access to the RoPA stays dashboard-only (ADR-007 D3) — there is no corresponding write route.
622
+ * @description Read-only: returns the organization's RoPA (`processingActivities`) entries declared as crossing borders (s.16), so a fiduciary's server integration can act on that register programmatically instead of a human reading it off the RoPA Builder dashboard. Archived activities are excluded. Write access to the RoPA stays dashboard-only (ADR-007 D3) — there is no corresponding write route.
499
623
  */
500
624
  get: operations["listCrossBorderTransfers"];
501
625
  put?: never;
@@ -506,6 +630,30 @@ export interface paths {
506
630
  patch?: never;
507
631
  trace?: never;
508
632
  };
633
+ "/api/v1/discovery/manifest": {
634
+ parameters: {
635
+ query?: never;
636
+ header?: never;
637
+ path?: never;
638
+ cookie?: never;
639
+ };
640
+ get?: never;
641
+ put?: never;
642
+ /**
643
+ * Ingest a column manifest for a data discovery source (research/2026-09-06-dpdp-research.md, §5-P0).
644
+ * @description The connector half of Data Discovery & Classification. Run a read-only `information_schema` (or equivalent) query against your own database replica, inside your own network, and POST the resulting column list here on whatever schedule you choose. The platform never holds your database credentials — nothing here is a connection string, and there is nothing for an attacker who breaches this platform to steal but a schema you can already see in the console.
645
+ * Each column is classified against the India-specific personal-data taxonomy (Aadhaar, PAN, IFSC, UPI VPA, Voter ID, ABHA, guardian indicators) and reconciled against what is already on file: a column already known is re-classified in place, keeping any review recorded against it, rather than duplicated. A nightly run keeps the source inside its 90-day re-scan horizon without anyone opening a browser.
646
+ * `sampleValues` is accepted only for a source whose owner has explicitly opted it in from the console. Values are matched against the patterns above and discarded inside the request; nothing stores them. Sending them for a source that has not opted in is a 400, not a silent drop.
647
+ * Listing a table in `reconcileTablePaths` asserts that this manifest describes it in full: any column on file under it and absent here is marked as dropped from your schema and stops counting toward your coverage gaps (it stays readable, because an evidence bundle may already attest to it). A table named there but carrying no column in `columns` is a 400 — otherwise one mis-pasted manifest would retire an entire inventory.
648
+ * Writes are narrow by design: this route can add to and reclassify one source's columns and retire columns in tables you reconcile. Creating or archiving a source, reviewing a field, and changing the sample-value opt-in all stay decisions someone makes in the console, signed in as themselves.
649
+ */
650
+ post: operations["ingestDiscoveryManifest"];
651
+ delete?: never;
652
+ options?: never;
653
+ head?: never;
654
+ patch?: never;
655
+ trace?: never;
656
+ };
509
657
  "/api/v1/offline-consent/links": {
510
658
  parameters: {
511
659
  query?: never;
@@ -517,7 +665,7 @@ export interface paths {
517
665
  put?: never;
518
666
  /**
519
667
  * Generate an offline/QR consent link (docs/specs/offline-consent-capture.md, Problem Statement gap 6 — "no integration surface").
520
- * @description Creates the same `offlineConsentLinks` row the dashboard's "Offline consent link" generator does, so a fiduciary's server integration (HIS, POS terminal, CBS) can mint one without a human using the React admin page. The returned `token` resolves at `/offline-consent/{token}` on the fiduciary's own dpdpbot app origin — turn that URL into a QR code for a branch counter, agent desk, or POS terminal. Requires the `dpdp_offline_consent_link_api_enabled` flag; while it is off, this route behaves as if it doesn't exist (404). The link is only redeemable once `dpdp_offline_consent_bridge_enabled` is also on.
668
+ * @description Creates the same `offlineConsentLinks` row the dashboard's "Offline consent link" generator does, so a fiduciary's server integration (HIS, POS terminal, CBS) can mint one without a human using the React admin page. The returned `token` resolves at `/offline-consent/{token}` on the fiduciary's own dpdpbot app origin — turn that URL into a QR code for a branch counter, agent desk, or POS terminal.
521
669
  */
522
670
  post: operations["createOfflineConsentLink"];
523
671
  delete?: never;
@@ -539,7 +687,7 @@ export interface paths {
539
687
  * Sync a batch of offline capture sessions (docs/specs/offline-consent-capture.md 4.4).
540
688
  * @description An enrolled field device — or the HIS/CBS/POS server fronting a fleet of them — pushes queued capture sessions here when connectivity returns. Each session carries an ECDSA P-256 signature produced in the device's secure element over the canonical string defined in `conformance/audit-hash-spec.md` (Cryptographic Consent Artifact); the server recomputes that string and verifies it against the enrolled `agentDevices` public key.
541
689
  *
542
- * Every session is processed independently, so **partial success is normal**: one forged or stale session never fails the others in the batch. `clientSessionId` is the idempotency key — replaying a batch that already committed returns the original per-session results and writes nothing. A session that fails verification is *quarantined* with a reason and retained as evidence, never silently dropped and never silently accepted. Requires the `dpdp_offline_agent_capture_enabled` flag; 404s while it is off.
690
+ * Every session is processed independently, so **partial success is normal**: one forged or stale session never fails the others in the batch. `clientSessionId` is the idempotency key — replaying a batch that already committed returns the original per-session results and writes nothing. A session that fails verification is *quarantined* with a reason and retained as evidence, never silently dropped and never silently accepted.
543
691
  */
544
692
  post: operations["syncOfflineCaptures"];
545
693
  delete?: never;
@@ -561,7 +709,7 @@ export interface paths {
561
709
  * Raise an asynchronous consent request from a POS terminal (docs/specs/offline-consent-capture.md 6.4).
562
710
  * @description Called by a till when a phone number is keyed in. Returns immediately with a token the receipt can render as a short link or QR code; the customer reviews the notice and confirms on their own device afterwards at `/consent/{token}`.
563
711
  *
564
- * **The response is not a consent.** No `consents` row exists until the principal affirmatively confirms — processing must not begin on the strength of a raised request. Unanswered requests expire after 72 hours into the fiduciary's DPO queue. The submitted `phone` is HMAC'd per-organization before storage and is never persisted in the clear. Requires the `dpdp_offline_async_consent_enabled` flag.
712
+ * **The response is not a consent.** No `consents` row exists until the principal affirmatively confirms — processing must not begin on the strength of a raised request. Unanswered requests expire after 72 hours into the fiduciary's DPO queue. The submitted `phone` is HMAC'd per-organization before storage and is never persisted in the clear.
565
713
  */
566
714
  post: operations["createPosConsentRequest"];
567
715
  delete?: never;
@@ -583,7 +731,7 @@ export interface paths {
583
731
  * Submit the artefact of a completed IVR consent call (docs/specs/offline-consent-capture.md 6.3).
584
732
  * @description dpdpbot defines this adapter contract but does not build telephony. A partner gateway posts what a completed call produced; the **audio recording is the affirmative-action evidence**, so `audioRecordingUri` is required — a DTMF digit alone proves a key was pressed, not that a notice was read out first.
585
733
  *
586
- * A `dtmfSequence` beginning with `1` is treated as acceptance and materialises an audited `consents` row with `source: offline_ivr`; anything else records the artefact without creating a consent, so a refusal is retained as evidence rather than discarded. `partnerSignature` is stored verbatim as an assertion of origin — dpdpbot cannot verify a key it does not hold and does not claim to. Requires the `dpdp_offline_async_consent_enabled` flag.
734
+ * A `dtmfSequence` beginning with `1` is treated as acceptance and materialises an audited `consents` row with `source: offline_ivr`; anything else records the artefact without creating a consent, so a refusal is retained as evidence rather than discarded. `partnerSignature` is stored verbatim as an assertion of origin — dpdpbot cannot verify a key it does not hold and does not claim to.
587
735
  */
588
736
  post: operations["recordIvrConsentArtifact"];
589
737
  delete?: never;
@@ -592,6 +740,28 @@ export interface paths {
592
740
  patch?: never;
593
741
  trace?: never;
594
742
  };
743
+ "/api/v1/offline/sms-withdraw": {
744
+ parameters: {
745
+ query?: never;
746
+ header?: never;
747
+ path?: never;
748
+ cookie?: never;
749
+ };
750
+ get?: never;
751
+ put?: never;
752
+ /**
753
+ * SMS/WhatsApp reply-keyword consent withdrawal (docs/specs/offline-consent-capture.md §5).
754
+ * @description A fiduciary points their SMS/WhatsApp gateway's inbound webhook at this URL; every inbound reply is posted here, and only ones matching a withdrawal keyword (`stop`, `unsubscribe`, `withdraw`, `revoke`, `cancel`, matched case-insensitively against the whole trimmed message) act.
755
+ *
756
+ * Always returns `200` on a well-formed, authenticated request — including "no keyword matched" and "no receipt found for this number" — because the caller is a gateway relaying arbitrary inbound text, not a client that can act on a 4xx. `matched`/`withdrawn` in the response let an integration log the outcome without needing a status-code-as-enum.
757
+ */
758
+ post: operations["smsWhatsappWithdraw"];
759
+ delete?: never;
760
+ options?: never;
761
+ head?: never;
762
+ patch?: never;
763
+ trace?: never;
764
+ };
595
765
  "/api/v1/staff/dsr": {
596
766
  parameters: {
597
767
  query?: never;
@@ -672,10 +842,7 @@ export interface paths {
672
842
  };
673
843
  get?: never;
674
844
  put?: never;
675
- /**
676
- * Deny an erasure request on a documented statutory basis.
677
- * @description Requires the `dpdp_dsr_statutory_denial_enabled` flag.
678
- */
845
+ /** Deny an erasure request on a documented statutory basis. */
679
846
  post: operations["staffRejectDsrWithStatutoryBasis"];
680
847
  delete?: never;
681
848
  options?: never;
@@ -717,6 +884,86 @@ export interface paths {
717
884
  patch?: never;
718
885
  trace?: never;
719
886
  };
887
+ "/api/v1/observatory/observations": {
888
+ parameters: {
889
+ query?: never;
890
+ header?: never;
891
+ path?: never;
892
+ cookie?: never;
893
+ };
894
+ get?: never;
895
+ put?: never;
896
+ /**
897
+ * Ingest a batch of Consent Observatory observations (docs/specs/consent-observatory-extension.md §7).
898
+ * @description Unauthenticated by design — requiring an account would collapse contribution rates and would force linking a browsing-derived observation to an identity. Anti-abuse (quorum before publication, outlier rejection) happens downstream of ingest, not here; rate limiting is enforced at the edge, not by this route. Every observation is processed independently, so **partial success is normal**: one malformed observation never fails the batch. `domain` is re-normalized server-side (lowercased, scheme/port/userinfo stripped, reduced toward eTLD+1) — the client's own normalization is never trusted.
899
+ */
900
+ post: operations["ingestObservatoryObservations"];
901
+ delete?: never;
902
+ options?: never;
903
+ head?: never;
904
+ patch?: never;
905
+ trace?: never;
906
+ };
907
+ "/api/v1/observatory/domains/{domain}": {
908
+ parameters: {
909
+ query?: never;
910
+ header?: never;
911
+ path?: never;
912
+ cookie?: never;
913
+ };
914
+ /**
915
+ * Read a domain's published Consent Observatory verdict (docs/specs/consent-observatory-extension.md §7).
916
+ * @description Unauthenticated public read, so the browser extension (and any third party) can show a registry grade without shipping a Convex client. Returns only a verdict whose `publicationState` is `"published"` — every other state, and any domain with no verdict at all, is a `404`, never a redacted body, so an unpublished verdict's existence is not itself observable from outside. `domain` is re-normalized server-side (lowercased, scheme/path stripped, reduced toward eTLD+1) the same way ingest normalizes it.
917
+ */
918
+ get: operations["getObservatoryDomainVerdict"];
919
+ put?: never;
920
+ post?: never;
921
+ delete?: never;
922
+ options?: never;
923
+ head?: never;
924
+ patch?: never;
925
+ trace?: never;
926
+ };
927
+ "/api/v1/observatory/sectors/{sector}": {
928
+ parameters: {
929
+ query?: never;
930
+ header?: never;
931
+ path?: never;
932
+ cookie?: never;
933
+ };
934
+ /**
935
+ * Download the aggregate-only sector report (docs/specs/consent-observatory-extension.md §12).
936
+ * @description Unauthenticated public read of a CSV: cohort size, average score, and grade distribution for one closed-enum sector, aggregated only over `publicationState: "published"` verdicts on the current rubric version — never per-domain rows ("Open data (decided, OQ 8): aggregates only"). 404s for a `sector` outside the closed enum.
937
+ */
938
+ get: operations["getObservatorySectorReport"];
939
+ put?: never;
940
+ post?: never;
941
+ delete?: never;
942
+ options?: never;
943
+ head?: never;
944
+ patch?: never;
945
+ trace?: never;
946
+ };
947
+ "/api/v1/observatory/contributions/{contributorHash}": {
948
+ parameters: {
949
+ query?: never;
950
+ header?: never;
951
+ path?: never;
952
+ cookie?: never;
953
+ };
954
+ get?: never;
955
+ put?: never;
956
+ post?: never;
957
+ /**
958
+ * "Delete my contributions" (docs/specs/consent-observatory-extension.md §18).
959
+ * @description Unauthenticated, same reasoning as ingest — the only identity available is the rotating, salted `contributorHash` itself, never a user or organization id (§9.2). Deletes every `siteObservations` row for that hash. Always responds `{ "deleted": true }`, including for a hash with no observations — deletion is idempotent, and distinguishing "nothing to delete" from "deleted" would let a caller probe whether a given hash ever contributed anything. `contributorHash` rotates monthly with the salt (§9.2), so this only reaches what the current salt epoch can still link to one installation; after rotation, the data is no longer linkable to it. Ships ungated.
960
+ */
961
+ delete: operations["deleteObservatoryContributions"];
962
+ options?: never;
963
+ head?: never;
964
+ patch?: never;
965
+ trace?: never;
966
+ };
720
967
  }
721
968
  export type webhooks = Record<string, never>;
722
969
  export interface components {
@@ -779,8 +1026,18 @@ export interface components {
779
1026
  };
780
1027
  /** @description Present only if the domain group has customized banner styling. */
781
1028
  bannerSettings?: Record<string, never>;
782
- };
783
- /** @description One immutable row of the Consent Gate decision audit log, written by `POST /api/v1/consent/verify` while `dpdp_consent_gate_decision_audit_enabled` is on. */
1029
+ /** @description Absolute URL of the org's data-principal portal, or null. */
1030
+ portalUrl?: string | null;
1031
+ /** @description Version of the notice being presented; the widget stores it with the visitor's decision and re-prompts when it changes. */
1032
+ noticeVersion?: number;
1033
+ noticeSourceLanguage?: string;
1034
+ /** @description The grievance contact the notice must name, or null. */
1035
+ grievanceContact?: {
1036
+ name?: string;
1037
+ email?: string;
1038
+ } | null;
1039
+ };
1040
+ /** @description One immutable row of the Consent Gate decision audit log, written by `POST /api/v1/consent/verify`. */
784
1041
  ConsentGateDecision: {
785
1042
  /** @description Stable id for this decision row — usable as a de-duplication key when tailing the log. */
786
1043
  decisionId: string;
@@ -936,6 +1193,59 @@ export interface components {
936
1193
  /** @description Free-text message. Not yet a stable machine-readable code — see the "Known gap" note in the top-level description. */
937
1194
  error: string;
938
1195
  };
1196
+ /** @description One (domain, pageType, phase) snapshot (docs/specs/consent-observatory-extension.md §2.2, §4.1). Trimmed from the spec's full shape — no `pathHash`, `fpApiCalls`, `buttonGraph`, or `discoveredLinks` yet; this endpoint ships ingest and storage only, not rubric evaluation. */
1197
+ ObservatoryObservation: {
1198
+ /** @description Re-normalized server-side on receipt — do not rely on this being echoed back verbatim. */
1199
+ domain: string;
1200
+ /** @enum {string} */
1201
+ pageType: "home" | "listing" | "product" | "article" | "auth" | "checkout" | "account" | "form" | "policy" | "other";
1202
+ /** @enum {string} */
1203
+ phase: "pre_interaction" | "post_accept" | "post_reject" | "no_action";
1204
+ /** @enum {string} */
1205
+ source: "extension" | "probe" | "seed_crawl";
1206
+ /** @description Epoch milliseconds. */
1207
+ observedAt: number;
1208
+ rulesetVersion: string;
1209
+ /** @description Rotating, salted hash of an extension-local install id. Absent for `source: probe`. */
1210
+ contributorHash?: string;
1211
+ cookies?: {
1212
+ name: string;
1213
+ /** @enum {string} */
1214
+ party: "first" | "third";
1215
+ /** @enum {string} */
1216
+ expiryClass: "session" | "lt_24h" | "lt_30d" | "lt_13mo" | "gt_13mo";
1217
+ }[];
1218
+ storageKeys?: string[];
1219
+ requestHosts?: string[];
1220
+ cmp?: string;
1221
+ bannerFingerprint?: string;
1222
+ };
1223
+ /** @description A published Consent Observatory verdict (docs/specs/consent-observatory-extension.md §4.3, §7). Never includes `publicationState`, `noticeSentAt`, `claimedByOrganizationId`, or `sector` — those are internal-only fields on `siteVerdicts`; a `200` from this endpoint is only ever returned for a `"published"` row in the first place. */
1224
+ ObservatoryDomainVerdict: {
1225
+ domain: string;
1226
+ rubricVersion: string;
1227
+ /** @description 0..100, excluding advisory checks. */
1228
+ score: number;
1229
+ /** @enum {string} */
1230
+ grade: "A" | "B" | "C" | "D" | "E" | "insufficient_data";
1231
+ checkResults: {
1232
+ checkKey: string;
1233
+ /** @enum {string} */
1234
+ status: "pass" | "fail" | "insufficient_data";
1235
+ weight: number;
1236
+ confidence: number;
1237
+ }[];
1238
+ /** @description Epoch milliseconds. */
1239
+ computedAt: number;
1240
+ /** @description Epoch milliseconds; past this the verdict is due a recompute. */
1241
+ staleAfter: number;
1242
+ /** @description Whether this domain itself runs dpdpbot's own consent banner (docs/specs/consent-observatory-extension.md §14, "Reciprocal loop"). Always populated, with `verified: false` when it does not; the type stays nullable for backward compatibility. */
1243
+ reciprocalBadge?: {
1244
+ verified?: boolean;
1245
+ /** @description Epoch milliseconds of the most recent observation reporting dpdpbot's own CMP, or null when `verified` is false. */
1246
+ verifiedAt?: number | null;
1247
+ } | null;
1248
+ };
939
1249
  };
940
1250
  responses: never;
941
1251
  parameters: {
@@ -1000,7 +1310,7 @@ export interface operations {
1000
1310
  "application/json": components["schemas"]["Error"];
1001
1311
  };
1002
1312
  };
1003
- /** @description Notice not found (or belongs to a different organization), or no data principal matches the given email. Also returned for every request while the `dpdp_cm_api_enabled` flag is off. */
1313
+ /** @description Notice not found (or belongs to a different organization), or no data principal matches the given email. */
1004
1314
  404: {
1005
1315
  headers: {
1006
1316
  [name: string]: unknown;
@@ -1059,7 +1369,7 @@ export interface operations {
1059
1369
  "application/json": components["schemas"]["Error"];
1060
1370
  };
1061
1371
  };
1062
- /** @description No such consent record, or it belongs to a different organization than the API key. Also returned for every request while the `dpdp_cm_api_enabled` flag is off. */
1372
+ /** @description No such consent record, or it belongs to a different organization than the API key. */
1063
1373
  404: {
1064
1374
  headers: {
1065
1375
  [name: string]: unknown;
@@ -1109,7 +1419,7 @@ export interface operations {
1109
1419
  "application/json": components["schemas"]["Error"];
1110
1420
  };
1111
1421
  };
1112
- /** @description No such consent record, or it belongs to a different organization than the API key. Also returned for every request while the `dpdp_cm_api_enabled` flag is off. */
1422
+ /** @description No such consent record, or it belongs to a different organization than the API key. */
1113
1423
  404: {
1114
1424
  headers: {
1115
1425
  [name: string]: unknown;
@@ -1149,7 +1459,7 @@ export interface operations {
1149
1459
  "application/json": components["schemas"]["OrgSummary"];
1150
1460
  };
1151
1461
  };
1152
- /** @description No organization matches this slug, or the flag is off. */
1462
+ /** @description No organization matches this slug. */
1153
1463
  404: {
1154
1464
  headers: {
1155
1465
  [name: string]: unknown;
@@ -1214,7 +1524,7 @@ export interface operations {
1214
1524
  "application/json": components["schemas"]["Notice"];
1215
1525
  };
1216
1526
  };
1217
- /** @description No such notice, or the flag is off. */
1527
+ /** @description No such notice. */
1218
1528
  404: {
1219
1529
  headers: {
1220
1530
  [name: string]: unknown;
@@ -1252,7 +1562,7 @@ export interface operations {
1252
1562
  };
1253
1563
  };
1254
1564
  };
1255
- /** @description The domain is not registered to this organization, or the flag is off. */
1565
+ /** @description The domain is not registered to this organization. */
1256
1566
  404: {
1257
1567
  headers: {
1258
1568
  [name: string]: unknown;
@@ -1294,7 +1604,7 @@ export interface operations {
1294
1604
  "application/json": components["schemas"]["ApiError"];
1295
1605
  };
1296
1606
  };
1297
- /** @description No matching domain group, the domain isn't registered to that group's organization, no notice resolves, or the `dpdp_mobile_api_enabled` flag is off. */
1607
+ /** @description No matching domain group, the domain isn't registered to that group's organization, or no notice resolves. */
1298
1608
  404: {
1299
1609
  headers: {
1300
1610
  [name: string]: unknown;
@@ -1353,15 +1663,6 @@ export interface operations {
1353
1663
  "application/json": components["schemas"]["ApiError"];
1354
1664
  };
1355
1665
  };
1356
- /** @description The `dpdp_mobile_api_enabled` flag is off. */
1357
- 404: {
1358
- headers: {
1359
- [name: string]: unknown;
1360
- };
1361
- content: {
1362
- "application/json": components["schemas"]["ApiError"];
1363
- };
1364
- };
1365
1666
  };
1366
1667
  };
1367
1668
  receiveCmSignal: {
@@ -1413,15 +1714,6 @@ export interface operations {
1413
1714
  "application/json": components["schemas"]["Error"];
1414
1715
  };
1415
1716
  };
1416
- /** @description The `dpdp_cm_inbound_signals_enabled` flag is off. */
1417
- 404: {
1418
- headers: {
1419
- [name: string]: unknown;
1420
- };
1421
- content: {
1422
- "application/json": components["schemas"]["Error"];
1423
- };
1424
- };
1425
1717
  };
1426
1718
  };
1427
1719
  linkAnonymousConsent: {
@@ -1450,6 +1742,7 @@ export interface operations {
1450
1742
  needsReconsent: {
1451
1743
  noticeId: string;
1452
1744
  purpose: string;
1745
+ dataTypes: string[];
1453
1746
  }[];
1454
1747
  };
1455
1748
  };
@@ -1491,6 +1784,12 @@ export interface operations {
1491
1784
  purpose: string;
1492
1785
  dataTypes: string[];
1493
1786
  anonymousId: string;
1787
+ /** @description Locale of the notice text the visitor read. */
1788
+ noticeLocale?: string;
1789
+ /** @description SHA-256 of the exact notice text presented, stored in the consent audit trail as evidence of what was shown. */
1790
+ noticeHash?: string;
1791
+ /** @description The domain group the embedded widget loaded for. Used to attribute the consent when the notice itself is org-wide (not scoped to a group); ignored when the notice is scoped, and dropped if it is not a group in this organization. */
1792
+ domainGroupId?: string;
1494
1793
  };
1495
1794
  };
1496
1795
  };
@@ -1518,38 +1817,91 @@ export interface operations {
1518
1817
  "application/json": components["schemas"]["ApiError"];
1519
1818
  };
1520
1819
  };
1521
- /** @description The `dpdp_mobile_api_enabled` flag is off. */
1522
- 404: {
1523
- headers: {
1524
- [name: string]: unknown;
1525
- };
1526
- content: {
1527
- "application/json": components["schemas"]["ApiError"];
1528
- };
1529
- };
1530
1820
  };
1531
1821
  };
1532
- listDsrRequestsForCurrentUser: {
1822
+ withdrawConsentAnonymous: {
1533
1823
  parameters: {
1534
1824
  query?: never;
1535
1825
  header?: never;
1536
1826
  path?: never;
1537
1827
  cookie?: never;
1538
1828
  };
1539
- requestBody?: never;
1540
- responses: {
1541
- /** @description The caller's own requests. */
1542
- 200: {
1543
- headers: {
1544
- [name: string]: unknown;
1829
+ requestBody: {
1830
+ content: {
1831
+ "application/json": {
1832
+ organizationId: string;
1833
+ anonymousId: string;
1834
+ purposes: string[];
1835
+ };
1836
+ };
1837
+ };
1838
+ responses: {
1839
+ /** @description Withdrawal processed. */
1840
+ 200: {
1841
+ headers: {
1842
+ [name: string]: unknown;
1545
1843
  };
1546
1844
  content: {
1547
1845
  "application/json": {
1548
- requests: components["schemas"]["DsrRequest"][];
1846
+ withdrawn: number;
1847
+ purposes: string[];
1848
+ withdrawnAt: number;
1549
1849
  };
1550
1850
  };
1551
1851
  };
1552
- /** @description Missing, invalid, or expired access token. */
1852
+ /** @description Missing/invalid fields or rate limited. */
1853
+ 400: {
1854
+ headers: {
1855
+ [name: string]: unknown;
1856
+ };
1857
+ content: {
1858
+ "application/json": components["schemas"]["ApiError"];
1859
+ };
1860
+ };
1861
+ };
1862
+ };
1863
+ acknowledgeConsentWithdrawal: {
1864
+ parameters: {
1865
+ query?: never;
1866
+ header?: never;
1867
+ path: {
1868
+ /** @description Convex document id of the `consents` row. */
1869
+ consentId: components["parameters"]["ConsentId"];
1870
+ };
1871
+ cookie?: never;
1872
+ };
1873
+ requestBody: {
1874
+ content: {
1875
+ "application/json": {
1876
+ processorName: string;
1877
+ /** @enum {string} */
1878
+ outcome: "completed" | "partial" | "failed";
1879
+ note?: string;
1880
+ };
1881
+ };
1882
+ };
1883
+ responses: {
1884
+ /** @description Acknowledgement recorded. */
1885
+ 201: {
1886
+ headers: {
1887
+ [name: string]: unknown;
1888
+ };
1889
+ content: {
1890
+ "application/json": {
1891
+ ok: boolean;
1892
+ };
1893
+ };
1894
+ };
1895
+ /** @description Missing/invalid fields, invalid consent id, or the consent has not been withdrawn. */
1896
+ 400: {
1897
+ headers: {
1898
+ [name: string]: unknown;
1899
+ };
1900
+ content: {
1901
+ "application/json": components["schemas"]["ApiError"];
1902
+ };
1903
+ };
1904
+ /** @description Missing or invalid API key. */
1553
1905
  401: {
1554
1906
  headers: {
1555
1907
  [name: string]: unknown;
@@ -1558,7 +1910,7 @@ export interface operations {
1558
1910
  "application/json": components["schemas"]["ApiError"];
1559
1911
  };
1560
1912
  };
1561
- /** @description The `dpdp_mobile_api_enabled` flag is off. */
1913
+ /** @description The consent does not belong to the calling key's organization. */
1562
1914
  404: {
1563
1915
  headers: {
1564
1916
  [name: string]: unknown;
@@ -1569,6 +1921,37 @@ export interface operations {
1569
1921
  };
1570
1922
  };
1571
1923
  };
1924
+ listDsrRequestsForCurrentUser: {
1925
+ parameters: {
1926
+ query?: never;
1927
+ header?: never;
1928
+ path?: never;
1929
+ cookie?: never;
1930
+ };
1931
+ requestBody?: never;
1932
+ responses: {
1933
+ /** @description The caller's own requests. */
1934
+ 200: {
1935
+ headers: {
1936
+ [name: string]: unknown;
1937
+ };
1938
+ content: {
1939
+ "application/json": {
1940
+ requests: components["schemas"]["DsrRequest"][];
1941
+ };
1942
+ };
1943
+ };
1944
+ /** @description Missing, invalid, or expired access token. */
1945
+ 401: {
1946
+ headers: {
1947
+ [name: string]: unknown;
1948
+ };
1949
+ content: {
1950
+ "application/json": components["schemas"]["ApiError"];
1951
+ };
1952
+ };
1953
+ };
1954
+ };
1572
1955
  createDsrRequest: {
1573
1956
  parameters: {
1574
1957
  query?: never;
@@ -1616,15 +1999,6 @@ export interface operations {
1616
1999
  "application/json": components["schemas"]["ApiError"];
1617
2000
  };
1618
2001
  };
1619
- /** @description The `dpdp_mobile_api_enabled` flag is off. */
1620
- 404: {
1621
- headers: {
1622
- [name: string]: unknown;
1623
- };
1624
- content: {
1625
- "application/json": components["schemas"]["ApiError"];
1626
- };
1627
- };
1628
2002
  };
1629
2003
  };
1630
2004
  listGrievancesForCurrentUser: {
@@ -1656,15 +2030,6 @@ export interface operations {
1656
2030
  "application/json": components["schemas"]["ApiError"];
1657
2031
  };
1658
2032
  };
1659
- /** @description The `dpdp_mobile_api_enabled` flag is off. */
1660
- 404: {
1661
- headers: {
1662
- [name: string]: unknown;
1663
- };
1664
- content: {
1665
- "application/json": components["schemas"]["ApiError"];
1666
- };
1667
- };
1668
2033
  };
1669
2034
  };
1670
2035
  createGrievance: {
@@ -1713,15 +2078,6 @@ export interface operations {
1713
2078
  "application/json": components["schemas"]["ApiError"];
1714
2079
  };
1715
2080
  };
1716
- /** @description The `dpdp_mobile_api_enabled` flag is off. */
1717
- 404: {
1718
- headers: {
1719
- [name: string]: unknown;
1720
- };
1721
- content: {
1722
- "application/json": components["schemas"]["ApiError"];
1723
- };
1724
- };
1725
2081
  };
1726
2082
  };
1727
2083
  getNomination: {
@@ -1751,15 +2107,6 @@ export interface operations {
1751
2107
  "application/json": components["schemas"]["ApiError"];
1752
2108
  };
1753
2109
  };
1754
- /** @description The `dpdp_mobile_api_enabled` flag is off. */
1755
- 404: {
1756
- headers: {
1757
- [name: string]: unknown;
1758
- };
1759
- content: {
1760
- "application/json": components["schemas"]["ApiError"];
1761
- };
1762
- };
1763
2110
  };
1764
2111
  };
1765
2112
  upsertNomination: {
@@ -1805,15 +2152,6 @@ export interface operations {
1805
2152
  "application/json": components["schemas"]["ApiError"];
1806
2153
  };
1807
2154
  };
1808
- /** @description The `dpdp_mobile_api_enabled` flag is off. */
1809
- 404: {
1810
- headers: {
1811
- [name: string]: unknown;
1812
- };
1813
- content: {
1814
- "application/json": components["schemas"]["ApiError"];
1815
- };
1816
- };
1817
2155
  };
1818
2156
  };
1819
2157
  revokeNomination: {
@@ -1845,15 +2183,6 @@ export interface operations {
1845
2183
  "application/json": components["schemas"]["ApiError"];
1846
2184
  };
1847
2185
  };
1848
- /** @description The `dpdp_mobile_api_enabled` flag is off. */
1849
- 404: {
1850
- headers: {
1851
- [name: string]: unknown;
1852
- };
1853
- content: {
1854
- "application/json": components["schemas"]["ApiError"];
1855
- };
1856
- };
1857
2186
  };
1858
2187
  };
1859
2188
  verifyParent: {
@@ -1901,15 +2230,6 @@ export interface operations {
1901
2230
  "application/json": components["schemas"]["ApiError"];
1902
2231
  };
1903
2232
  };
1904
- /** @description The `dpdp_mobile_api_enabled` flag is off. */
1905
- 404: {
1906
- headers: {
1907
- [name: string]: unknown;
1908
- };
1909
- content: {
1910
- "application/json": components["schemas"]["ApiError"];
1911
- };
1912
- };
1913
2233
  };
1914
2234
  };
1915
2235
  listDueRetentions: {
@@ -1948,15 +2268,6 @@ export interface operations {
1948
2268
  "application/json": components["schemas"]["ApiError"];
1949
2269
  };
1950
2270
  };
1951
- /** @description The `dpdp_retention_due_api_enabled` flag is off. */
1952
- 404: {
1953
- headers: {
1954
- [name: string]: unknown;
1955
- };
1956
- content: {
1957
- "application/json": components["schemas"]["ApiError"];
1958
- };
1959
- };
1960
2271
  };
1961
2272
  };
1962
2273
  getConsentStatus: {
@@ -1989,6 +2300,11 @@ export interface operations {
1989
2300
  status: "opt_in" | "opt_out";
1990
2301
  /** @description Epoch ms this purpose was withdrawn, if any. */
1991
2302
  withdrawnAt?: number;
2303
+ /**
2304
+ * @description The reserved, platform-defined purpose this org's own `purposeKey` maps to (docs/specs/ecommerce-cms-plugins.md, §6), when a DPO has set one — so a plugin written once against e.g. `dpdp_reserved.marketing` works across every dpdpbot organization instead of string-matching a per-org label. Omitted when no mapping exists.
2305
+ * @enum {string}
2306
+ */
2307
+ standardPurpose?: "dpdp_reserved.marketing" | "dpdp_reserved.transactional" | "dpdp_reserved.analytics";
1992
2308
  }[];
1993
2309
  };
1994
2310
  };
@@ -2011,7 +2327,157 @@ export interface operations {
2011
2327
  "application/json": components["schemas"]["ApiError"];
2012
2328
  };
2013
2329
  };
2014
- /** @description The `dpdp_consent_status_lookup_enabled` flag is off. */
2330
+ };
2331
+ };
2332
+ recordDsrProcessorConfirmation: {
2333
+ parameters: {
2334
+ query?: never;
2335
+ header?: never;
2336
+ path: {
2337
+ requestId: string;
2338
+ };
2339
+ cookie?: never;
2340
+ };
2341
+ requestBody: {
2342
+ content: {
2343
+ "application/json": {
2344
+ processorName: string;
2345
+ /** @enum {string} */
2346
+ outcome: "completed" | "partial" | "failed";
2347
+ details: {
2348
+ system: string;
2349
+ action: string;
2350
+ note?: string;
2351
+ }[];
2352
+ };
2353
+ };
2354
+ };
2355
+ responses: {
2356
+ /** @description Confirmation recorded. */
2357
+ 201: {
2358
+ headers: {
2359
+ [name: string]: unknown;
2360
+ };
2361
+ content: {
2362
+ "application/json": {
2363
+ ok: boolean;
2364
+ };
2365
+ };
2366
+ };
2367
+ /** @description Malformed request body, or the request id does not identify an erasure request. */
2368
+ 400: {
2369
+ headers: {
2370
+ [name: string]: unknown;
2371
+ };
2372
+ content: {
2373
+ "application/json": components["schemas"]["ApiError"];
2374
+ };
2375
+ };
2376
+ /** @description Missing or invalid API key. */
2377
+ 401: {
2378
+ headers: {
2379
+ [name: string]: unknown;
2380
+ };
2381
+ content: {
2382
+ "application/json": components["schemas"]["ApiError"];
2383
+ };
2384
+ };
2385
+ /** @description The request id does not belong to the calling key's organization. */
2386
+ 404: {
2387
+ headers: {
2388
+ [name: string]: unknown;
2389
+ };
2390
+ content: {
2391
+ "application/json": components["schemas"]["ApiError"];
2392
+ };
2393
+ };
2394
+ };
2395
+ };
2396
+ acknowledgeWithdrawalPropagation: {
2397
+ parameters: {
2398
+ query?: never;
2399
+ header?: never;
2400
+ path: {
2401
+ propagationId: string;
2402
+ };
2403
+ cookie?: never;
2404
+ };
2405
+ requestBody?: never;
2406
+ responses: {
2407
+ /** @description Acknowledged (or already was). */
2408
+ 200: {
2409
+ headers: {
2410
+ [name: string]: unknown;
2411
+ };
2412
+ content: {
2413
+ "application/json": {
2414
+ ok: boolean;
2415
+ };
2416
+ };
2417
+ };
2418
+ /** @description Missing or invalid API key. */
2419
+ 401: {
2420
+ headers: {
2421
+ [name: string]: unknown;
2422
+ };
2423
+ content: {
2424
+ "application/json": components["schemas"]["ApiError"];
2425
+ };
2426
+ };
2427
+ /** @description The propagation id does not belong to the calling key's organization. */
2428
+ 404: {
2429
+ headers: {
2430
+ [name: string]: unknown;
2431
+ };
2432
+ content: {
2433
+ "application/json": components["schemas"]["ApiError"];
2434
+ };
2435
+ };
2436
+ };
2437
+ };
2438
+ getWithdrawalPropagationStatus: {
2439
+ parameters: {
2440
+ query?: never;
2441
+ header?: never;
2442
+ path: {
2443
+ suppressionId: string;
2444
+ };
2445
+ cookie?: never;
2446
+ };
2447
+ requestBody?: never;
2448
+ responses: {
2449
+ /** @description The suppression's fan-out status across destinations. */
2450
+ 200: {
2451
+ headers: {
2452
+ [name: string]: unknown;
2453
+ };
2454
+ content: {
2455
+ "application/json": {
2456
+ total: number;
2457
+ acknowledged: number;
2458
+ failed: number;
2459
+ entries: {
2460
+ destinationLabel: string;
2461
+ /** @enum {string} */
2462
+ status: "pending" | "acknowledged" | "overdue" | "failed";
2463
+ dispatchedAt: number;
2464
+ acknowledgedAt?: number;
2465
+ failedAt?: number;
2466
+ failureReason?: string;
2467
+ }[];
2468
+ };
2469
+ };
2470
+ };
2471
+ /** @description Missing or invalid API key. */
2472
+ 401: {
2473
+ headers: {
2474
+ [name: string]: unknown;
2475
+ };
2476
+ content: {
2477
+ "application/json": components["schemas"]["ApiError"];
2478
+ };
2479
+ };
2480
+ /** @description The suppression entry does not belong to the calling key's organization. */
2015
2481
  404: {
2016
2482
  headers: {
2017
2483
  [name: string]: unknown;
@@ -2022,6 +2488,50 @@ export interface operations {
2022
2488
  };
2023
2489
  };
2024
2490
  };
2491
+ listSuppressionList: {
2492
+ parameters: {
2493
+ query?: {
2494
+ /** @description Epoch ms; only entries suppressed strictly after this are returned. Omit (or pass 0) to pull the full list. */
2495
+ since?: number;
2496
+ };
2497
+ header?: never;
2498
+ path?: never;
2499
+ cookie?: never;
2500
+ };
2501
+ requestBody?: never;
2502
+ responses: {
2503
+ /** @description The calling key's organization's suppression-list entries after `since`. */
2504
+ 200: {
2505
+ headers: {
2506
+ [name: string]: unknown;
2507
+ };
2508
+ content: {
2509
+ "application/json": {
2510
+ entries: {
2511
+ identifier: string;
2512
+ /** @enum {string} */
2513
+ identifierType: "email" | "phone";
2514
+ /** @enum {string} */
2515
+ reason: "consent_withdrawal" | "dsr_erasure" | "manual";
2516
+ suppressedAt: number;
2517
+ liftedAt?: number;
2518
+ }[];
2519
+ /** @description Pass as the next call's `since` to continue the pull; `null` means the response was not truncated. */
2520
+ nextCursor: number | null;
2521
+ };
2522
+ };
2523
+ };
2524
+ /** @description Missing or invalid API key. */
2525
+ 401: {
2526
+ headers: {
2527
+ [name: string]: unknown;
2528
+ };
2529
+ content: {
2530
+ "application/json": components["schemas"]["ApiError"];
2531
+ };
2532
+ };
2533
+ };
2534
+ };
2025
2535
  listBreaches: {
2026
2536
  parameters: {
2027
2537
  query?: never;
@@ -2063,7 +2573,54 @@ export interface operations {
2063
2573
  "application/json": components["schemas"]["ApiError"];
2064
2574
  };
2065
2575
  };
2066
- /** @description The `dpdp_breach_register_api_enabled` flag is off. */
2576
+ };
2577
+ };
2578
+ getConsentPortabilityManifest: {
2579
+ parameters: {
2580
+ query?: never;
2581
+ header?: never;
2582
+ path: {
2583
+ exportId: string;
2584
+ };
2585
+ cookie?: never;
2586
+ };
2587
+ requestBody?: never;
2588
+ responses: {
2589
+ /** @description The export's signed manifest. */
2590
+ 200: {
2591
+ headers: {
2592
+ [name: string]: unknown;
2593
+ };
2594
+ content: {
2595
+ "application/json": {
2596
+ exportId: string;
2597
+ organizationId: string;
2598
+ schemaVersion: string;
2599
+ /** @enum {string} */
2600
+ status: "pending" | "building" | "complete" | "failed";
2601
+ /** @enum {string} */
2602
+ reason: "consent_manager_change" | "solution_replacement" | "regulatory_portability" | "routine_drill";
2603
+ scopeFromMs: number;
2604
+ scopeToMs: number;
2605
+ consentRecordCount: number;
2606
+ runningDigest?: string;
2607
+ manifestSignature?: string;
2608
+ manifestSignatureKeyId?: string;
2609
+ completedAt?: number;
2610
+ verificationRecipe?: string;
2611
+ };
2612
+ };
2613
+ };
2614
+ /** @description Missing or invalid API key. */
2615
+ 401: {
2616
+ headers: {
2617
+ [name: string]: unknown;
2618
+ };
2619
+ content: {
2620
+ "application/json": components["schemas"]["ApiError"];
2621
+ };
2622
+ };
2623
+ /** @description The export does not exist or belongs to a different organization. */
2067
2624
  404: {
2068
2625
  headers: {
2069
2626
  [name: string]: unknown;
@@ -2123,7 +2680,7 @@ export interface operations {
2123
2680
  "application/json": components["schemas"]["ApiError"];
2124
2681
  };
2125
2682
  };
2126
- /** @description The `dpdp_consent_gate_api_enabled` flag is off, or the consent does not exist or belongs to another organization. */
2683
+ /** @description The consent does not exist or belongs to another organization. */
2127
2684
  404: {
2128
2685
  headers: {
2129
2686
  [name: string]: unknown;
@@ -2175,15 +2732,6 @@ export interface operations {
2175
2732
  "application/json": components["schemas"]["ApiError"];
2176
2733
  };
2177
2734
  };
2178
- /** @description The `dpdp_consent_gate_audit_api_enabled` flag is off. */
2179
- 404: {
2180
- headers: {
2181
- [name: string]: unknown;
2182
- };
2183
- content: {
2184
- "application/json": components["schemas"]["ApiError"];
2185
- };
2186
- };
2187
2735
  };
2188
2736
  };
2189
2737
  listConsentGateBypassAlerts: {
@@ -2206,21 +2754,12 @@ export interface operations {
2206
2754
  /** @description The rolling window, in milliseconds, a deny still counts toward an alert within. */
2207
2755
  windowMs: number;
2208
2756
  /** @description In-window denies against one consent that constitute an alert. */
2209
- denyThreshold: number;
2210
- };
2211
- };
2212
- };
2213
- /** @description Missing, invalid, or revoked API key. */
2214
- 401: {
2215
- headers: {
2216
- [name: string]: unknown;
2217
- };
2218
- content: {
2219
- "application/json": components["schemas"]["ApiError"];
2757
+ denyThreshold: number;
2758
+ };
2220
2759
  };
2221
2760
  };
2222
- /** @description The `dpdp_consent_gate_audit_api_enabled` flag is off. */
2223
- 404: {
2761
+ /** @description Missing, invalid, or revoked API key. */
2762
+ 401: {
2224
2763
  headers: {
2225
2764
  [name: string]: unknown;
2226
2765
  };
@@ -2268,7 +2807,68 @@ export interface operations {
2268
2807
  "application/json": components["schemas"]["ApiError"];
2269
2808
  };
2270
2809
  };
2271
- /** @description The `dpdp_cross_border_transfers_api_enabled` flag is off. */
2810
+ };
2811
+ };
2812
+ ingestDiscoveryManifest: {
2813
+ parameters: {
2814
+ query?: never;
2815
+ header?: never;
2816
+ path?: never;
2817
+ cookie?: never;
2818
+ };
2819
+ requestBody: {
2820
+ content: {
2821
+ "application/json": {
2822
+ /** @description The discovery source to ingest into, as shown in the console. Must belong to the calling key's organization and must not be archived. */
2823
+ sourceId: string;
2824
+ columns: {
2825
+ /** @description e.g. `public.customers`. */
2826
+ tablePath: string;
2827
+ columnName: string;
2828
+ /** @description The database's own type, e.g. `varchar`. */
2829
+ declaredType?: string;
2830
+ /** @description Opt-in only, and never stored. See the description above before sending these. */
2831
+ sampleValues?: string[];
2832
+ }[];
2833
+ /** @description Tables this manifest describes in full. Columns on file under them and absent here are retired. */
2834
+ reconcileTablePaths?: string[];
2835
+ };
2836
+ };
2837
+ };
2838
+ responses: {
2839
+ /** @description What the ingestion classified, added, updated and retired. */
2840
+ 200: {
2841
+ headers: {
2842
+ [name: string]: unknown;
2843
+ };
2844
+ content: {
2845
+ "application/json": {
2846
+ fieldsClassified: number;
2847
+ fieldsNew: number;
2848
+ fieldsUpdated: number;
2849
+ fieldsRetired: number;
2850
+ };
2851
+ };
2852
+ };
2853
+ /** @description Malformed body, a manifest over the per-call column limit, sample values for a source that has not opted in, or a reconciled table the manifest does not describe. */
2854
+ 400: {
2855
+ headers: {
2856
+ [name: string]: unknown;
2857
+ };
2858
+ content: {
2859
+ "application/json": components["schemas"]["ApiError"];
2860
+ };
2861
+ };
2862
+ /** @description Missing or invalid API key. */
2863
+ 401: {
2864
+ headers: {
2865
+ [name: string]: unknown;
2866
+ };
2867
+ content: {
2868
+ "application/json": components["schemas"]["ApiError"];
2869
+ };
2870
+ };
2871
+ /** @description No such source for the calling key's organization. */
2272
2872
  404: {
2273
2873
  headers: {
2274
2874
  [name: string]: unknown;
@@ -2277,6 +2877,15 @@ export interface operations {
2277
2877
  "application/json": components["schemas"]["ApiError"];
2278
2878
  };
2279
2879
  };
2880
+ /** @description The source is archived. */
2881
+ 409: {
2882
+ headers: {
2883
+ [name: string]: unknown;
2884
+ };
2885
+ content: {
2886
+ "application/json": components["schemas"]["ApiError"];
2887
+ };
2888
+ };
2280
2889
  };
2281
2890
  };
2282
2891
  createOfflineConsentLink: {
@@ -2319,7 +2928,7 @@ export interface operations {
2319
2928
  "application/json": components["schemas"]["ApiError"];
2320
2929
  };
2321
2930
  };
2322
- /** @description Notice not found (or belongs to a different organization). Also returned for every request while the `dpdp_offline_consent_link_api_enabled` flag is off. */
2931
+ /** @description Notice not found (or belongs to a different organization). */
2323
2932
  404: {
2324
2933
  headers: {
2325
2934
  [name: string]: unknown;
@@ -2383,15 +2992,6 @@ export interface operations {
2383
2992
  "application/json": components["schemas"]["ApiError"];
2384
2993
  };
2385
2994
  };
2386
- /** @description The `dpdp_offline_agent_capture_enabled` flag is off. */
2387
- 404: {
2388
- headers: {
2389
- [name: string]: unknown;
2390
- };
2391
- content: {
2392
- "application/json": components["schemas"]["ApiError"];
2393
- };
2394
- };
2395
2995
  /** @description Per-API-key rate limit exceeded. */
2396
2996
  429: {
2397
2997
  headers: {
@@ -2454,7 +3054,7 @@ export interface operations {
2454
3054
  "application/json": components["schemas"]["ApiError"];
2455
3055
  };
2456
3056
  };
2457
- /** @description Notice not found, or the `dpdp_offline_async_consent_enabled` flag is off. */
3057
+ /** @description Notice not found. */
2458
3058
  404: {
2459
3059
  headers: {
2460
3060
  [name: string]: unknown;
@@ -2518,7 +3118,7 @@ export interface operations {
2518
3118
  "application/json": components["schemas"]["ApiError"];
2519
3119
  };
2520
3120
  };
2521
- /** @description Notice not found, or the `dpdp_offline_async_consent_enabled` flag is off. */
3121
+ /** @description Notice not found. */
2522
3122
  404: {
2523
3123
  headers: {
2524
3124
  [name: string]: unknown;
@@ -2538,29 +3138,39 @@ export interface operations {
2538
3138
  };
2539
3139
  };
2540
3140
  };
2541
- staffListDsrRequests: {
3141
+ smsWhatsappWithdraw: {
2542
3142
  parameters: {
2543
- query: {
2544
- organizationId: string;
2545
- };
3143
+ query?: never;
2546
3144
  header?: never;
2547
3145
  path?: never;
2548
3146
  cookie?: never;
2549
3147
  };
2550
- requestBody?: never;
3148
+ requestBody: {
3149
+ content: {
3150
+ "application/json": {
3151
+ /** @description Raw number as the gateway received it; normalised and HMAC'd per-organization before lookup. Never stored in the clear. */
3152
+ phone: string;
3153
+ /** @description The inbound reply text, verbatim. */
3154
+ message: string;
3155
+ };
3156
+ };
3157
+ };
2551
3158
  responses: {
2552
- /** @description The organization's DSR requests. */
3159
+ /** @description Request processed (regardless of whether a keyword matched). */
2553
3160
  200: {
2554
3161
  headers: {
2555
3162
  [name: string]: unknown;
2556
3163
  };
2557
3164
  content: {
2558
3165
  "application/json": {
2559
- requests: Record<string, never>[];
3166
+ /** @description Whether the message matched a withdrawal keyword. */
3167
+ matched: boolean;
3168
+ /** @description Whether at least one receipt for this phone number was withdrawn as a result. */
3169
+ withdrawn: boolean;
2560
3170
  };
2561
3171
  };
2562
3172
  };
2563
- /** @description Missing organizationId, or the caller isn't staff at that org. */
3173
+ /** @description Malformed body, or the phone number contains no digits. */
2564
3174
  400: {
2565
3175
  headers: {
2566
3176
  [name: string]: unknown;
@@ -2569,7 +3179,7 @@ export interface operations {
2569
3179
  "application/json": components["schemas"]["ApiError"];
2570
3180
  };
2571
3181
  };
2572
- /** @description Not authenticated. */
3182
+ /** @description Missing, invalid, or revoked API key. */
2573
3183
  401: {
2574
3184
  headers: {
2575
3185
  [name: string]: unknown;
@@ -2578,8 +3188,8 @@ export interface operations {
2578
3188
  "application/json": components["schemas"]["ApiError"];
2579
3189
  };
2580
3190
  };
2581
- /** @description The `dpdp_mobile_api_enabled` flag is off. */
2582
- 404: {
3191
+ /** @description Per-API-key rate limit exceeded. */
3192
+ 429: {
2583
3193
  headers: {
2584
3194
  [name: string]: unknown;
2585
3195
  };
@@ -2589,10 +3199,10 @@ export interface operations {
2589
3199
  };
2590
3200
  };
2591
3201
  };
2592
- staffListStatutoryBasisSuggestions: {
3202
+ staffListDsrRequests: {
2593
3203
  parameters: {
2594
- query?: {
2595
- sector?: string;
3204
+ query: {
3205
+ organizationId: string;
2596
3206
  };
2597
3207
  header?: never;
2598
3208
  path?: never;
@@ -2600,17 +3210,26 @@ export interface operations {
2600
3210
  };
2601
3211
  requestBody?: never;
2602
3212
  responses: {
2603
- /** @description Suggestions (empty if the feature flag is off). */
3213
+ /** @description The organization's DSR requests. */
2604
3214
  200: {
2605
3215
  headers: {
2606
3216
  [name: string]: unknown;
2607
3217
  };
2608
3218
  content: {
2609
3219
  "application/json": {
2610
- suggestions: Record<string, never>[];
3220
+ requests: Record<string, never>[];
2611
3221
  };
2612
3222
  };
2613
3223
  };
3224
+ /** @description Missing organizationId, or the caller isn't staff at that org. */
3225
+ 400: {
3226
+ headers: {
3227
+ [name: string]: unknown;
3228
+ };
3229
+ content: {
3230
+ "application/json": components["schemas"]["ApiError"];
3231
+ };
3232
+ };
2614
3233
  /** @description Not authenticated. */
2615
3234
  401: {
2616
3235
  headers: {
@@ -2620,8 +3239,32 @@ export interface operations {
2620
3239
  "application/json": components["schemas"]["ApiError"];
2621
3240
  };
2622
3241
  };
2623
- /** @description The `dpdp_mobile_api_enabled` flag is off. */
2624
- 404: {
3242
+ };
3243
+ };
3244
+ staffListStatutoryBasisSuggestions: {
3245
+ parameters: {
3246
+ query?: {
3247
+ sector?: string;
3248
+ };
3249
+ header?: never;
3250
+ path?: never;
3251
+ cookie?: never;
3252
+ };
3253
+ requestBody?: never;
3254
+ responses: {
3255
+ /** @description Suggestions. */
3256
+ 200: {
3257
+ headers: {
3258
+ [name: string]: unknown;
3259
+ };
3260
+ content: {
3261
+ "application/json": {
3262
+ suggestions: Record<string, never>[];
3263
+ };
3264
+ };
3265
+ };
3266
+ /** @description Not authenticated. */
3267
+ 401: {
2625
3268
  headers: {
2626
3269
  [name: string]: unknown;
2627
3270
  };
@@ -2753,7 +3396,7 @@ export interface operations {
2753
3396
  };
2754
3397
  };
2755
3398
  };
2756
- /** @description Missing fields, flag off, wrong request type, invalid transition, or the caller isn't staff at that org. */
3399
+ /** @description Missing fields, wrong request type, invalid transition, or the caller isn't staff at that org. */
2757
3400
  400: {
2758
3401
  headers: {
2759
3402
  [name: string]: unknown;
@@ -2813,15 +3456,6 @@ export interface operations {
2813
3456
  "application/json": components["schemas"]["ApiError"];
2814
3457
  };
2815
3458
  };
2816
- /** @description The `dpdp_mobile_api_enabled` flag is off. */
2817
- 404: {
2818
- headers: {
2819
- [name: string]: unknown;
2820
- };
2821
- content: {
2822
- "application/json": components["schemas"]["ApiError"];
2823
- };
2824
- };
2825
3459
  };
2826
3460
  };
2827
3461
  staffRespondToGrievance: {
@@ -2874,4 +3508,151 @@ export interface operations {
2874
3508
  };
2875
3509
  };
2876
3510
  };
3511
+ ingestObservatoryObservations: {
3512
+ parameters: {
3513
+ query?: never;
3514
+ header?: never;
3515
+ path?: never;
3516
+ cookie?: never;
3517
+ };
3518
+ requestBody: {
3519
+ content: {
3520
+ "application/json": {
3521
+ batch: components["schemas"]["ObservatoryObservation"][];
3522
+ };
3523
+ };
3524
+ };
3525
+ responses: {
3526
+ /** @description Per-observation results. A 202 does **not** mean every observation was accepted — inspect `rejected`. */
3527
+ 202: {
3528
+ headers: {
3529
+ [name: string]: unknown;
3530
+ };
3531
+ content: {
3532
+ "application/json": {
3533
+ accepted: number;
3534
+ rejected: {
3535
+ index: number;
3536
+ reason: string;
3537
+ }[];
3538
+ };
3539
+ };
3540
+ };
3541
+ /** @description Malformed body, missing `batch`, or an empty batch. */
3542
+ 400: {
3543
+ headers: {
3544
+ [name: string]: unknown;
3545
+ };
3546
+ content: {
3547
+ "application/json": components["schemas"]["ApiError"];
3548
+ };
3549
+ };
3550
+ /** @description More than 50 observations, or the body exceeds 256KB. */
3551
+ 413: {
3552
+ headers: {
3553
+ [name: string]: unknown;
3554
+ };
3555
+ content: {
3556
+ "application/json": components["schemas"]["ApiError"];
3557
+ };
3558
+ };
3559
+ };
3560
+ };
3561
+ getObservatoryDomainVerdict: {
3562
+ parameters: {
3563
+ query?: never;
3564
+ header?: never;
3565
+ path: {
3566
+ /** @description The domain to look up, e.g. `example.com`. */
3567
+ domain: string;
3568
+ };
3569
+ cookie?: never;
3570
+ };
3571
+ requestBody?: never;
3572
+ responses: {
3573
+ /** @description The domain's published verdict. */
3574
+ 200: {
3575
+ headers: {
3576
+ [name: string]: unknown;
3577
+ };
3578
+ content: {
3579
+ "application/json": components["schemas"]["ObservatoryDomainVerdict"];
3580
+ };
3581
+ };
3582
+ /** @description No published verdict for this domain, or the `domain` path segment is missing. */
3583
+ 404: {
3584
+ headers: {
3585
+ [name: string]: unknown;
3586
+ };
3587
+ content: {
3588
+ "application/json": components["schemas"]["ApiError"];
3589
+ };
3590
+ };
3591
+ };
3592
+ };
3593
+ getObservatorySectorReport: {
3594
+ parameters: {
3595
+ query?: never;
3596
+ header?: never;
3597
+ path: {
3598
+ sector: "ecommerce" | "bfsi" | "healthtech" | "edtech" | "travel" | "media" | "gov_adjacent";
3599
+ };
3600
+ cookie?: never;
3601
+ };
3602
+ requestBody?: never;
3603
+ responses: {
3604
+ /** @description CSV with header `sector,cohortSize,averageScore,grade,count` — one row per grade in the sector's distribution. */
3605
+ 200: {
3606
+ headers: {
3607
+ [name: string]: unknown;
3608
+ };
3609
+ content: {
3610
+ "text/csv": string;
3611
+ };
3612
+ };
3613
+ /** @description `sector` is not a recognized sector. */
3614
+ 404: {
3615
+ headers: {
3616
+ [name: string]: unknown;
3617
+ };
3618
+ content: {
3619
+ "application/json": components["schemas"]["ApiError"];
3620
+ };
3621
+ };
3622
+ };
3623
+ };
3624
+ deleteObservatoryContributions: {
3625
+ parameters: {
3626
+ query?: never;
3627
+ header?: never;
3628
+ path: {
3629
+ /** @description The extension-local, salted contributor hash to erase. */
3630
+ contributorHash: string;
3631
+ };
3632
+ cookie?: never;
3633
+ };
3634
+ requestBody?: never;
3635
+ responses: {
3636
+ /** @description Every observation for this hash has been deleted (or there were none). */
3637
+ 200: {
3638
+ headers: {
3639
+ [name: string]: unknown;
3640
+ };
3641
+ content: {
3642
+ "application/json": {
3643
+ deleted: boolean;
3644
+ };
3645
+ };
3646
+ };
3647
+ /** @description The `contributorHash` path segment is missing or malformed. */
3648
+ 404: {
3649
+ headers: {
3650
+ [name: string]: unknown;
3651
+ };
3652
+ content: {
3653
+ "application/json": components["schemas"]["ApiError"];
3654
+ };
3655
+ };
3656
+ };
3657
+ };
2877
3658
  }