sf-queue-sdk 0.3.1b256__tar.gz → 0.4.0b7__tar.gz

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.
Files changed (30) hide show
  1. {sf_queue_sdk-0.3.1b256 → sf_queue_sdk-0.4.0b7}/.gitignore +4 -0
  2. sf_queue_sdk-0.3.1b256/README.md → sf_queue_sdk-0.4.0b7/PKG-INFO +47 -20
  3. sf_queue_sdk-0.3.1b256/PKG-INFO → sf_queue_sdk-0.4.0b7/README.md +39 -28
  4. {sf_queue_sdk-0.3.1b256 → sf_queue_sdk-0.4.0b7}/pyproject.toml +1 -1
  5. {sf_queue_sdk-0.3.1b256 → sf_queue_sdk-0.4.0b7}/queue_sdk/__init__.py +1 -1
  6. {sf_queue_sdk-0.3.1b256 → sf_queue_sdk-0.4.0b7}/queue_sdk/generated/queue/email/v1/email_pb2.py +6 -6
  7. {sf_queue_sdk-0.3.1b256 → sf_queue_sdk-0.4.0b7}/queue_sdk/generated/queue/email/v1/email_pb2.pyi +8 -4
  8. {sf_queue_sdk-0.3.1b256 → sf_queue_sdk-0.4.0b7}/queue_sdk/queues/document_notes_queue.py +20 -0
  9. {sf_queue_sdk-0.3.1b256 → sf_queue_sdk-0.4.0b7}/queue_sdk/queues/email_queue.py +66 -12
  10. {sf_queue_sdk-0.3.1b256 → sf_queue_sdk-0.4.0b7}/queue_sdk/types.py +3 -0
  11. {sf_queue_sdk-0.3.1b256 → sf_queue_sdk-0.4.0b7}/queue_sdk/client.py +0 -0
  12. {sf_queue_sdk-0.3.1b256 → sf_queue_sdk-0.4.0b7}/queue_sdk/generated/__init__.py +0 -0
  13. {sf_queue_sdk-0.3.1b256 → sf_queue_sdk-0.4.0b7}/queue_sdk/generated/queue/prompt_ingest/v1/prompt_ingest_pb2.py +0 -0
  14. {sf_queue_sdk-0.3.1b256 → sf_queue_sdk-0.4.0b7}/queue_sdk/generated/queue/prompt_ingest/v1/prompt_ingest_pb2.pyi +0 -0
  15. {sf_queue_sdk-0.3.1b256 → sf_queue_sdk-0.4.0b7}/queue_sdk/generated/queue/tool_assignment/v1/tool_assignment_pb2.py +0 -0
  16. {sf_queue_sdk-0.3.1b256 → sf_queue_sdk-0.4.0b7}/queue_sdk/generated/queue/tool_assignment/v1/tool_assignment_pb2.pyi +0 -0
  17. {sf_queue_sdk-0.3.1b256 → sf_queue_sdk-0.4.0b7}/queue_sdk/generated/queue/topic_assignment/v1/topic_assignment_pb2.py +0 -0
  18. {sf_queue_sdk-0.3.1b256 → sf_queue_sdk-0.4.0b7}/queue_sdk/generated/queue/topic_assignment/v1/topic_assignment_pb2.pyi +0 -0
  19. {sf_queue_sdk-0.3.1b256 → sf_queue_sdk-0.4.0b7}/queue_sdk/query.py +0 -0
  20. {sf_queue_sdk-0.3.1b256 → sf_queue_sdk-0.4.0b7}/queue_sdk/queues/__init__.py +0 -0
  21. {sf_queue_sdk-0.3.1b256 → sf_queue_sdk-0.4.0b7}/queue_sdk/queues/base_queue.py +0 -0
  22. {sf_queue_sdk-0.3.1b256 → sf_queue_sdk-0.4.0b7}/queue_sdk/queues/document_persist_queue.py +0 -0
  23. {sf_queue_sdk-0.3.1b256 → sf_queue_sdk-0.4.0b7}/queue_sdk/queues/document_upload_queue.py +0 -0
  24. {sf_queue_sdk-0.3.1b256 → sf_queue_sdk-0.4.0b7}/queue_sdk/queues/pdf_to_image_queue.py +0 -0
  25. {sf_queue_sdk-0.3.1b256 → sf_queue_sdk-0.4.0b7}/queue_sdk/queues/prompt_ingest_queue.py +0 -0
  26. {sf_queue_sdk-0.3.1b256 → sf_queue_sdk-0.4.0b7}/queue_sdk/queues/recommendation_queue.py +0 -0
  27. {sf_queue_sdk-0.3.1b256 → sf_queue_sdk-0.4.0b7}/queue_sdk/queues/tool_assignment_queue.py +0 -0
  28. {sf_queue_sdk-0.3.1b256 → sf_queue_sdk-0.4.0b7}/queue_sdk/queues/topic_assignment_queue.py +0 -0
  29. {sf_queue_sdk-0.3.1b256 → sf_queue_sdk-0.4.0b7}/queue_sdk/redis_client.py +0 -0
  30. {sf_queue_sdk-0.3.1b256 → sf_queue_sdk-0.4.0b7}/queue_sdk/sanitize.py +0 -0
@@ -55,3 +55,7 @@ coverage/
55
55
  # packages/ts/queue-sdk/src/generated/ -- committed
56
56
  # packages/py/queue-sdk/queue_sdk/generated/ -- committed
57
57
  TODO-document-upload-queue.md
58
+
59
+ # Local pipeline test output
60
+ scripts/.env
61
+ scripts/local-pdfs/
@@ -1,3 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: sf-queue-sdk
3
+ Version: 0.4.0b7
4
+ Summary: Python SDK for sf-queue - Redis-based queue system
5
+ Requires-Python: >=3.11
6
+ Requires-Dist: redis>=5.0.0
7
+ Description-Content-Type: text/markdown
8
+
1
9
  # sf-queue-sdk
2
10
 
3
11
  Python SDK for sf-queue. Enqueues emails via Redis Streams with optional blocking confirmation from the Go consumer service.
@@ -144,7 +152,6 @@ print(result.error) # error message if all failed
144
152
  | `image` | `{"src": str, "alt"?: str, "width"?: int, "height"?: int}` | No | Image in email body |
145
153
  | `service` | `str` | No | Service name for routing (e.g. `"sparkles"`). Routes to service-specific credentials, from address, and template. Defaults to the base config when omitted. |
146
154
  | `template` | `str` | No | Template name override (e.g. `"sparkles"`). Overrides the service's default template. Falls back to `"studyfetch"` when omitted. |
147
- | `brand` | `{"name"?: str, "logoUrl"?: str, "url"?: str, "accentColor"?: str}` | No | Per-workspace whitelabel branding (Honen template). `name` sets the wordmark/footer text **and** the From display name (the sending address and Resend key are unchanged, preserving the verified domain). Also overrides the logo, footer + header + unsubscribe link base, and CTA color. Any omitted field falls back to the default Honen brand. |
148
155
 
149
156
  ## Optional Fields
150
157
 
@@ -192,31 +199,28 @@ client.email.send(
192
199
 
193
200
  ## Whitelabel Branding
194
201
 
195
- Pass `brand` to render the Honen template with a workspace's own identity. Any
196
- omitted field falls back to the default Honen brand, so unbranded emails are
197
- unaffected.
202
+ The Honen template accepts optional brand overrides. Empty fields retain the
203
+ standard Honen identity.
198
204
 
199
205
  ```python
200
206
  client.email.send(
201
207
  to="member@acme.com",
202
208
  preview="You have been invited",
203
- subject="Join the Acme workspace",
209
+ subject="Join Acme",
204
210
  paragraphs=["You have been invited to Acme."],
205
- button={"text": "Accept invite", "href": "https://learn.acme.com/invite/abc"},
211
+ service="honen",
212
+ template="honen",
206
213
  brand={
207
- "name": "Acme", # wordmark + From display name ("Acme <noreply@...>")
214
+ "name": "Acme",
208
215
  "logoUrl": "https://cdn.acme.com/logo.png",
209
- "url": "https://learn.acme.com", # header/footer/unsubscribe link base
210
- "accentColor": "#0043FF", # CTA button color
216
+ "url": "https://learn.acme.com",
217
+ "accentColor": "#0043FF",
211
218
  },
212
219
  )
213
220
  ```
214
221
 
215
- The `name` also becomes the From display name: the email arrives from
216
- `Acme <noreply@honen.studyfetch.com>` instead of `Honen <noreply@...>`. Only the
217
- display label changes — the verified sending address and the service's Resend
218
- key stay the same, so DKIM/SPF alignment is unaffected. Omit `name` (or the whole
219
- `brand`) to keep the default Honen From.
222
+ `name` also becomes the From display name while the selected route or validated
223
+ From override continues to control the sending address and provider key.
220
224
 
221
225
  ## Migrating from sendEmail / sendBatchEmail
222
226
 
@@ -594,8 +598,8 @@ result = client.document_upload.send(
594
598
  | `url` | `str` | Yes | Download URL for the source file |
595
599
  | `generate_notes` | `bool` | Yes | Whether to generate study notes |
596
600
  | `generate_study_path` | `bool` | Yes | Whether to generate a study path |
597
- | `splitting` | `str` | No | Splitting strategy (default: `"auto"`) |
598
- | `language` | `str` | No | Target language code (default: `"en"`) |
601
+ | `splitting` | `str` | No | Splitting strategy for sources with at least 40,000 tokens (default: `"auto"`). Smaller sources always persist as one material after per-topic note generation. |
602
+ | `language` | `str` | No | Output language as an app ISO code, e.g. `"de"` — not a locale like `"de-DE"`. Note the non-standard `cz` (Czech) and `gr` (Greek). (default: `"en"`) |
599
603
  | `use_markdown_ocr` | `bool` | No | Use Markdown OCR for image interpretation (default: `False`) |
600
604
  | `flashcard_amount` | `int` | No | Number of flashcards to generate (default: `12`) |
601
605
  | `new_url` | `Optional[str]` | No | Alternative URL if file was re-uploaded |
@@ -677,7 +681,7 @@ result = client.document_notes.send(
677
681
  uploaded_file_id="file_abc123",
678
682
  study_set_id="set_xyz",
679
683
  user_id="user_123",
680
- stash_key="clustering-stash/file_abc123/1720000000.json",
684
+ stash_key="clustering-stash/set_xyz/file_abc123.json",
681
685
  is_deferred=True,
682
686
  callback_url="https://api.example.com/completeCluster",
683
687
  callback_api_key="secret",
@@ -693,7 +697,7 @@ result = client.document_notes.send(
693
697
  uploaded_file_id="file_abc123",
694
698
  study_set_id="set_xyz",
695
699
  user_id="user_123",
696
- stash_key="clustering-stash/file_abc123/1720000000.json",
700
+ stash_key="clustering-stash/set_xyz/file_abc123.json",
697
701
  is_deferred=False,
698
702
  generate_notes=False,
699
703
  folder_id="folder_abc",
@@ -702,6 +706,25 @@ result = client.document_notes.send(
702
706
  )
703
707
  ```
704
708
 
709
+ ### Note regeneration for existing materials
710
+
711
+ Use `skip_material_creation` and `existing_material_id` to regenerate notes for an existing `noNotes` material using its original clustering stash. The stash key is deterministic: `clustering-stash/{study_set_id}/{uploaded_file_id}.json`.
712
+
713
+ ```python
714
+ result = client.document_notes.send(
715
+ uploaded_file_id="file_abc123",
716
+ study_set_id="set_xyz",
717
+ user_id="user_123",
718
+ stash_key="clustering-stash/set_xyz/file_abc123.json",
719
+ is_deferred=False,
720
+ generate_notes=True,
721
+ skip_material_creation=True,
722
+ existing_material_id="mat_existing_456",
723
+ callback_url="https://api.example.com/noNotesGen/callback",
724
+ callback_api_key="secret",
725
+ )
726
+ ```
727
+
705
728
  ### Document Notes Fields
706
729
 
707
730
  | Field | Type | Required | Description |
@@ -709,12 +732,16 @@ result = client.document_notes.send(
709
732
  | `uploaded_file_id` | `str` | Yes | ID of the uploaded file record |
710
733
  | `study_set_id` | `str` | Yes | Study set to persist materials into |
711
734
  | `user_id` | `str` | Yes | User who uploaded the document |
712
- | `stash_key` | `str` | Yes | GCS key of the clustering stash JSON |
735
+ | `stash_key` | `str` | Yes | GCS key of the clustering stash JSON (deterministic: `clustering-stash/{study_set_id}/{uploaded_file_id}.json`) |
713
736
  | `is_deferred` | `bool` | No | Whether the study set has existing topics requiring approval (default: `False`) |
714
737
  | `generate_notes` | `bool` | No | Whether to generate notes (default: `True`). Set to `False` for noNotes materials |
715
738
  | `callback_url` | `Optional[str]` | No | URL to POST results to after completion |
716
739
  | `callback_api_key` | `Optional[str]` | No | Auth key for the callback |
717
740
  | `folder_id` | `Optional[str]` | No | Pre-created folder ID for the materials |
741
+ | `skip_material_creation` | `Optional[bool]` | No | Skip creating new materials; update an existing one instead (used for note regeneration) |
742
+ | `existing_material_id` | `Optional[str]` | No | ID of the existing material to update with generated notes (used with `skip_material_creation`) |
743
+ | `target_topic_index` | `Optional[int]` | No | Index of the specific topic to regenerate notes for (matches `material.order`). Omit to generate all topics. |
744
+ | `append_mode` | `Optional[bool]` | No | When `True`, generated notes are appended to existing material content rather than replacing it |
718
745
 
719
746
  ### Response Types
720
747
 
@@ -768,7 +795,7 @@ result = client.document_persist.send(
768
795
  | `study_set_id` | `str` | Yes | Study set to persist materials into |
769
796
  | `user_id` | `str` | Yes | User who uploaded the document |
770
797
  | `stash_key` | `str` | Yes | GCS key of the clustering stash JSON |
771
- | `language` | `str` | No | Target language code (default: `"en"`) |
798
+ | `language` | `str` | No | Output language as an app ISO code, e.g. `"de"` — not a locale like `"de-DE"`. Note the non-standard `cz` (Czech) and `gr` (Greek). (default: `"en"`) |
772
799
  | `callback_url` | `Optional[str]` | No | URL to POST results to after persistence |
773
800
  | `callback_api_key` | `Optional[str]` | No | Auth key for the callback |
774
801
  | `approved_topic_ids` | `Optional[list[str]]` | No | Filter to only these topic IDs from the stash (omit to persist all) |
@@ -1,11 +1,3 @@
1
- Metadata-Version: 2.4
2
- Name: sf-queue-sdk
3
- Version: 0.3.1b256
4
- Summary: Python SDK for sf-queue - Redis-based queue system
5
- Requires-Python: >=3.11
6
- Requires-Dist: redis>=5.0.0
7
- Description-Content-Type: text/markdown
8
-
9
1
  # sf-queue-sdk
10
2
 
11
3
  Python SDK for sf-queue. Enqueues emails via Redis Streams with optional blocking confirmation from the Go consumer service.
@@ -152,7 +144,6 @@ print(result.error) # error message if all failed
152
144
  | `image` | `{"src": str, "alt"?: str, "width"?: int, "height"?: int}` | No | Image in email body |
153
145
  | `service` | `str` | No | Service name for routing (e.g. `"sparkles"`). Routes to service-specific credentials, from address, and template. Defaults to the base config when omitted. |
154
146
  | `template` | `str` | No | Template name override (e.g. `"sparkles"`). Overrides the service's default template. Falls back to `"studyfetch"` when omitted. |
155
- | `brand` | `{"name"?: str, "logoUrl"?: str, "url"?: str, "accentColor"?: str}` | No | Per-workspace whitelabel branding (Honen template). `name` sets the wordmark/footer text **and** the From display name (the sending address and Resend key are unchanged, preserving the verified domain). Also overrides the logo, footer + header + unsubscribe link base, and CTA color. Any omitted field falls back to the default Honen brand. |
156
147
 
157
148
  ## Optional Fields
158
149
 
@@ -200,31 +191,28 @@ client.email.send(
200
191
 
201
192
  ## Whitelabel Branding
202
193
 
203
- Pass `brand` to render the Honen template with a workspace's own identity. Any
204
- omitted field falls back to the default Honen brand, so unbranded emails are
205
- unaffected.
194
+ The Honen template accepts optional brand overrides. Empty fields retain the
195
+ standard Honen identity.
206
196
 
207
197
  ```python
208
198
  client.email.send(
209
199
  to="member@acme.com",
210
200
  preview="You have been invited",
211
- subject="Join the Acme workspace",
201
+ subject="Join Acme",
212
202
  paragraphs=["You have been invited to Acme."],
213
- button={"text": "Accept invite", "href": "https://learn.acme.com/invite/abc"},
203
+ service="honen",
204
+ template="honen",
214
205
  brand={
215
- "name": "Acme", # wordmark + From display name ("Acme <noreply@...>")
206
+ "name": "Acme",
216
207
  "logoUrl": "https://cdn.acme.com/logo.png",
217
- "url": "https://learn.acme.com", # header/footer/unsubscribe link base
218
- "accentColor": "#0043FF", # CTA button color
208
+ "url": "https://learn.acme.com",
209
+ "accentColor": "#0043FF",
219
210
  },
220
211
  )
221
212
  ```
222
213
 
223
- The `name` also becomes the From display name: the email arrives from
224
- `Acme <noreply@honen.studyfetch.com>` instead of `Honen <noreply@...>`. Only the
225
- display label changes — the verified sending address and the service's Resend
226
- key stay the same, so DKIM/SPF alignment is unaffected. Omit `name` (or the whole
227
- `brand`) to keep the default Honen From.
214
+ `name` also becomes the From display name while the selected route or validated
215
+ From override continues to control the sending address and provider key.
228
216
 
229
217
  ## Migrating from sendEmail / sendBatchEmail
230
218
 
@@ -602,8 +590,8 @@ result = client.document_upload.send(
602
590
  | `url` | `str` | Yes | Download URL for the source file |
603
591
  | `generate_notes` | `bool` | Yes | Whether to generate study notes |
604
592
  | `generate_study_path` | `bool` | Yes | Whether to generate a study path |
605
- | `splitting` | `str` | No | Splitting strategy (default: `"auto"`) |
606
- | `language` | `str` | No | Target language code (default: `"en"`) |
593
+ | `splitting` | `str` | No | Splitting strategy for sources with at least 40,000 tokens (default: `"auto"`). Smaller sources always persist as one material after per-topic note generation. |
594
+ | `language` | `str` | No | Output language as an app ISO code, e.g. `"de"` — not a locale like `"de-DE"`. Note the non-standard `cz` (Czech) and `gr` (Greek). (default: `"en"`) |
607
595
  | `use_markdown_ocr` | `bool` | No | Use Markdown OCR for image interpretation (default: `False`) |
608
596
  | `flashcard_amount` | `int` | No | Number of flashcards to generate (default: `12`) |
609
597
  | `new_url` | `Optional[str]` | No | Alternative URL if file was re-uploaded |
@@ -685,7 +673,7 @@ result = client.document_notes.send(
685
673
  uploaded_file_id="file_abc123",
686
674
  study_set_id="set_xyz",
687
675
  user_id="user_123",
688
- stash_key="clustering-stash/file_abc123/1720000000.json",
676
+ stash_key="clustering-stash/set_xyz/file_abc123.json",
689
677
  is_deferred=True,
690
678
  callback_url="https://api.example.com/completeCluster",
691
679
  callback_api_key="secret",
@@ -701,7 +689,7 @@ result = client.document_notes.send(
701
689
  uploaded_file_id="file_abc123",
702
690
  study_set_id="set_xyz",
703
691
  user_id="user_123",
704
- stash_key="clustering-stash/file_abc123/1720000000.json",
692
+ stash_key="clustering-stash/set_xyz/file_abc123.json",
705
693
  is_deferred=False,
706
694
  generate_notes=False,
707
695
  folder_id="folder_abc",
@@ -710,6 +698,25 @@ result = client.document_notes.send(
710
698
  )
711
699
  ```
712
700
 
701
+ ### Note regeneration for existing materials
702
+
703
+ Use `skip_material_creation` and `existing_material_id` to regenerate notes for an existing `noNotes` material using its original clustering stash. The stash key is deterministic: `clustering-stash/{study_set_id}/{uploaded_file_id}.json`.
704
+
705
+ ```python
706
+ result = client.document_notes.send(
707
+ uploaded_file_id="file_abc123",
708
+ study_set_id="set_xyz",
709
+ user_id="user_123",
710
+ stash_key="clustering-stash/set_xyz/file_abc123.json",
711
+ is_deferred=False,
712
+ generate_notes=True,
713
+ skip_material_creation=True,
714
+ existing_material_id="mat_existing_456",
715
+ callback_url="https://api.example.com/noNotesGen/callback",
716
+ callback_api_key="secret",
717
+ )
718
+ ```
719
+
713
720
  ### Document Notes Fields
714
721
 
715
722
  | Field | Type | Required | Description |
@@ -717,12 +724,16 @@ result = client.document_notes.send(
717
724
  | `uploaded_file_id` | `str` | Yes | ID of the uploaded file record |
718
725
  | `study_set_id` | `str` | Yes | Study set to persist materials into |
719
726
  | `user_id` | `str` | Yes | User who uploaded the document |
720
- | `stash_key` | `str` | Yes | GCS key of the clustering stash JSON |
727
+ | `stash_key` | `str` | Yes | GCS key of the clustering stash JSON (deterministic: `clustering-stash/{study_set_id}/{uploaded_file_id}.json`) |
721
728
  | `is_deferred` | `bool` | No | Whether the study set has existing topics requiring approval (default: `False`) |
722
729
  | `generate_notes` | `bool` | No | Whether to generate notes (default: `True`). Set to `False` for noNotes materials |
723
730
  | `callback_url` | `Optional[str]` | No | URL to POST results to after completion |
724
731
  | `callback_api_key` | `Optional[str]` | No | Auth key for the callback |
725
732
  | `folder_id` | `Optional[str]` | No | Pre-created folder ID for the materials |
733
+ | `skip_material_creation` | `Optional[bool]` | No | Skip creating new materials; update an existing one instead (used for note regeneration) |
734
+ | `existing_material_id` | `Optional[str]` | No | ID of the existing material to update with generated notes (used with `skip_material_creation`) |
735
+ | `target_topic_index` | `Optional[int]` | No | Index of the specific topic to regenerate notes for (matches `material.order`). Omit to generate all topics. |
736
+ | `append_mode` | `Optional[bool]` | No | When `True`, generated notes are appended to existing material content rather than replacing it |
726
737
 
727
738
  ### Response Types
728
739
 
@@ -776,7 +787,7 @@ result = client.document_persist.send(
776
787
  | `study_set_id` | `str` | Yes | Study set to persist materials into |
777
788
  | `user_id` | `str` | Yes | User who uploaded the document |
778
789
  | `stash_key` | `str` | Yes | GCS key of the clustering stash JSON |
779
- | `language` | `str` | No | Target language code (default: `"en"`) |
790
+ | `language` | `str` | No | Output language as an app ISO code, e.g. `"de"` — not a locale like `"de-DE"`. Note the non-standard `cz` (Czech) and `gr` (Greek). (default: `"en"`) |
780
791
  | `callback_url` | `Optional[str]` | No | URL to POST results to after persistence |
781
792
  | `callback_api_key` | `Optional[str]` | No | Auth key for the callback |
782
793
  | `approved_topic_ids` | `Optional[list[str]]` | No | Filter to only these topic IDs from the stash (omit to persist all) |
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sf-queue-sdk"
3
- version = "0.3.1b256"
3
+ version = "0.4.0b7"
4
4
  description = "Python SDK for sf-queue - Redis-based queue system"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -1,6 +1,6 @@
1
1
  """sf-queue Python SDK - Redis-based queue system."""
2
2
 
3
- __version__ = "0.3.1b256"
3
+ __version__ = "0.4.0b7"
4
4
 
5
5
  from queue_sdk.client import QueueClient
6
6
  from queue_sdk.query import QueryClient
@@ -24,7 +24,7 @@ _sym_db = _symbol_database.Default()
24
24
 
25
25
 
26
26
 
27
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1aqueue/email/v1/email.proto\x12\x0equeue.email.v1\"5\n\x0b\x45mailButton\x12\x12\n\x04text\x18\x01 \x01(\tR\x04text\x12\x12\n\x04href\x18\x02 \x01(\tR\x04href\"^\n\nEmailImage\x12\x10\n\x03src\x18\x01 \x01(\tR\x03src\x12\x10\n\x03\x61lt\x18\x02 \x01(\tR\x03\x61lt\x12\x14\n\x05width\x18\x03 \x01(\x05R\x05width\x12\x16\n\x06height\x18\x04 \x01(\x05R\x06height\"p\n\nEmailBrand\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x19\n\x08logo_url\x18\x02 \x01(\tR\x07logoUrl\x12\x10\n\x03url\x18\x03 \x01(\tR\x03url\x12!\n\x0c\x61\x63\x63\x65nt_color\x18\x04 \x01(\tR\x0b\x61\x63\x63\x65ntColor\"\xa3\x03\n\x0c\x45mailRequest\x12\x0e\n\x02to\x18\x01 \x01(\tR\x02to\x12\x18\n\x07preview\x18\x02 \x01(\tR\x07preview\x12\x18\n\x07subject\x18\x03 \x01(\tR\x07subject\x12\x1e\n\nparagraphs\x18\x04 \x03(\tR\nparagraphs\x12\x33\n\x06\x62utton\x18\x05 \x01(\x0b\x32\x1b.queue.email.v1.EmailButtonR\x06\x62utton\x12\x19\n\x08reply_to\x18\x06 \x01(\tR\x07replyTo\x12\x30\n\x05image\x18\x07 \x01(\x0b\x32\x1a.queue.email.v1.EmailImageR\x05image\x12\x18\n\x07service\x18\x08 \x01(\tR\x07service\x12\x1a\n\x08template\x18\t \x01(\tR\x08template\x12\x12\n\x04html\x18\n \x01(\tR\x04html\x12\x12\n\x04\x66rom\x18\x0b \x01(\tR\x04\x66rom\x12\x1d\n\nresend_key\x18\x0c \x01(\tR\tresendKey\x12\x30\n\x05\x62rand\x18\r \x01(\x0b\x32\x1a.queue.email.v1.EmailBrandR\x05\x62rand\"\xa8\x03\n\x11\x42\x61tchEmailRequest\x12\x0e\n\x02to\x18\x01 \x03(\tR\x02to\x12\x18\n\x07preview\x18\x02 \x01(\tR\x07preview\x12\x18\n\x07subject\x18\x03 \x01(\tR\x07subject\x12\x1e\n\nparagraphs\x18\x04 \x03(\tR\nparagraphs\x12\x33\n\x06\x62utton\x18\x05 \x01(\x0b\x32\x1b.queue.email.v1.EmailButtonR\x06\x62utton\x12\x19\n\x08reply_to\x18\x06 \x01(\tR\x07replyTo\x12\x30\n\x05image\x18\x07 \x01(\x0b\x32\x1a.queue.email.v1.EmailImageR\x05image\x12\x18\n\x07service\x18\x08 \x01(\tR\x07service\x12\x1a\n\x08template\x18\t \x01(\tR\x08template\x12\x12\n\x04html\x18\n \x01(\tR\x04html\x12\x12\n\x04\x66rom\x18\x0b \x01(\tR\x04\x66rom\x12\x1d\n\nresend_key\x18\x0c \x01(\tR\tresendKey\x12\x30\n\x05\x62rand\x18\r \x01(\x0b\x32\x1a.queue.email.v1.EmailBrandR\x05\x62rand\"^\n\rEmailResponse\x12\x18\n\x07success\x18\x01 \x01(\x08R\x07success\x12\x1d\n\nmessage_id\x18\x02 \x01(\tR\tmessageId\x12\x14\n\x05\x65rror\x18\x03 \x01(\tR\x05\x65rrorBLZJgithub.com/StudyFetch/sf-queue/packages/go/proto-go/queue/email/v1;emailv1b\x06proto3')
27
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1aqueue/email/v1/email.proto\x12\x0equeue.email.v1\"5\n\x0b\x45mailButton\x12\x12\n\x04text\x18\x01 \x01(\tR\x04text\x12\x12\n\x04href\x18\x02 \x01(\tR\x04href\"^\n\nEmailImage\x12\x10\n\x03src\x18\x01 \x01(\tR\x03src\x12\x10\n\x03\x61lt\x18\x02 \x01(\tR\x03\x61lt\x12\x14\n\x05width\x18\x03 \x01(\x05R\x05width\x12\x16\n\x06height\x18\x04 \x01(\x05R\x06height\"p\n\nEmailBrand\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x19\n\x08logo_url\x18\x02 \x01(\tR\x07logoUrl\x12\x10\n\x03url\x18\x03 \x01(\tR\x03url\x12!\n\x0c\x61\x63\x63\x65nt_color\x18\x04 \x01(\tR\x0b\x61\x63\x63\x65ntColor\"\xc0\x03\n\x0c\x45mailRequest\x12\x0e\n\x02to\x18\x01 \x01(\tR\x02to\x12\x18\n\x07preview\x18\x02 \x01(\tR\x07preview\x12\x18\n\x07subject\x18\x03 \x01(\tR\x07subject\x12\x1e\n\nparagraphs\x18\x04 \x03(\tR\nparagraphs\x12\x33\n\x06\x62utton\x18\x05 \x01(\x0b\x32\x1b.queue.email.v1.EmailButtonR\x06\x62utton\x12\x19\n\x08reply_to\x18\x06 \x01(\tR\x07replyTo\x12\x30\n\x05image\x18\x07 \x01(\x0b\x32\x1a.queue.email.v1.EmailImageR\x05image\x12\x18\n\x07service\x18\x08 \x01(\tR\x07service\x12\x1a\n\x08template\x18\t \x01(\tR\x08template\x12\x12\n\x04html\x18\n \x01(\tR\x04html\x12\x12\n\x04\x66rom\x18\x0b \x01(\tR\x04\x66rom\x12\x1d\n\nresend_key\x18\x0c \x01(\tR\tresendKey\x12\x30\n\x05\x62rand\x18\r \x01(\x0b\x32\x1a.queue.email.v1.EmailBrandR\x05\x62rand\x12\x1b\n\tbody_html\x18\x0e \x01(\tR\x08\x62odyHtml\"\xc5\x03\n\x11\x42\x61tchEmailRequest\x12\x0e\n\x02to\x18\x01 \x03(\tR\x02to\x12\x18\n\x07preview\x18\x02 \x01(\tR\x07preview\x12\x18\n\x07subject\x18\x03 \x01(\tR\x07subject\x12\x1e\n\nparagraphs\x18\x04 \x03(\tR\nparagraphs\x12\x33\n\x06\x62utton\x18\x05 \x01(\x0b\x32\x1b.queue.email.v1.EmailButtonR\x06\x62utton\x12\x19\n\x08reply_to\x18\x06 \x01(\tR\x07replyTo\x12\x30\n\x05image\x18\x07 \x01(\x0b\x32\x1a.queue.email.v1.EmailImageR\x05image\x12\x18\n\x07service\x18\x08 \x01(\tR\x07service\x12\x1a\n\x08template\x18\t \x01(\tR\x08template\x12\x12\n\x04html\x18\n \x01(\tR\x04html\x12\x12\n\x04\x66rom\x18\x0b \x01(\tR\x04\x66rom\x12\x1d\n\nresend_key\x18\x0c \x01(\tR\tresendKey\x12\x30\n\x05\x62rand\x18\r \x01(\x0b\x32\x1a.queue.email.v1.EmailBrandR\x05\x62rand\x12\x1b\n\tbody_html\x18\x0e \x01(\tR\x08\x62odyHtml\"^\n\rEmailResponse\x12\x18\n\x07success\x18\x01 \x01(\x08R\x07success\x12\x1d\n\nmessage_id\x18\x02 \x01(\tR\tmessageId\x12\x14\n\x05\x65rror\x18\x03 \x01(\tR\x05\x65rrorBLZJgithub.com/StudyFetch/sf-queue/packages/go/proto-go/queue/email/v1;emailv1b\x06proto3')
28
28
 
29
29
  _globals = globals()
30
30
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -39,9 +39,9 @@ if not _descriptor._USE_C_DESCRIPTORS:
39
39
  _globals['_EMAILBRAND']._serialized_start=197
40
40
  _globals['_EMAILBRAND']._serialized_end=309
41
41
  _globals['_EMAILREQUEST']._serialized_start=312
42
- _globals['_EMAILREQUEST']._serialized_end=731
43
- _globals['_BATCHEMAILREQUEST']._serialized_start=734
44
- _globals['_BATCHEMAILREQUEST']._serialized_end=1158
45
- _globals['_EMAILRESPONSE']._serialized_start=1160
46
- _globals['_EMAILRESPONSE']._serialized_end=1254
42
+ _globals['_EMAILREQUEST']._serialized_end=760
43
+ _globals['_BATCHEMAILREQUEST']._serialized_start=763
44
+ _globals['_BATCHEMAILREQUEST']._serialized_end=1216
45
+ _globals['_EMAILRESPONSE']._serialized_start=1218
46
+ _globals['_EMAILRESPONSE']._serialized_end=1312
47
47
  # @@protoc_insertion_point(module_scope)
@@ -39,7 +39,7 @@ class EmailBrand(_message.Message):
39
39
  def __init__(self, name: _Optional[str] = ..., logo_url: _Optional[str] = ..., url: _Optional[str] = ..., accent_color: _Optional[str] = ...) -> None: ...
40
40
 
41
41
  class EmailRequest(_message.Message):
42
- __slots__ = ("to", "preview", "subject", "paragraphs", "button", "reply_to", "image", "service", "template", "html", "resend_key", "brand")
42
+ __slots__ = ("to", "preview", "subject", "paragraphs", "button", "reply_to", "image", "service", "template", "html", "resend_key", "brand", "body_html")
43
43
  TO_FIELD_NUMBER: _ClassVar[int]
44
44
  PREVIEW_FIELD_NUMBER: _ClassVar[int]
45
45
  SUBJECT_FIELD_NUMBER: _ClassVar[int]
@@ -53,6 +53,7 @@ class EmailRequest(_message.Message):
53
53
  FROM_FIELD_NUMBER: _ClassVar[int]
54
54
  RESEND_KEY_FIELD_NUMBER: _ClassVar[int]
55
55
  BRAND_FIELD_NUMBER: _ClassVar[int]
56
+ BODY_HTML_FIELD_NUMBER: _ClassVar[int]
56
57
  to: str
57
58
  preview: str
58
59
  subject: str
@@ -65,10 +66,11 @@ class EmailRequest(_message.Message):
65
66
  html: str
66
67
  resend_key: str
67
68
  brand: EmailBrand
68
- def __init__(self, to: _Optional[str] = ..., preview: _Optional[str] = ..., subject: _Optional[str] = ..., paragraphs: _Optional[_Iterable[str]] = ..., button: _Optional[_Union[EmailButton, _Mapping]] = ..., reply_to: _Optional[str] = ..., image: _Optional[_Union[EmailImage, _Mapping]] = ..., service: _Optional[str] = ..., template: _Optional[str] = ..., html: _Optional[str] = ..., resend_key: _Optional[str] = ..., brand: _Optional[_Union[EmailBrand, _Mapping]] = ..., **kwargs) -> None: ...
69
+ body_html: str
70
+ def __init__(self, to: _Optional[str] = ..., preview: _Optional[str] = ..., subject: _Optional[str] = ..., paragraphs: _Optional[_Iterable[str]] = ..., button: _Optional[_Union[EmailButton, _Mapping]] = ..., reply_to: _Optional[str] = ..., image: _Optional[_Union[EmailImage, _Mapping]] = ..., service: _Optional[str] = ..., template: _Optional[str] = ..., html: _Optional[str] = ..., resend_key: _Optional[str] = ..., brand: _Optional[_Union[EmailBrand, _Mapping]] = ..., body_html: _Optional[str] = ..., **kwargs) -> None: ...
69
71
 
70
72
  class BatchEmailRequest(_message.Message):
71
- __slots__ = ("to", "preview", "subject", "paragraphs", "button", "reply_to", "image", "service", "template", "html", "resend_key", "brand")
73
+ __slots__ = ("to", "preview", "subject", "paragraphs", "button", "reply_to", "image", "service", "template", "html", "resend_key", "brand", "body_html")
72
74
  TO_FIELD_NUMBER: _ClassVar[int]
73
75
  PREVIEW_FIELD_NUMBER: _ClassVar[int]
74
76
  SUBJECT_FIELD_NUMBER: _ClassVar[int]
@@ -82,6 +84,7 @@ class BatchEmailRequest(_message.Message):
82
84
  FROM_FIELD_NUMBER: _ClassVar[int]
83
85
  RESEND_KEY_FIELD_NUMBER: _ClassVar[int]
84
86
  BRAND_FIELD_NUMBER: _ClassVar[int]
87
+ BODY_HTML_FIELD_NUMBER: _ClassVar[int]
85
88
  to: _containers.RepeatedScalarFieldContainer[str]
86
89
  preview: str
87
90
  subject: str
@@ -94,7 +97,8 @@ class BatchEmailRequest(_message.Message):
94
97
  html: str
95
98
  resend_key: str
96
99
  brand: EmailBrand
97
- def __init__(self, to: _Optional[_Iterable[str]] = ..., preview: _Optional[str] = ..., subject: _Optional[str] = ..., paragraphs: _Optional[_Iterable[str]] = ..., button: _Optional[_Union[EmailButton, _Mapping]] = ..., reply_to: _Optional[str] = ..., image: _Optional[_Union[EmailImage, _Mapping]] = ..., service: _Optional[str] = ..., template: _Optional[str] = ..., html: _Optional[str] = ..., resend_key: _Optional[str] = ..., brand: _Optional[_Union[EmailBrand, _Mapping]] = ..., **kwargs) -> None: ...
100
+ body_html: str
101
+ def __init__(self, to: _Optional[_Iterable[str]] = ..., preview: _Optional[str] = ..., subject: _Optional[str] = ..., paragraphs: _Optional[_Iterable[str]] = ..., button: _Optional[_Union[EmailButton, _Mapping]] = ..., reply_to: _Optional[str] = ..., image: _Optional[_Union[EmailImage, _Mapping]] = ..., service: _Optional[str] = ..., template: _Optional[str] = ..., html: _Optional[str] = ..., resend_key: _Optional[str] = ..., brand: _Optional[_Union[EmailBrand, _Mapping]] = ..., body_html: _Optional[str] = ..., **kwargs) -> None: ...
98
102
 
99
103
  class EmailResponse(_message.Message):
100
104
  __slots__ = ("success", "message_id", "error")
@@ -30,6 +30,10 @@ class DocumentNotesQueue(BaseQueue):
30
30
  callback_url: Optional[str] = None,
31
31
  callback_api_key: Optional[str] = None,
32
32
  folder_id: Optional[str] = None,
33
+ skip_material_creation: Optional[bool] = None,
34
+ existing_material_id: Optional[str] = None,
35
+ target_topic_index: Optional[int] = None,
36
+ append_mode: Optional[bool] = None,
33
37
  ) -> SendResult:
34
38
  """Enqueue a note generation request."""
35
39
  self._validate(uploaded_file_id, study_set_id, user_id, stash_key)
@@ -43,6 +47,10 @@ class DocumentNotesQueue(BaseQueue):
43
47
  callback_url=callback_url,
44
48
  callback_api_key=callback_api_key,
45
49
  folder_id=folder_id,
50
+ skip_material_creation=skip_material_creation,
51
+ existing_material_id=existing_material_id,
52
+ target_topic_index=target_topic_index,
53
+ append_mode=append_mode,
46
54
  )
47
55
  message_id = self._enqueue(payload)
48
56
 
@@ -59,6 +67,10 @@ class DocumentNotesQueue(BaseQueue):
59
67
  callback_url: Optional[str],
60
68
  callback_api_key: Optional[str],
61
69
  folder_id: Optional[str],
70
+ skip_material_creation: Optional[bool],
71
+ existing_material_id: Optional[str],
72
+ target_topic_index: Optional[int],
73
+ append_mode: Optional[bool],
62
74
  ) -> dict[str, Any]:
63
75
  payload: dict[str, Any] = {
64
76
  "uploadedFileId": uploaded_file_id,
@@ -75,6 +87,14 @@ class DocumentNotesQueue(BaseQueue):
75
87
  payload["callbackApiKey"] = callback_api_key
76
88
  if folder_id is not None:
77
89
  payload["folderId"] = folder_id
90
+ if skip_material_creation is not None:
91
+ payload["skipMaterialCreation"] = skip_material_creation
92
+ if existing_material_id is not None:
93
+ payload["existingMaterialId"] = existing_material_id
94
+ if target_topic_index is not None:
95
+ payload["targetTopicIndex"] = target_topic_index
96
+ if append_mode is not None:
97
+ payload["appendMode"] = append_mode
78
98
 
79
99
  return payload
80
100
 
@@ -24,7 +24,7 @@ class EmailQueue(BaseQueue):
24
24
  to: str,
25
25
  preview: str,
26
26
  subject: str,
27
- paragraphs: list[str],
27
+ paragraphs: Optional[list[str]] = None,
28
28
  button: Optional[dict[str, str]] = None,
29
29
  reply_to: Optional[str] = None,
30
30
  image: Optional[dict[str, Any]] = None,
@@ -32,10 +32,22 @@ class EmailQueue(BaseQueue):
32
32
  template: Optional[str] = None,
33
33
  html: Optional[str] = None,
34
34
  brand: Optional[dict[str, str]] = None,
35
+ body_html: Optional[str] = None,
35
36
  ) -> SendResult:
36
37
  """Enqueue an email for processing (fire and forget)."""
37
38
  payload = self._build_payload(
38
- to, preview, subject, paragraphs, button, reply_to, image, service, template, html, brand
39
+ to,
40
+ preview,
41
+ subject,
42
+ paragraphs,
43
+ button,
44
+ reply_to,
45
+ image,
46
+ service,
47
+ template,
48
+ html,
49
+ brand,
50
+ body_html,
39
51
  )
40
52
  message_id = self._enqueue(payload)
41
53
  return SendResult(message_id=message_id)
@@ -45,7 +57,7 @@ class EmailQueue(BaseQueue):
45
57
  to: str,
46
58
  preview: str,
47
59
  subject: str,
48
- paragraphs: list[str],
60
+ paragraphs: Optional[list[str]] = None,
49
61
  button: Optional[dict[str, str]] = None,
50
62
  reply_to: Optional[str] = None,
51
63
  image: Optional[dict[str, Any]] = None,
@@ -54,10 +66,22 @@ class EmailQueue(BaseQueue):
54
66
  template: Optional[str] = None,
55
67
  html: Optional[str] = None,
56
68
  brand: Optional[dict[str, str]] = None,
69
+ body_html: Optional[str] = None,
57
70
  ) -> EmailResponse:
58
71
  """Enqueue an email and wait for processing confirmation."""
59
72
  payload = self._build_payload(
60
- to, preview, subject, paragraphs, button, reply_to, image, service, template, html, brand
73
+ to,
74
+ preview,
75
+ subject,
76
+ paragraphs,
77
+ button,
78
+ reply_to,
79
+ image,
80
+ service,
81
+ template,
82
+ html,
83
+ brand,
84
+ body_html,
61
85
  )
62
86
  return self._enqueue_and_wait(payload, timeout)
63
87
 
@@ -66,7 +90,7 @@ class EmailQueue(BaseQueue):
66
90
  to: list[str],
67
91
  preview: str,
68
92
  subject: str,
69
- paragraphs: list[str],
93
+ paragraphs: Optional[list[str]] = None,
70
94
  button: Optional[dict[str, str]] = None,
71
95
  reply_to: Optional[str] = None,
72
96
  image: Optional[dict[str, Any]] = None,
@@ -74,11 +98,23 @@ class EmailQueue(BaseQueue):
74
98
  template: Optional[str] = None,
75
99
  html: Optional[str] = None,
76
100
  brand: Optional[dict[str, str]] = None,
101
+ body_html: Optional[str] = None,
77
102
  ) -> SendResult:
78
103
  """Enqueue a batch email for processing (fire and forget)."""
79
104
  self._validate_batch(to)
80
105
  payload = self._build_batch_payload(
81
- to, preview, subject, paragraphs, button, reply_to, image, service, template, html, brand
106
+ to,
107
+ preview,
108
+ subject,
109
+ paragraphs,
110
+ button,
111
+ reply_to,
112
+ image,
113
+ service,
114
+ template,
115
+ html,
116
+ brand,
117
+ body_html,
82
118
  )
83
119
  message_id = self._enqueue(payload)
84
120
  return SendResult(message_id=message_id)
@@ -88,7 +124,7 @@ class EmailQueue(BaseQueue):
88
124
  to: list[str],
89
125
  preview: str,
90
126
  subject: str,
91
- paragraphs: list[str],
127
+ paragraphs: Optional[list[str]] = None,
92
128
  button: Optional[dict[str, str]] = None,
93
129
  reply_to: Optional[str] = None,
94
130
  image: Optional[dict[str, Any]] = None,
@@ -97,11 +133,23 @@ class EmailQueue(BaseQueue):
97
133
  template: Optional[str] = None,
98
134
  html: Optional[str] = None,
99
135
  brand: Optional[dict[str, str]] = None,
136
+ body_html: Optional[str] = None,
100
137
  ) -> BatchEmailResponse:
101
138
  """Enqueue a batch email and wait for processing confirmation."""
102
139
  self._validate_batch(to)
103
140
  payload = self._build_batch_payload(
104
- to, preview, subject, paragraphs, button, reply_to, image, service, template, html, brand
141
+ to,
142
+ preview,
143
+ subject,
144
+ paragraphs,
145
+ button,
146
+ reply_to,
147
+ image,
148
+ service,
149
+ template,
150
+ html,
151
+ brand,
152
+ body_html,
105
153
  )
106
154
  response = self._enqueue_and_wait(payload, timeout)
107
155
 
@@ -120,7 +168,7 @@ class EmailQueue(BaseQueue):
120
168
  to: str,
121
169
  preview: str,
122
170
  subject: str,
123
- paragraphs: list[str],
171
+ paragraphs: Optional[list[str]],
124
172
  button: Optional[dict[str, str]],
125
173
  reply_to: Optional[str],
126
174
  image: Optional[dict[str, Any]],
@@ -128,13 +176,14 @@ class EmailQueue(BaseQueue):
128
176
  template: Optional[str] = None,
129
177
  html: Optional[str] = None,
130
178
  brand: Optional[dict[str, str]] = None,
179
+ body_html: Optional[str] = None,
131
180
  ) -> dict[str, Any]:
132
181
  """Build and sanitize the email payload."""
133
182
  payload: dict[str, Any] = {
134
183
  "to": to,
135
184
  "preview": sanitize(preview),
136
185
  "subject": sanitize(subject),
137
- "paragraphs": sanitize_list(paragraphs),
186
+ "paragraphs": sanitize_list(paragraphs or []),
138
187
  }
139
188
 
140
189
  if button:
@@ -163,6 +212,9 @@ class EmailQueue(BaseQueue):
163
212
  if html:
164
213
  payload["html"] = html
165
214
 
215
+ if body_html:
216
+ payload["bodyHtml"] = body_html
217
+
166
218
  if brand:
167
219
  payload["brand"] = self._build_brand(brand)
168
220
 
@@ -170,7 +222,7 @@ class EmailQueue(BaseQueue):
170
222
 
171
223
  @staticmethod
172
224
  def _build_brand(brand: dict[str, str]) -> dict[str, str]:
173
- """Sanitize whitelabel branding. Name is user text; URLs/color pass through."""
225
+ """Sanitize Honen template branding."""
174
226
  result: dict[str, str] = {}
175
227
  if brand.get("name"):
176
228
  result["name"] = sanitize(brand["name"])
@@ -187,7 +239,7 @@ class EmailQueue(BaseQueue):
187
239
  to: list[str],
188
240
  preview: str,
189
241
  subject: str,
190
- paragraphs: list[str],
242
+ paragraphs: Optional[list[str]],
191
243
  button: Optional[dict[str, str]],
192
244
  reply_to: Optional[str],
193
245
  image: Optional[dict[str, Any]],
@@ -195,6 +247,7 @@ class EmailQueue(BaseQueue):
195
247
  template: Optional[str] = None,
196
248
  html: Optional[str] = None,
197
249
  brand: Optional[dict[str, str]] = None,
250
+ body_html: Optional[str] = None,
198
251
  ) -> dict[str, Any]:
199
252
  """Build and sanitize the batch email payload."""
200
253
  payload = self._build_payload(
@@ -209,6 +262,7 @@ class EmailQueue(BaseQueue):
209
262
  template=template,
210
263
  html=html,
211
264
  brand=brand,
265
+ body_html=body_html,
212
266
  )
213
267
  payload["to"] = to
214
268
  payload["batch"] = True
@@ -281,6 +281,9 @@ class DocumentNotesData:
281
281
  callback_url: Optional[str] = None
282
282
  callback_api_key: Optional[str] = None
283
283
  folder_id: Optional[str] = None
284
+ skip_material_creation: Optional[bool] = None
285
+ existing_material_id: Optional[str] = None
286
+ target_topic_index: Optional[int] = None
284
287
 
285
288
 
286
289
  @dataclass